UnityShader(五)

这次要用表面着色器实现一个水的特效。先翻到最下边看代码,看不懂再看下面的解释。

首先第一步要实现水的深浅判断,实现深水区和浅水区的区分。

这里需要用到深度图的概念。不去说太多概念,只去说怎么实现的,首先我们的水面是在地面上的,我们可以利用深度图,计算得到人眼到地面的距离depth,人眼到水面的距离记作IN.Proj.z

可以看的出来,当我们看深水区的时候,depth-IN.Proj.z的差值会比浅水区大,所以可以通过这个差值的大小去判断深水区和浅水区。这个IN就是我这个水面的其中某一块着色面片表面着色器的输入结构体值。depth可以通过调一系列api得到,里面涉及到很多空间变换,不仔细去讲了,只说大体思路。

然后,在判断完水的深浅以后,我们要通过设置一个深水色和一个浅水色,将我们的depth-IN.Proj.z映射到[0,1]中,这样通过对每个面片设置一个lerp差值,lerp(_WaterShallowColor,_WaterDeepColor, min(_DepthRange, deltaDepth)/_DepthRange),就可以得到整片水域的颜色深浅过度,而这个映射的过程,我们可以通过设置另外一个变量_DepthRange实现。

min(_DepthRange, deltaDepth)/_DepthRange,这个意思就是,当我水很深的时候,min(_DepthRange, deltaDepth)=_DepthRange,整体得1,所以在lerp函数中取的是深水色,当我水很浅的时候,min(_DepthRange, deltaDepth)=deltaDepth,lerp的第三个参数就成了deltaDepth/_DepthRange,因为水很浅,所以这个数接近0,颜色是接近浅水色,如果水的深浅介于深水和浅水之间,那么颜色也介于深水色和潜水色之间。但是这样深水和浅水虽然区分开了,但是都是纯色的,没有立体效果。

第二步通过对法线贴图采样,实现水的立体效果。

下面的代码是说,本来把法线贴图假设是第i,j个面片采样到我当前模型的面片,但是现在不是采样到我当前面片, 而是到了x方向加上_WaterSpeed * _Time.x这个值的面片,先不管_Time.x会动,因为uv的范围都是0-1,所以本质上是在0-1的范围内,采样的数量少了,因为是本来是法线0-1对应模型0-1,现在相当于同样一个点采样的距离大了,假设法线0-1对应模型0-1.4,模型1-1.4的不会显示,所以相当于是原来的0.6倍。

  float4 bumpOffset1 = tex2D(_NormalTex, IN.uv_NormalTex + float2(_WaterSpeed * _Time.x, 0));

剩下三个计算也是同理,只不过需要注意,_Time.x是会变的,这样子,本来法线贴图假设是第i,j个面片采样到模型的m,n个面片,一段时间后,就是对应到了模型第m + k,n个面片,所有面片都这样映射,相当于整体是移动了,也就是有了大波浪效果,汹涌澎湃。
  float4 bumpOffset2 = tex2D(_NormalTex, float2(1 - IN.uv_NormalTex.y, IN.uv_NormalTex.x) + float2(_WaterSpeed * _Time.x, 0));
  float4 offsetColor = (bumpOffset1 + bumpOffset2)/2;
  float2 offset = UnpackNormal(offsetColor).xy * _Refract;
  float4 bumpColor1 = tex2D(_NormalTex, IN.uv_NormalTex + offset + float2(_WaterSpeed * _Time.x, 0));
  float4 bumpColor2 = tex2D(_NormalTex, float2(1 - IN.uv_NormalTex.y, IN.uv_NormalTex.x) + offset + float2(_WaterSpeed * _Time.x, 0));


 第三步是要实现波纹,主要对浅水区,波纹的实现是要对WaveTex贴图进行采样。

WaveTex贴图就是一个黑白交替的图,白色表示有波纹,黑色表示没有,但它是0-1,对应模型0-1,但其实我们是需要将一个更大范围的x映射到一个更小范围的WaveTex贴图上的,此时就可以用到GPUGem里讲的正弦波模拟水面波纹了           

首先要对波形图和噪声图进行采样,对波形图进行采样两次,是为了产生两道波纹,采样的时候使用噪声图是为了产生随机的效果,对波形图采样要采用上面的正弦波的方式去采,这样就有一个波浪的效果了

            fixed4 waveColor = tex2D(_WaveTex,float2(waveA + _WaveRange * sin(_Time.x * _WaveSpeed + noiseColor.r) , 1) + offset);
            fixed4 waveColor2 = tex2D(_WaveTex,float2(waveA + _WaveRange * sin(_Time.x * _WaveSpeed + _WaveDelta + noiseColor.r) , 1) + offset);

