Shaderlab Notizen 10 屏幕像素化特效

一、Shader实现部分

Shader "Shader/PixelEffect"  
{  
    //------------------------------------【属性值】------------------------------------  
    Properties  
    {  
    //主纹理  
    _MainTex("Texture", 2D) = "white" {}  
    //封装的变量值  
    _Params("PixelNumPerRow (X) Ratio (Y)", Vector) = (80, 1, 1, 1.5)  
}  

    //------------------------------------【唯一的子着色器】------------------------------------  
    SubShader  
    {  
        //关闭剔除操作  
        Cull Off  
        //关闭深度写入模式  
        ZWrite Off  
        //设置深度测试模式:渲染所有像素.等同于关闭透明度测试(AlphaTest Off)  
        ZTest Always  

        //--------------------------------唯一的通道-------------------------------  
        Pass  
        {  
            //===========开启CG着色器语言编写模块===========  
            CGPROGRAM  

            //编译指令:告知编译器顶点和片段着色函数的名称  
            #pragma vertex vert  
            #pragma fragment frag  

            //包含头文件  
            #include "UnityCG.cginc"  

            //顶点着色器输入结构  
            struct vertexInput  
            {  
                float4 vertex : POSITION;//顶点位置  
                float2 uv : TEXCOORD0;//一级纹理坐标  
            };  

            //顶点着色器输出结构  
            struct vertexOutput  
            {  
                float4 vertex : SV_POSITION;//像素位置  
                float2 uv : TEXCOORD0;//一级纹理坐标  
            };  

            //--------------------------------【顶点着色函数】-----------------------------  
            // 输入:顶点输入结构体  
            // 输出:顶点输出结构体  
            //---------------------------------------------------------------------------------  
            //顶点着色函数  
            vertexOutput vert(vertexInput   v)  
            {  
                //【1】实例化一个输入结构体  
                vertexOutput o;  
                //【2】填充此输出结构  
                //输出的顶点位置(像素位置)为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口  
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);  
                //输入的UV纹理坐标为顶点输出的坐标  
                o.uv = v.uv;  

                //【3】返回此输出结构对象  
                return o;  
            }  

            //变量的声明  
            sampler2D _MainTex;  
            half4 _Params;  

            //进行像素化操作的自定义函数PixelateOperation  
            half4 PixelateOperation(sampler2D tex, half2 uv, half scale, half ratio)  
            {  
                //【1】计算每个像素块的尺寸  
                half PixelSize = 1.0 / scale;  
                //【2】取整计算每个像素块的坐标值,ceil函数,对输入参数向上取整  
                half coordX=PixelSize * ceil(uv.x / PixelSize);  
                half coordY = (ratio * PixelSize)* ceil(uv.y / PixelSize / ratio);  
                //【3】组合坐标值  
                half2 coord = half2(coordX,coordY);  
                //【4】返回坐标值  
                return half4(tex2D(tex, coord).xyzw);  
            }  

            //--------------------------------【片段着色函数】-----------------------------  
            // 输入:顶点输出结构体  
            // 输出:float4型的像素颜色值  
            //---------------------------------------------------------------------------------  
            fixed4 frag(vertexOutput  Input) : COLOR  
            {  
                //使用自定义的PixelateOperation函数,计算每个像素经过取整后的颜色值  
                return PixelateOperation(_MainTex, Input.uv, _Params.x, _Params.y);  
            }  

            //===========结束CG着色器语言编写模块===========  
            ENDCG  
        }  
    }  
}

屏幕像素化特效主要用一个自定义函数来实现,实现代码如下:

 //进行像素化操作的自定义函数PixelateOperation  
            half4 PixelateOperation(sampler2D tex, half2 uv, half scale, half ratio)  
            {  
                //【1】计算每个像素块的尺寸  
                half PixelSize = 1.0 / scale;  
                //【2】取整计算每个像素块的坐标值,ceil函数,对输入参数向上取整  
                half coordX=PixelSize * ceil(uv.x / PixelSize);  
                half coordY=( ratio * PixelSize ) * ceil(uv.y / PixelSize / ratio);  
                //【3】组合坐标值  
                half2 coord = half2(coordX,coordY);  
                //【4】返回坐标值  
                return half4(tex2D(tex, coord).xyzw);  
            }

ps:此自定义函数中用到了CG标准函数库中的一个库函数——ceil。ceil(x)的作用是对输入参数向上取整。例如:ceil(float(1.3)) ,返回值就为2.0。
PixelateOperation函数的首先先计算出每个像素块的尺寸,然后根据这里的向上取整函数ceil,分别表示出像素块的坐标值。X坐标值为PixelSize * ceil(uv.x / PixelSize)。而Y轴这边还引入了一个系数ratio,先在式子一开头乘以此系数,然后在ceil函数之中的分母部分除以一个ratio,以达到用此参数实现自定义像素长宽比的调整操作。
然后在片段着色器中调用此自定义的PixelateOperation函数,其返回值就作为片段函数frag的返回值即可:

