【《Unity着色器和屏幕特效开发秘笈》】学习整理:关于屏幕特效【2】

一个简单的屏幕效果

屏幕特效通用脚本的基础上,制作一个简单的灰度效果。
首先是使用的挂在摄像机上面的脚本:

[ExecuteInEditMode]
public class MyTestRenderImage : MonoBehaviour
{
    public Shader curShader = null;

    [Range(0f, 1f)]//添加此特性后可在Inspector面板中使用滑动条控制grayScaleAmount
    //控制灰度值
    public float grayScaleAmount = 1.0f;

    private Material curMaterial = null;
    private Material CurMaterial
    {
        get
        {
            if(curMaterial == null)
            {
                curMaterial = new Material(curShader);
                curMaterial.hideFlags = HideFlags.HideAndDontSave;
            }
            return curMaterial;
        }
    }

    private void Start()
    {
        if(!SystemInfo.supportsImageEffects)
        {
            enabled = false;
            return;
        }
        if(!curShader && !curShader.isSupported)
        {
            enabled = false;
        }
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if(curShader)
        {
            //使用grayScaleAmount控制着色器中对应的属性数值
            CurMaterial.SetFloat("_LuminosityAmount", grayScaleAmount);
            Graphics.Blit(source, destination, CurMaterial);
        }
        else
        {
            Graphics.Blit(source, destination);
        }
    }

    private void Update()
    {        
        //检查并将grayScaleAmount的值限定在0到1之间
        grayScaleAmount = Mathf.Clamp01(grayScaleAmount);
    }

    private void OnDisable()
    {
        if(curMaterial)
        {
            DestroyImmediate(curMaterial);
        }
    }
}

通过和之前基本结构的对比,可以看出此脚本只是多了对于grayScaleAmount这一灰度数值相关的代码。
通过CurMaterial.SetFloat("_LuminosityAmount", grayScaleAmount);可以实现外部对于着色器的控制,从而直接看到屏幕效果的变化。
当然如果为了简便也可以通过继承的方式,将基本结构作为父类,改写其中相应的函数来简化代码,此处为了进行对比,所以还是直接将其直接写了出来。

下面是用到的shader代码:

Shader "RenderImage/MyTestRenderImage" 
{
    Properties 
    {
        _MainTex("Base Tex", 2D) = "white"{}
        _LuminosityAmount("GrayScale Amount", Range(0.0, 1)) = 1.0
    }

    SubShader 
    {
        Pass
        {
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            sampler2D _MainTex;
            fixed _LuminosityAmount;

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

            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);

                float luminosity = 0.299 * renderTex.r + 0.587 * renderTex.g + 0.114 * renderTex.b;
                fixed4 finalColor = lerp(renderTex, luminosity, _LuminosityAmount);

                return finalColor;
            }

            ENDCG
        }
    }

}   //end shader

在此Shader中,使用了Unity内置的appdata_img结构体作为顶点着色器的输入。
此结构体位于UnityCG.cginc中,只包含图像处理时必须的定点坐标和纹理坐标等变量。

struct appdata_img
{
    float4 vertex : POSITION;
    half2 texcoord : TEXCOORD0;
    UNITY_VERTEX_INPUT_INSTANCE_ID
};

vert顶点着色器中,仅进行了必要的顶点变换和uv坐标的赋值。
frag片元着色器中,通过lerp差值返回最终的屏幕颜色。
其中需要注意的是以下代码:

float luminosity = 0.299 * renderTex.r + 0.587 * renderTex.g + 0.114 * renderTex.b;

此代码中的(0.299, 0.587, 0.114)并不是随意写上去的,经过查询资料显示,这个是一个经典的色彩心理学公式。

Gray = R*0.299 + G*0.587 + B*0.114

目的是将彩色转化为灰度,是国际公认的一个公式。其中Gray是最终的灰度值,R代表红色通道、G代表绿色通道、B代表蓝色通道。
知道了这个公式,片元着色器中的代码意义也就可以很轻松的理解了。

//根据uv获取相应像素的颜色值
fixed4 renderTex = tex2D(_MainTex, i.uv);
//按照公式,将颜色值转化为对应的灰度值    
float luminosity = 0.299 * renderTex.r + 0.587 * renderTex.g + 0.114 * renderTex.b;
//通过差值,返回经过_LuminosityAmount控制的最终颜色
fixed4 finalColor = lerp(renderTex, luminosity, _LuminosityAmount);

以下是此Shader的效果

grayScaleAmount = 0

Write picture description here

grayScaleAmount = 0.25

Write picture description here

grayScaleAmount = 0.5

Write picture description here

grayScaleAmount = 0.75

Write picture description here

grayScaleAmount = 1.0

Write picture description here

At that timegrayScaleAmount = 0 , the final display was a normal color effect image.
At that timegrayScaleAmount = 1 , the final display was a completely grayscale effect image.

Since this Shader is learned and used in the book, it only contains the necessary code to achieve the effect. However, in the actual project process, the Shader needs to be optimized before it can be used.

おすすめ

転載: blog.csdn.net/EverNess010/article/details/79834555