Unity Shader入门精要学习笔记 - 第12章 屏幕后处理效果

本文是Unity Shader入门精要学习笔记的第12章,详细介绍了屏幕后处理效果,包括建立屏幕后处理脚本系统,调整亮度、饱和度和对比度,边缘检测,高斯模糊,Bloom效果以及运动模糊的实现方法。通过这些技术,可以为游戏增加艺术效果,如景深和运动模糊。文章提供了一系列脚本和Shader的实现细节。
摘要由CSDN通过智能技术生成

Unity Shader入门精要学习笔记 - 第12章 屏幕后处理效果

本系列为UnityShader入门精要读书笔记总结,
原作者博客链接:http://blog.csdn.net/candycat1992/article/
书籍链接:http://product.dangdang.com/23972910.html

第12章 屏幕后处理效果

屏幕后处理效果(screen post-processing effects ) 是游戏中实现屏幕特效的常见方法。 在本
章中, 我们将学习如何在 Unity 中利用渲染纹理来实现各种常见的屏幕后处理效果。我们首先会解释在 Unity 中实现屏幕后处理效果的原理, 并建立一个基本的屏幕后处理脚本系统。随后 我们会使用这个系统实现一个简单的调整画面亮度、 饱和度和对比度的屏幕特效。 接下来, 我们会接触到图像滤波的概念, 并利用 Sobel 算子在屏幕空间中对图像进行边缘检测, 实现描边效果。 在此基础上,将会介绍如何实现一个高斯模糊的屏幕特效。后期我们会分别介绍如何实现 Bloom 和运动模糊效果。

12.1 建立一个基本的屏幕后处理脚本系统

屏幕后处理, 顾名思义, 通常指的是在渲染完整个场景得到屏幕图像后, 再对这个图像进行一系列操作, 实现各种屏幕特效。 使用这种技术, 可以为游戏画面添加更多的艺术效果, 例如景深( Depth of Field)、 运动模糊( Motion Blur) 等。
因此,想要实现屏幕后处理的基础在于得到渲染后的屏幕图像,即抓取屏幕,而Unity为我们提供了这样一个方便的接口OnRenderImage函数。它的函数声明如下:

MonoBehaviour.OnRenderImage(RenderTexture src,RenderTexture dest)  

当我们再脚本中声明此函数后,Unity会把当前渲染得到的图像存储在第一个参数对应的源渲染纹理中,通过函数中的一系列操作后,再把目标渲染纹理,即第二个参数对应的渲染纹理显示到屏幕上。

在默认的情况下,OnRenderImage 函数会在所有的不透明和透明的Pass执行完毕后被调用,以便对场景中所有的游戏对象都产生影响。但有时,我们希望在不透明的Pass(即渲染队列小于等于2500 的Pass,内置的Background、Geometry 和 AlphaTest渲染队列均在此范围内)执行完毕后立即调用OnRenderImage 函数,从而不对透明物体产生任何影响。此时,我们可以在OnRenderImage 函数前添加ImageEffectOpaque 属性来实现这样的目的。

因此,要在Unity 中实现屏幕后处理效果,过程通常如下:
我们首先需要再摄像机中添加一个用于屏幕后处理的脚本。在这个脚本中,我们会实现OnRenderImage函数来获取当前屏幕的渲染纹理。
然后,再调用Graphics.Blit 函数使用特定的Unity Shader 来对当前图像进行处理,再把返回的渲染纹理显示到屏幕上。对于一些复杂的屏幕特效,我们可能需要多次调用Graphics.Blit 函数来对上一步的输出结果进行下一步处理。

但是,在进行屏幕后处理之前,我们需要检查一系列条件是否满足,例如当前平台是否支持渲染纹理和屏幕特效,是否支持当前使用的Unity Shader等,为此,我们创建了一个用于屏幕后处理效果的基类,在实现各种屏幕特效时,我们只需要继承自该基类,再实现派生类中不同的操作即可。
基类PostEffectBase.cs 的代码如下