fixed4 frag(vertexOutput  Input) : COLOR  
    {  
        //使用自定义的PixelateOperation函数,计算每个像素经过取整后的颜色值  
        return PixelateOperation(_MainTex, Input.uv, _Params.x, _Params.y);  
    }

二、脚本实现部分

using UnityEngine;  
using System.Collections;  

//设置在编辑模式下也执行该脚本  
[ExecuteInEditMode]  
//添加选项到菜单中  
[AddComponentMenu("Shader/PixelEffect")]  
public class PixelEffect : MonoBehaviour  
{  
    //-----------------------------变量声明部分---------------------------  
    #region Variables  

    //着色器和材质实例  
    public Shader CurShader;  
    private Material CurMaterial;  

    //三个可调节的自定义参数  
    [Range(1f, 1024f), Tooltip("屏幕每行将被均分为多少个像素块")]  
    public float PixelNumPerRow = 580.0f;  

    [Tooltip("自动计算平方像素所需的长宽比与否")]  
    public bool AutoCalulateRatio = true;  

    [Range(0f, 24f), Tooltip("此参数用于自定义长宽比")]  
    public float Ratio = 1.0f;  

    #endregion  

    //-------------------------材质的get&set----------------------------  
    #region MaterialGetAndSet  
    Material material  
    {  
        get  
        {  
            if(CurMaterial == null)  
            {  
                CurMaterial = new Material(CurShader);  
                CurMaterial.hideFlags = HideFlags.HideAndDontSave;    
            }  
            return CurMaterial;  
        }  
    }  
    #endregion  

    //-----------------------------------------【Start()函数】---------------------------------------------    
    // 说明:此函数仅在Update函数第一次被调用前被调用  
    //--------------------------------------------------------------------------------------------------------  
    void Start ()  
    {  
        //找到当前的Shader文件  
        CurShader = Shader.Find("浅墨Shader编程/Volume11/PixelEffect");  

        //判断当前设备是否支持屏幕特效  
        if(!SystemInfo.supportsImageEffects)  
        {  
            enabled = false;  
            return;  
        }  
    }  

    //-------------------------------------【OnRenderImage()函数】------------------------------------    
    // 说明:此函数在当完成所有渲染图片后被调用,用来渲染图片后期效果  
    //--------------------------------------------------------------------------------------------------------  
    void OnRenderImage (RenderTexture sourceTexture, RenderTexture destTexture)  
    {  
        //着色器实例不为空,就进行参数设置  
        if(CurShader != null)  
        {  
            float pixelNumPerRow = PixelNumPerRow;  
            //给Shader中的外部变量赋值  
            material.SetVector("_Params", new Vector2(pixelNumPerRow,  
                AutoCalulateRatio ? ((float)sourceTexture.width / (float)sourceTexture.height) : Ratio ));  

            Graphics.Blit(sourceTexture, destTexture, material);  
        }  

        //着色器实例为空,直接拷贝屏幕上的效果。此情况下是没有实现屏幕特效的  
        else  
        {  
            //直接拷贝源纹理到目标渲染纹理  
            Graphics.Blit(sourceTexture, destTexture);  
        }  
    }  

    //-----------------------------------------【Update()函数】----------------------------------------  
    // 说明:此函数在每一帧中都会被调用    
    //------------------------------------------------------------------------------------------------------  
    void Update()  
    {  
        //若程序在运行,进行赋值  
        if (Application.isPlaying)  
        {  
         #if UNITY_EDITOR  
            if (Application.isPlaying != true)  
            {  
                CurShader = Shader.Find("浅墨Shader编程/Volume11/PixelEffect");  
            }  
        #endif  
        }  
    }  
    //-----------------------------------------【OnDisable()函数】---------------------------------------    
    // 说明:当对象变为不可用或非激活状态时此函数便被调用    
    //--------------------------------------------------------------------------------------------------------  
    void OnDisable ()  
    {  
        if(CurMaterial)  
        {  
            //立即销毁材质实例  
            DestroyImmediate(CurMaterial);    
        }        
    }  
}  

根据C#脚本中参数的设定,可以有每行每列的像素个数PixelNumPerRow参数、是否自动计算正方形像素所需的长宽比与否AutoCalulateRatio参数、自定义长宽比的Ratio参数可以调节。
ps:若AutoCalulateRatio参数被勾选,Shader将自动计算正方形像素所需的长宽比,这样第三个参数Ratio也就失效了。反正,若AutoCalulateRatio参数没有被勾选,就可以用Ratio参数自己定制像素的长宽比。

你可能感兴趣的:(Shaderlab Notizen 10 屏幕像素化特效)