参考
https://roystan.net/articles/toon-water/
源码
https://github.com/IronWarrior/ToonWaterShader
两张噪声图
挂在相机上,开启深度模式,使shader可以拿到深度图
using UnityEngine;public class ChangeCameraDepth : MonoBehaviour
{public DepthTextureMode textureMode;private void OnValidate(){GetComponent<Camera>().depthTextureMode = textureMode;}private void Awake(){GetComponent<Camera>().depthTextureMode = textureMode;}}
水面shader
Shader "Custom/Water"
{Properties{_ShallowColor ("_ShallowColor", Color) = (0.4,0.9,1,1)_DeepColor ("_DeepColor", Color) = (0,0.2,0.7,1)_FoamColor("_FoamColr",COLOR) = (1,1,1,1)//这里用黑白噪声_NoiseTex ("_NoiseTex", 2D) = "white" {}//用彩色噪声表示扭曲_DistortionTex("_DistortionTex",2D) = "white"{}_FoamMinDistance("FoamMinDistance",float) = 0.02_FoamMaxDistance("_FoamMaxDistance",float) = 0.05_SurferNoiseCutoff("_SurferNoiseCutoff",Range(0,1)) = 0.7_foamSpeed("_foamSpeed",vector) = (0.3,0.3,0,0)}SubShader{Tags { "Queue" = "Transparent" }pass{Blend SrcAlpha OneMinusSrcAlphaZWrite OffCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"float4 _ShallowColor;float4 _DeepColor;float4 _FoamColor;float _FoamMinDistance;float _FoamMaxDistance;float _SurferNoiseCutoff;//水波移动速度,只用xy两个参数float2 _foamSpeed;sampler2D _CameraDepthTexture;sampler2D _CameraNormalsTexture;sampler2D _NoiseTex;sampler2D _DistortionTex;float4 _NoiseTex_ST;float4 _DistortionTex_ST;struct appdata{float4 vertex:POSITION;float2 uv:TEXCOORD0;float3 normal:NORMAL;};struct v2f{float4 pos:SV_POSITION;float4 screenPos:TEXCOORD0;float2 noiseUV:TEXCOORD1;float2 distortionUV:TEXCOORD2;float3 viewNormal:NORMAL;};v2f vert(appdata v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);//齐次坐标下的屏幕坐标值o.screenPos = ComputeScreenPos(o.pos);o.viewNormal = COMPUTE_VIEW_NORMAL;o.noiseUV = TRANSFORM_TEX(v.uv,_NoiseTex);o.distortionUV = TRANSFORM_TEX(v.uv,_DistortionTex);return o;}float4 frag(v2f i):SV_TARGET0{float4 depth = tex2Dproj(_CameraDepthTexture,UNITY_PROJ_COORD( i.screenPos));//等价于//depth = tex2D(_CameraDepthTexture,i.screenPos.xy/i.screenPos.w);float depthliner = LinearEyeDepth( depth.x);//i.screenPos.zw= i.pos.zw,所以这里还可以替换成i.pos.w ,pos.w就是裁剪空间里与相机的距离(大概是,之后去确认下)float depthDiff = depthliner-i.screenPos.w;//这步非常重要,不然把视角拉平之后会出现颜色堆积渐变,也就是远处颜色深近处颜色浅,此外,在linner色彩空间下明显,在gamma空间下不明显,不太明白为啥float waterDepthDifference = saturate(depthDiff/1);float3 existingNormal = tex2Dproj(_CameraNormalsTexture,UNITY_PROJ_COORD(i.screenPos));//相当于拿到屏幕坐标和view坐标下法线的夹角,以此判断物体边缘float normalDot = saturate(dot(existingNormal,i.viewNormal));float normalDistance = lerp(_FoamMinDistance,_FoamMaxDistance,normalDot);float foamDepthDiff = saturate(depthDiff/normalDistance) *_SurferNoiseCutoff;//水的小扭曲水花float4 distortionSample = tex2D(_DistortionTex,i.distortionUV);float2 noiseUV = i.noiseUV;noiseUV =float2(noiseUV.x+ _Time.y*_foamSpeed.x+distortionSample.r,noiseUV.y+_Time.y*_foamSpeed.y+distortionSample.g);float surferNoiseSample = tex2D(_NoiseTex,noiseUV).r;float4 waterColor = lerp(_ShallowColor,_DeepColor,waterDepthDifference);float4 foamColor = _FoamColor;foamColor.a = smoothstep( foamDepthDiff-0.1,foamDepthDiff+0.1,surferNoiseSample);float4 res = lerp(waterColor,foamColor,foamColor.a);//调下透明度,让水底物体可见,而不是深度图的颜色res.a = 0.7;return res;}ENDCG}}}
核心就两点,一是对_CameraDepthTexture进行采样,获得水深水浅效果,同时获得物体水下的效果
二是对_CameraNormalsTexture进行采样拿到屏幕深度图,再和水面的view空间下的法线进行点乘,可以获得物体与水面相交的泡沫
SmoothStep用来生成:指定范围内0到1的平滑过渡值smoothstep(a,b,x) x取a-b之间
简化其中的步骤,使
foamColor.a = saturate((0.1-foamDepthDiff)/0.2);
return(0,0,0,0.7-foamDepthDiff);
可以单输出泡沫
shader想弄明白,就一条一条去把每行代码修改的结果输出出来看看,这个折腾了很长时间就是因为数值太小,输出在r通道上看不出来,弄到a通道上就清楚了