//希望在编辑器状态下也可以执行该脚本来查看效果  
[ExecuteInEditMode]  
//所有的屏幕后处理效果都需要绑定在某个摄像机上  
[RequireComponent(typeof(Camera))]  
public class PostEffectsBase : MonoBehaviour {  

    protected void CheckResource(){  
        bool isSupported = CheckSupport();  
        if(isSupported == false){  
            NotSupported();  
        }  
    }  

    protected bool CheckSupport(){  
        if(SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false){  
            Debug.LogWarning("This platform does not support image effects or render textures.");  
            return false;  
        }  
        return true;  
    }  

    protected void NotSupported(){  
        enabled = false;  
    }  

    protected void Start(){  
        CheckResource();  
    }  

    //第一个参数指定了该特效需要使用的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;  
        }  
        material = new Material(shader);  
        material.hideFlags = HideFlags.DontSave;  
        return material;  
    }  
}  

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

在上面,我们了解了实现屏幕后处理特效的技术原理。我们现在先来实现一个非常简单的屏幕特效——调整屏幕的亮度、饱和度和对比度。我们的效果图如下所示
这里写图片描述
新建一个脚本,名为BrightnessSaturationAndContrast.cs。添加到摄像机上。

public class BrightnessSaturationAndContrast : PostEffectsBase {  
    public Shader briSatConShader;  
    private Material briSatConMaterial;  
    public Material material {  
        get{  
            briSatConMaterial = CheckShaderAndCreateMaterial(briSatConShader,briSatConMaterial);  
            return briSatConMaterial;  
        }  
    }  

    [Range(0.0f,3.0f)]  
    public float brightness = 1.0f;  

    [Range(0.0f,3.0f)]  
    public float saturation = 1.0f;  

    [Range(0.0f,3.0f)]  
    public float contrast = 1.0f;  

    //真正的处理特效  
    //图像特效可以在不透明渲染通道或透明与不透明渲染通道(默认情况)后直接执行。
    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);  
        }  
    }  
}  

然后我们再修改Shader 的代码。

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unlit/Chapter12-MyBrightnessSaturationAndContrast"
{
    Properties {  
        _MainTex ("Base (RGB)", 2D) = "white" {}  
        _Brightness ("Brightness", Float) = 1  
        _Saturation("Saturation", Float) = 1  
        _Contrast("Contrast", Float) = 1  
    }  
    SubShader {  
        Pass {    
            //屏幕后处理实际上是在场景中绘制了一个与屏幕同宽同高的四边形面片  
            //为了防止它对其他物体产生影响,我们需要设置相关的渲染状态。  
            //关闭深度写入,是为了防止它“挡住”在其后面被渲染的物体  
            ZTest Always Cull Off 
            ZWrite Off  

            CGPROGRAM    
            #pragma vertex vert    
            #pragma fragment frag    

            #include "UnityCG.cginc"    

            sampler2D _MainTex;    
            half _Brightness;  
            half _Saturation;  
            half _Contrast;  

            struct v2f {  
                float4 pos : SV_POSITION;  
                half2 uv: TEXCOORD0;  
            };  

            //屏幕特效使用的顶点着色器代码通常比较简单,我们只需要进行必须的顶点变换  
            //更重要的是,我们需要把正确的纹理坐标传递给片元着色器,以便对屏幕图像进行正确的采样  
            //使用了内置appdata_img 结构体作为顶点着色器的输入  
            //可以在 UnityCGxginc 中找到该结构体的声明, 它只包含了图像处理时必需的顶点坐标和纹理坐标等变量
            v2f vert(appdata_img v) {  
                v2f o;  
                o.pos = UnityObjectToClipPos(v.vertex);  
                o.uv = v.texcoord;  
                return o;  
            }  

            fixed4 frag(v2f i) : SV_Target {  
                fixed4 renderTex = tex2D(_MainTex, i.uv);    

                //调整亮度  
                fixed3 finalColor = renderTex.rgb * _Brightness;  

                //调整饱和度  
     
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值