【Shader】屏幕后处理笔记

是什么?

顾名思义就是在得到屏幕渲染图后我们在对这个图做一定的处理,这个处理就差不多我们平时p图的哪些操作,改亮度,对比度,模糊啥的。

怎么做?

这里unity给我们一个接口-OnRenderImage

//src为我们得到屏幕渲染图 dest为我们处理后的一定结果
MonoBehaviour.OnRenderImage (RenderTexture src,RenderTexture dest)

在这个方法中我们一般使用Graphics.Blit来做融合处理。

public static void Blit(Texture source, RenderTexture dest, Material mat, [Internal.DefaultValue("-1")] int pass);
public static void Blit(Texture source, RenderTexture dest, Material mat);
public static void Blit(Texture source, Material mat);

简单的原理就是建一个材质把这个材质的效果叠加到source图上去,所以我们的所写的Shader中必须有一个叫_MainTex的属性。

都说是后处理了,最后渲染得到的图是通过摄像机得到的,所以我们需要把这些处理的脚本放在跟摄像机一个GameObject上。首先我们需要判断当前平台是否支持,然后需要创建一个材质。这里我们有一个基类PostEffectBase。如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class PostEffectBase : MonoBehaviour
{

    // Use this for initialization
    void Start()
    {
        CheckResoures();
    }
    //在最开始调用
    protected void CheckResoures()
    {
        var isSupported = CheckSupport();
        if (!isSupported)
        {
            NoSupport();
        }
    }
    //检查是否支持
    protected bool CheckSupport()
    {
        if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false)
        {
            Debug.LogWarning("此平台不支持图片效果后处理");
            return false;
        }
        return true;
    }

    //取消该脚本效果
    protected void NoSupport()
    {
        enabled = false;
    }

    //检查shader是否存在并创建该shader的材质
    protected Material CheckShaderAndCreateMaterial(Shader shader,Material material){
        if (shader==null)
        {
            return null;
        }
        if (shader.isSupported&&material&&material.shader==shader)
        {
            return material;
        }
        if (!shader.isSupported)
        {
            return null;
        }else
        {
            material=new Material(shader);
            material.hideFlags=HideFlags.DontSave;
            if (material)
            {
                return material;
            }else
            {
                return null;
            }
        }
    }
}

调整屏幕亮度、饱和度、对比度

要做到这个三个效果我们只需要在片元方法中,调节其中颜色值即可。首先获得屏幕:

fixed4 renderTex = tex2D(_MainTex, i.uv); 

首先我们来看如何调整亮度:

fixed3 finalCol = renderTex.rgb * _Brightness; 

在调整饱和度:

//这个应该是一个经验公式
fixed luminance = 0.2125 * renderTex.r + 0.7254 * renderTex.g + 0.0721 * renderTex.b; 
fixed3 luminaceColor = fixed3(luminance, luminance, luminance); 
finalCol == lerp(luminaceColor, finalCol, _Saturation); 

最后设置对比度即可:

fixed3 avgColor = fixed3(0.5, 0.5, 0.5); 
finalCol = lerp(avgColor,finalCol,_Contrast); 
return fixed4(finalCol,renderTex.a); 

我们在OnRenderImage中设置这三个参数即可:

void OnRenderImage(RenderTexture src, RenderTexture dest)
{
    if (material!=null)
    {
        material.SetFloat("_Brightness",brightness);
        material.SetFloat("_Saturation",saturation);
        material.SetFloat("_Contrast",contrast);

        Graphics.Blit(src,dest,material);
    }else
    {
        Graphics.Blit(src,dest);
    }
}

亮度的效果还是可以用来做开场动画和结束动画,挺好看的。

image

边缘检测

卷积: 是指使用卷积核对一张图像中的每个像素进行一系列操作。卷积核一般是一个正方形网格结构(3x3或者是2x2),每正方形网格中的格子都会有一个数值,称为权重值。既然是积就需要相乘,比如我们需要计算某一个像素的卷积,就把一个卷核的中心格子放在哪个像素上,然后依次计算核中每个元素和其覆盖的图像像素值得乘积并求和,得到该像素的卷积。

卷积示意图

这个我看了几次,才看明白哦。这个就很像乘以矩阵啥的,我们可以把卷积核理解成一个常量。对,就是相当于扫雷那种感觉我们在排一个点是否是雷就看周围的数字嘛,这个周围的就可以理解为卷积核。我们这个时候已知周围的数字,然后把周围的字与那个格子的颜色相乘然后,最后把所有相乘的值相加就得到了卷积。

那我们现在获得了卷积值,卷积值越大就越可能是边缘点。我们知道边缘为什么是边缘?就是因为他与其他地方的颜色差别很。当然这个卷积核肯定是特定的,这样就可以检测出来颜色大小。

3种常见的边缘检测算子

边缘检测cs代码

边缘检测shader代码

我这里没有实现这个功能,说的是我的shader不支持平台。。。。

高斯模糊

使用的原理与求边缘的原理差不多,也是使用一个卷积核,但是这里叫做高斯核,这里有一个求高斯核的权重的公式,


公式

然后我们可以跟求边缘一样求得卷积。这里我们做了一个优化,把二维的正方形高斯核转化成两个一维的高斯核,就是转化中间的横竖那一列和行。

把一个二维5x5的高斯核转化为两个一维高斯核
效果

高斯模糊cs代码
高斯模糊shader代码


Bloom效果

这个效果是做到一种高光部分过爆的效果,带有朦胧效果。
原理就是先生成高亮部分的图,然后对这个图做高斯模糊最终获得一张图(模拟光线图),然后把模糊图与原图做一个混合即可。
所以这用了4个pass块,几次来处理。

效果图

Bloom效果cs代码
Bloom效果shader代码


运动模糊

做运动模糊一般有很多种方式:
1.累计缓存:把每一帧的图像记录下来,然后把记录下来的图片连续混合起来显示,这样就可以做出动态模糊同时也可以做出虚影的效果。这个消耗很大。
2.速度缓存:缓存中存储了各个像素当前的运动速度,然后利用该值来决定模糊的方向大小。

这里作者使用得的第一个方式的优化来实现,在得到之前渲染结果,不断把当前的渲染图像叠加到之前的渲染图像中,从而产生一种运动轨迹的视觉效果。

在OnRenderImage中,我们就把当前渲染得到得的图不断存入acumulationTexture中。

void OnRenderImage(RenderTexture src, RenderTexture dest)
{
    if (material != null)
    {
        if (accumulationTexture==null||accumulationTexture.width!=src.width||
        accumulationTexture.height!=src.height)
        {
            DestroyImmediate(accumulationTexture);
            accumulationTexture=new RenderTexture(src.width,src.height,0);
            accumulationTexture.hideFlags=HideFlags.HideAndDontSave;
            Graphics.Blit(src,accumulationTexture);
        }
        //恢复操作 发生在渲染到纹理而该纹理又没有被提前清空或销毁的情况
        accumulationTexture.MarkRestoreExpected();

        material.SetFloat("_BlurAmount",1.0f-blurAmount);

        Graphics.Blit(src,accumulationTexture,material);
        Graphics.Blit(accumulationTexture,dest);
    }
    else
    {
        Graphics.Blit(src, dest);
    }
}

我们在shader中需要分别处理透明通道和颜色通道在两个pass块里,因为我们在获得模糊虚影时,需要设置虚影的长度,这个长度就是我们的设置的模糊参数。

实现效果

看起来还是很酷炫得的。

Bloom效果cs代码
Bloom效果shader代码

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容