Unity shader之水屏幕

发表于2018-04-26
评论0 1.2k浏览
前面和大家介绍过shader屏幕油画特效的实现,本篇文章也有利用其中的原理,下面就来看看使用shader实现水屏幕特效的方法吧。

首先创建一个顶点片段shader,将内容修改如下:
Shader "Custom/waterCamera" {  
    Properties {  
        //主纹理  
        _MainTex("Base (RGB)",2D)="white"{}  
        //屏幕水滴的素材图  
        _ScreenWaterDropTex("Base (RGB)",2D)="white"{}  
        //当前时间  
        _CurTime("Time",Range(0.0,1.0))=1.0  
        //X坐标上的水滴尺寸  
        _SizeX("SizeX",Range(0.0,1.0))=1.0  
        //Y坐标上的水滴尺寸  
        _SizeY("SizeY",Range(0.0,1.0))=1.0  
        //水滴的流动速度  
        _DropSpeed("Speed",Range(0.0,10.0))=1.0  
        //溶解度  
        _Distortion("_Distortion",Range(0.0,1.0))=0.87  
    }  
    SubShader {  
        Pass  
        {  
            //设置深度测试模式:渲染所有像素.等同于关闭透明度测试(AlphaTest Off)  
            ZTest Always  
            //===========开启CG着色器语言编写模块===========  
            CGPROGRAM  
            //编译指令:告知编译器顶点和片段着色函数的名称  
            #pragma vertex vert  
            #pragma fragment frag  
            //添加选项到编译的OpenGL 片段程序。  
            #pragma fragmentoption ARB_precision_hint_fastest  
            //编译指令: 指定着色器编译目标为Shader Model 3.0  
            #pragma target 3.0  
            //包含辅助CG头文件  
            #include "UnityCG.cginc"  
            //外部变量的声明  
            uniform sampler2D _MainTex;  
            uniform sampler2D _ScreenWaterDropTex;  
            uniform float _CurTime;  
            uniform float _DropSpeed;  
            uniform float _SizeX;  
            uniform float _SizeY;  
            uniform float _Distortion;  
            uniform float2 _MainTex_TexelSize;  
            //顶点输入结构  
            struct vertexInput  
            {  
                float4 vertex : POSITION;//顶点位置  
                float4 color : COLOR;//颜色值  
                float2 texcoord : TEXCOORD0;//一级纹理坐标  
            };  
            //顶点输出结构  
            struct vertexOutput  
            {  
                half2 texcoord:TEXCOORD0; //一级纹理坐标  
                float4 vertex : SV_POSITION; //像素位置  
                fixed4 color:COLOR;  //颜色值  
            };  
            //--------------------------------【顶点着色函数】-----------------------------  
            // 输入:顶点输入结构体  
            // 输出:顶点输出结构体  
            //---------------------------------------------------------------------------------  
            vertexOutput vert(vertexInput Input)  
            {  
                //【1】声明一个输出结构对象  
                vertexOutput Output;  
                //【2】填充此输出结构  
                //输出的顶点位置为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口  
                Output.vertex=mul(UNITY_MATRIX_MVP,Input.vertex);  
                //输出的纹理坐标也就是输入的纹理坐标  
                Output.texcoord=Input.texcoord;  
                //输出的颜色值也就是输入的颜色值  
                Output.color=Input.color;  
                //【3】返回此输出结构对象  
                return Output;  
            }  
            //--------------------------------【片段着色函数】-----------------------------  
            // 输入:顶点输出结构体  
            // 输出:float4型的颜色值  
            //---------------------------------------------------------------------------------  
            fixed4 frag(vertexOutput Input):COLOR  
            {  
                //【1】获取顶点的坐标值  
                float2 uv = Input.texcoord.xy;  
                //【2】解决平台差异的问题。校正方向,若和规定方向相反,则将速度反向并加1  
                #if UNITY_UV_STARTS_AT_TOP  
                if (_MainTex_TexelSize.y < 0)  
                    _DropSpeed = 1 - _DropSpeed;  
                #endif  
                //【3】设置三层水流效果,按照一定的规律在水滴纹理上分别进行取样  
                float3 rainTex1 = tex2D(_ScreenWaterDropTex,float2(uv.x * 1.15 * _SizeX,(uv.y*_SizeY * 1.1)+_CurTime*_DropSpeed*0.15)).rgb/_Distortion;  
                float3 rainTex2 = tex2D(_ScreenWaterDropTex,float2(uv.x*1.25*_SizeX-0.1,(uv.y*_SizeY*1.2)+_CurTime*_DropSpeed*0.2)).rgb/_Distortion;  
                float3 rainTex3=tex2D(_ScreenWaterDropTex,float2(uv.x*0.9*_SizeX,(uv.y*_SizeY*1.25)+_CurTime*_DropSpeed*0.032)).rgb/_Distortion;  
                //【4】整合三层水流效果的颜色信息,存于finalRainTex中  
                float2 finalRainTex=uv.xy-(rainTex1.xy-rainTex2.xy-rainTex3.xy)/3;  
                //【5】按照finalRainTex的坐标信息,在主纹理上进行采样  
                float3 finalColor = tex2D(_MainTex, float2(finalRainTex.x, finalRainTex.y)).rgb;  
                //【6】返回加上alpha分量的最终颜色值  
                return fixed4(finalColor, 1.0);  
            }  
            //===========结束CG着色器语言编写模块===========  
            ENDCG  
        }  
    }  
    FallBack "Diffuse"  
}  

