Unity Shader学习笔记(28)噪声纹理、消融效果、水波效果、噪声雾效
发表于2018-01-02
本篇文章给大家介绍几种噪声效果,让大家了解噪声纹理、消融效果、水波效果、噪声雾效这些效果的实现。
噪声纹理
通过纹理深浅可以作为一些变换的阈值,或者透明度等。=
消融效果(Dissolve)
常用与角色死亡、地图烧毁时效果。
Burn Amount从0变化到1。
Properties { _BurnAmount ("Burn Amount", Range(0.0, 1.0)) = 0.0 // 消融程度 0为正常效果 _LineWidth("Burn Line Width", Range(0.0, 0.2)) = 0.1 // 消融效果时的线宽 _MainTex ("Base (RGB)", 2D) = "white" {} // 漫反射纹理 _BumpMap ("Normal Map", 2D) = "bump" {} // 法线纹理 _BurnFirstColor("Burn First Color", Color) = (1, 0, 0, 1) // 火焰边缘颜色值 _BurnSecondColor("Burn Second Color", Color) = (1, 0, 0, 1) _BurnMap("Burn Map", 2D) = "white"{} // 噪声纹理 } SubShader { Tags { "RenderType"="Opaque" "Queue"="Geometry"} Pass { Tags { "LightMode"="ForwardBase" } Cull Off // 关掉剔除,正反面都渲染 ... fixed4 frag(v2f i) : SV_Target { ... fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb; clip(burn.r - _BurnAmount); // 如果小于0,该像素会被剔除 //_LineWidth宽度范围模拟渐变。t为0是正常颜色,为1时位于消融的边界 fixed t = 1 - smoothstep(0.0, _LineWidth, burn.r - _BurnAmount); fixed3 burnColor = lerp(_BurnFirstColor, _BurnSecondColor, t); //两个烧焦颜色做插值 burnColor = pow(burnColor, 5); // 加强颜色模拟烧焦痕迹 UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos); fixed3 finalColor = lerp(ambient + diffuse * atten, burnColor, t * step(0.0001, _BurnAmount)); return fixed4(finalColor, 1); } ENDCG } // 阴影投射,直接剔除掉阈值内的像素即可 Pass { Tags { "LightMode" = "ShadowCaster" } ... fixed4 frag(v2f i) : SV_Target { fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb; clip(burn.r - _BurnAmount); // 剔除掉透明的 SHADOW_CASTER_FRAGMENT(i) } ENDCG } }
水波效果
噪声作为高度图,修改水面法线。使用Schilick菲涅尔反射实现:fresnel = pow(1 - max(0, v·n),4 )。
菲涅尔动态水面效果:
Properties { _Color ("Main Color", Color) = (0, 0.15, 0.115, 1) _MainTex ("Base (RGB)", 2D) = "white" {} _WaveMap ("Wave Map", 2D) = "bump" {} // 噪声法线纹理 _Cubemap ("Environment Cubemap", Cube) = "_Skybox" {} // 立方体纹理 _WaveXSpeed ("Wave Horizontal Speed", Range(-0.1, 0.1)) = 0.01 // 法线平移速度 _WaveYSpeed ("Wave Vertical Speed", Range(-0.1, 0.1)) = 0.01 _Distortion ("Distortion", Range(0, 100)) = 10 // 折射扭曲度 } SubShader { // 所有不透明物体渲染完后 Tags { "Queue"="Transparent" "RenderType"="Opaque" } // 抓取屏幕Pass 存入_RefractionTex GrabPass { "_RefractionTex" } Pass { ... #pragma multi_compile_fwdbase ... sampler2D _RefractionTex; // 对应GrabPass float4 _RefractionTex_TexelSize; ... v2f vert(a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.scrPos = ComputeGrabScreenPos(o.pos); // 抓取屏幕图像的采样坐标 ...// 计算世界空间下切线法线等,构造转换矩阵 return o; } fixed4 frag(v2f i) : SV_Target { float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w); fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos)); float2 speed = _Time.y * float2(_WaveXSpeed, _WaveYSpeed); // 对法线两次采样(模拟两层交叉水面波动),相加得到切线空间下的法线方向。 fixed3 bump1 = UnpackNormal(tex2D(_WaveMap, i.uv.zw + speed)).rgb; fixed3 bump2 = UnpackNormal(tex2D(_WaveMap, i.uv.zw - speed)).rgb; fixed3 bump = normalize(bump1 + bump2); // 计算偏移量,切线空间 float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy; i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy; // offset * i.scrPos.z 模拟水越深,折射越大 fixed3 refrCol = tex2D( _RefractionTex, i.scrPos.xy/i.scrPos.w).rgb; // 计算法线到世界空间 bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump))); fixed4 texColor = tex2D(_MainTex, i.uv.xy + speed); fixed3 reflDir = reflect(-viewDir, bump); fixed3 reflCol = texCUBE(_Cubemap, reflDir).rgb * texColor.rgb * _Color.rgb; // Schilick菲涅尔近似等式 fixed fresnel = pow(1 - saturate(dot(viewDir, bump)), 4); fixed3 finalColor = reflCol * fresnel + refrCol * (1 - fresnel); return fixed4(finalColor, 1); } ENDCG } }
噪声雾效
前面实现过雾效在同一高度的浓度是一致的,这里使用噪声模拟雾的浓度,实现非均匀雾效。
在之前实现的雾效添加一些属性。
Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _FogDensity ("Fog Density", Float) = 1.0 _FogColor ("Fog Color", Color) = (1, 1, 1, 1) _FogStart ("Fog Start", Float) = 0.0 _FogEnd ("Fog End", Float) = 1.0 _NoiseTex ("Noise Texture", 2D) = "white" {} // 噪声纹理 _FogXSpeed ("Fog Horizontal Speed", Float) = 0.1 _FogYSpeed ("Fog Vertical Speed", Float) = 0.1 _NoiseAmount ("Noise Amount", Float) = 1 // 噪声数量,0为不使用噪声 }
fixed4 frag(v2f i) : SV_Target { float linearDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth)); float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.interpolatedRay.xyz; float2 speed = _Time.y * float2(_FogXSpeed, _FogYSpeed); float noise = (tex2D(_NoiseTex, i.uv + speed).r - 0.5) * _NoiseAmount; // 随时间偏移雾效,-0.5是把范围控制在[-0.5,0.5] float fogDensity = (_FogEnd - worldPos.y) / (_FogEnd - _FogStart); fogDensity = saturate(fogDensity * _FogDensity * (1 + noise)); // (1 + noise):浓度倍数范围在[0.5, 1.5]内。 fixed4 finalColor = tex2D(_MainTex, i.uv); finalColor.rgb = lerp(finalColor.rgb, _FogColor.rgb, fogDensity); return finalColor; }
Unity Shader学习笔记系列教程: