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

相关推荐
寻水的鱼、、7 天前
【Unity Shader学习笔记】(二)图形显示系统
unity·shader
淡海水13 天前
【URP】Unity Shader Tags
unity·游戏引擎·渲染·shader·tag·urp
淡海水15 天前
【URP】Unity 插入自定义RenderPass
unity·游戏引擎·渲染·shader·renderpass
benben0441 个月前
《Unity Shader入门精要》学习笔记一
unity·shader
两水先木示1 个月前
【Unity3D】Shader圆形弧度裁剪
unity·shader·圆形裁剪·弧度裁剪
龚子亦3 个月前
【Shader学习】完整光照效果
unity·技术美术·shader
achonor4 个月前
UnityShader 植物被风吹弯效果
unity·shader·mesh
优雅永不过时·4 个月前
实现一个漂亮的Three.js 扫光地面 圆形贴图扫光
前端·javascript·智慧城市·three.js·贴图·shader
太妃糖耶4 个月前
URP - 深度图
unity·shader
大模型铲屎官4 个月前
Unity C# 与 Shader 交互入门:脚本动态控制材质与视觉效果 (含 MaterialPropertyBlock 详解)(Day 38)
c语言·unity·c#·交互·游戏开发·材质·shader