Shader "Custom/MyWater2"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _WaterShallowColor("WaterShallowColor", Color) = (1,1,1,1)
        _WaterDeepColor("WaterDeepColor", Color) = (1,1,1,1)
        _TranAmount("TranAmount", Range(0,100)) = 0.5
        _DepthRange("DepthRange",float) = 1
        _NormalTex("Normal",2D) = "bump"{}
        _WaterSpeed("WaterSpeed",float) = 5
        //控制法线的密集程度
        _Refract("Refract",float) = 0.5
        _Specular("Specular",float) = 1
        _Gloss("Gloss",float) = 0.5
        _SpecularColor("_SpecularColor", Color) = (1,1,1,1)
        //波浪效果需要先对波浪图采样,噪声图是为了产生随机效果
        _WaveTex("WaveTex",2D) = "white"{}
        _NoiseTex("NoiseTex",2D) = "white"{}
        _WaveSpeed("WaveSpeed",float) = 1
        _WaveRange("WaveRange",float) = 0.5
        _WaveRangeA("WaveRangeA",float) = 1
        _WaveDelta("WaveDelta", float) = 0.5
    } 
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue"="Transparent"  }
        LOD 200
        //水是透明的,所以关闭ZWrite
        ZWrite off
        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf WaterLight vertex:vert alpha noshadow

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0
        //加float是为了增加精度
        sampler2D_float _CameraDepthTexture;
        sampler2D _MainTex;
        fixed4 _WaterShallowColor;
        fixed4 _WaterDeepColor;
        half _TranAmount;
        half _DepthRange;
        sampler2D _NormalTex;
        half _WaterSpeed;
        sampler2D _WaveTex;
        sampler2D _NoiseTex;
        float _WaveSpeed;
        float _WaveRange;
        //这两个控制波浪的范围
        float _WaveRangeA;
        float _WaveDelta;

        struct Input
        {
            float2 uv_MainTex;
            float4 proj;
            float2 uv_NormalTex;
            float2 uv_WaveTex;
            float2 uv_NoiseTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        half _Refract;
        half _Specular;
        half _Gloss;
        fixed4 _SpecularColor;

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

        fixed4 LightingWaterLight(SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten)
        {
            float diffuseFactor = max(0,dot(normalize(lightDir),s.Normal));
            half3 halfDir = normalize(lightDir + viewDir);
            float nh = max(0,dot(halfDir,s.Normal));
            float spec = pow(nh, s.Specular * 128) * s.Gloss;
            fixed4 c;
            c.rgb = (s.Albedo * _LightColor0.rgb * diffuseFactor + _SpecularColor.rgb * spec * _LightColor0.rgb) * atten;
            c.a = s.Alpha + spec * _SpecularColor.a;
            return c;
        }

        void vert(inout appdata_full v,out Input i)
        {
            UNITY_INITIALIZE_OUTPUT(Input, i);
            i.proj = ComputeScreenPos(UnityObjectToClipPos(v.vertex));
            COMPUTE_EYEDEPTH(i.proj.z);
        }

        void surf (Input IN, inout SurfaceOutput o)
        {
        //关键点是深度图判断深水和浅水,法线移动实现波浪效果
            //tex2Dproj(_CameraDepthTexture, IN.proj)=tex2D(_CameraDepthTexture, IN.proj.xy/IN.proj.w)
            //对深度图采样,采样屏幕空间
            //它的uv表示当前模型在顶点着色器画到屏幕后的坐标
            //采样后的每个变量都是一样的,取r即可
            //越近深度图上对应的区域越红(深度值接近1),越远则越黑(深度值接近0)。也就是说深度值从0到1代表的是从远到近。
            half depth = LinearEyeDepth(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(IN.proj)).r);
            //depth 就相当于是这样计算出来的
            //SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(IN.proj));
            //UNITY_PROJ_COORD(IN.proj)是返回的采样后的纹理坐标,大多数平台就是返回的IN.proj,所以写不写区别不大
            //摄像机深度贴图的深度值减去地面模型面片的深度值
            half deltaDepth = depth - IN.proj.z;
            
            fixed4 c = lerp(_WaterShallowColor,_WaterDeepColor, min(_DepthRange, deltaDepth)/_DepthRange);



            //对法线贴图采样
            float4 bumpOffset1 = tex2D(_NormalTex, IN.uv_NormalTex + float2(_WaterSpeed * _Time.x, 0));
            float4 bumpOffset2 = tex2D(_NormalTex, float2(1 - IN.uv_NormalTex.y, IN.uv_NormalTex.x) + float2(_WaterSpeed * _Time.x, 0));
            float4 offsetColor = (bumpOffset1 + bumpOffset2)/2;
            float2 offset = UnpackNormal(offsetColor).xy * _Refract;
            float4 bumpColor1 = tex2D(_NormalTex, IN.uv_NormalTex + offset + float2(_WaterSpeed * _Time.x, 0));
            float4 bumpColor2 = tex2D(_NormalTex, float2(1 - IN.uv_NormalTex.y, IN.uv_NormalTex.x) + offset + float2(_WaterSpeed * _Time.x, 0));
            
            //波浪 跟水的深浅相反即可 min(_DepthRange, deltaDepth)/_DepthRange接近1的地方,应该无波浪
            //因为波浪要出现在水的边缘,min(_DepthRange, deltaDepth)/_DepthRange接近1的地方 表示deltadepth很大,水很深
            half waveA = 1 - min(_WaveRangeA,deltaDepth)/_WaveRangeA;
            fixed4 noiseColor = tex2D(_NoiseTex,IN.uv_NoiseTex);
            //sin函数来对波纹图采样,产生流动效果
            fixed4 waveColor = tex2D(_WaveTex,float2(waveA + _WaveRange * sin(_Time.x * _WaveSpeed + noiseColor.r) , 1) + offset);
            fixed4 waveColor2 = tex2D(_WaveTex,float2(waveA + _WaveRange * sin(_Time.x * _WaveSpeed + _WaveDelta + noiseColor.r) , 1) + offset);
            o.Albedo = c.rgb + (waveColor.rgb + waveColor2.rgb) * waveA;
            o.Normal = UnpackNormal((bumpColor1 + bumpColor2)/2).xyz;
            o.Gloss = _Gloss;
            o.Specular = _Specular;


            o.Alpha = min(_TranAmount,deltaDepth) / _TranAmount;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

法线贴图,噪声图,波纹图网盘链接

链接:https://pan.baidu.com/s/1Qdm3ly2YW9vq9lNqYvZnbA?pwd=jk3l 
提取码:jk3l

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值