接着创建一个脚本挂在摄像机上,如下:
using UnityEngine;  
using System.Collections;  
[ExecuteInEditMode]  
public class WaterScreen : MonoBehaviour {  
    //-------------------变量声明部分-------------------  
    #region Variables  
    //着色器和材质实例  
    public Shader CurShader; // 着色器实例  
    private Material CurMaterial;  //当前的材质  
    //时间变量和素材图的定义  
    private float TimeX = 1.0f;    //时间变量  
    private Texture2D ScreenWaterDropTex;   //屏幕水滴的素材图  
    //可以再编辑器中调整的参数值  
    [Range(5, 64), Tooltip("溶解度")]  
    public float Distortion = 8.0f;  
    [Range(0, 7), Tooltip("水滴在X坐标上的尺寸")]  
    public float SizeX = 1f;  
    [Range(0, 7), Tooltip("水滴在Y坐标上的尺寸")]  
    public float SizeY = 0.5f;  
    [Range(0, 10), Tooltip("水滴的流动速度")]  
    public float DropSpeed = 3.6f;  
    //用于参数调节的中间变量  
    public static float ChangeDistortion;  
    public static float ChangeSizeX;  
    public static float ChangeSizeY;  
    public static float ChangeDropSpeed;  
    #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()  
    {  
        //依次赋值  
        ChangeDistortion = Distortion;  
        ChangeSizeX = SizeX;  
        ChangeSizeY = SizeY;  
        ChangeDropSpeed = DropSpeed;  
        //载入素材图  
        ScreenWaterDropTex = Resources.Load("ScreenWaterDrop") as Texture2D;  
        //找到当前的Shader文件  
        CurShader = Shader.Find("Custom/waterCamera");  
        //判断是否支持屏幕特效  
        if (!SystemInfo.supportsImageEffects)  
        {  
            enabled = false;  
            return;  
        }  
    }  
    //-------------------------------------【OnRenderImage()函数】------------------------------------    
    // 说明:此函数在当完成所有渲染图片后被调用,用来渲染图片后期效果  
    //--------------------------------------------------------------------------------------------------------  
    void OnRenderImage(RenderTexture sourceTexture, RenderTexture destTexture)  
    {  
        //着色器实例不为空,就进行参数设置  
        if (CurShader != null)  
        {  
            //时间的变化  
            TimeX += Time.deltaTime;  
            //时间大于100,便置0,保证可以循环  
            if (TimeX > 100) TimeX = 0;  
            //设置Shader中其他的外部变量  
            material.SetFloat("_CurTime", TimeX);  
            material.SetFloat("_Distortion", Distortion);  
            material.SetFloat("_SizeX", SizeX);  
            material.SetFloat("_SizeY", SizeY);  
            material.SetFloat("_DropSpeed", DropSpeed);  
            material.SetTexture("_ScreenWaterDropTex", ScreenWaterDropTex);  
            //拷贝源纹理到目标渲染纹理,加上我们的材质效果  
            Graphics.Blit(sourceTexture, destTexture, material);  
        }  
        //着色器实例为空,直接拷贝屏幕上的效果。此情况下是没有实现屏幕特效的  
        else  
        {  
            //直接拷贝源纹理到目标渲染纹理  
            Graphics.Blit(sourceTexture, destTexture);  
        }  
    }  
    //-----------------------------------------【OnValidate()函数】--------------------------------------    
    // 说明:此函数在编辑器中该脚本的某个值发生了改变后被调用  
    //--------------------------------------------------------------------------------------------------------  
    void OnValidate()  
    {  
        ChangeDistortion = Distortion;  
        ChangeSizeX = SizeX;  
        ChangeSizeY = SizeY;  
        ChangeDropSpeed = DropSpeed;  
    }  
    //-----------------------------------------【Update()函数】------------------------------------------    
    // 说明:此函数在每一帧中都会被调用  
    //--------------------------------------------------------------------------------------------------------   
    void Update()  
    {  
        //若程序在运行,进行赋值  
        if (Application.isPlaying)  
        {  
            //赋值  
            Distortion = ChangeDistortion;  
            SizeX = ChangeSizeX;  
            SizeY = ChangeSizeY;  
            DropSpeed = ChangeDropSpeed;  
        }  
        //找到对应的Shader文件,和纹理素材  
#if UNITY_EDITOR  
        if (Application.isPlaying != true)  
        {  
            CurShader = Shader.Find("Custom/waterCamera");  
            ScreenWaterDropTex = Resources.Load("ScreenWaterDrop") as Texture2D;  
        }  
#endif  
    }  
    //-----------------------------------------【OnDisable()函数】---------------------------------------    
    // 说明:当对象变为不可用或非激活状态时此函数便被调用    
    //--------------------------------------------------------------------------------------------------------  
    void OnDisable()  
    {  
        if (CurMaterial)  
        {  
            //立即销毁材质实例  
            DestroyImmediate(CurMaterial);  
        }  
    }  
}  

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论