水效果

水面颜色渐变效果

深度图

定义

使用深度图来决定水体表面和底部的距,从而改变该部分水体的颜色,达到一个从岸边到深水区的颜色渐变效果

深度图在Shader使用

使用SurfaceShader

深度图获取

固定名称

1
2
3
sampler2D _CameraDepthTexture;

//sampler2D_float ,增加精度,预防某些手机精度不够

同时还需要开启摄像机的深度

深度图采样

深度图渲染出来后在屏幕空间内,要到屏幕空间进行采样,需要获取到屏幕坐标的UV才能对深度图进行采样

Input结构体中额外定义

1
float4 proj;//屏幕坐标

属性

1
2
3
4
fixed4 _WaterShallowColor;//浅水区的颜色
fixed4 _WaterDeepColor;//深水区的颜色
half _DeepPower;//水深强度
half _TranAmount;//透明度数

顶点着色器

1
2
3
i.proj = ComputeScreenPos(UnityObjectToClipPos(v.vertex));//根据顶点计算屏幕空间,没有齐次裁剪

COMPUTE_EYEDEPTH(i.proj.z);//将摄像机的深度传递给固给定的值

surf方法

提取深度,转换到线性视角空间,求出水平面到水底的深度,使用lerp差值进行过度

1
2
3
4
5
6
7
8
9
10
//下面的宏的第二个宏是接受一个float4 的变量,返回适合深度图采样的变量,大部分平台都是原路返回
// half depth = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture,UNITY_PROJ_COORD(IN.proj));
half depth = LinearEyeDepth(tex2Dproj(_CameraDepthTexture,UNITY_PROJ_COORD(IN.proj)).r);//投影矩阵,将深度转换为一个线性值,转换到屏幕空间的第一个线性值
//摄像机的深度减去当前物体的深度,得到结果是水深
//实际上还
half deltaDepth = depth - IN.proj.z;
// Albedo comes from a texture tinted by color
fixed4 c = lerp(_WaterShallowColor,_WaterDeepColor,min(_DeepPower,deltaDepth)/_DeepPower);
o.Albedo = c;
o.Alpha = min(_TranAmount,deltaDepth)/_TranAmount;

IN.proj中的计算在顶点着色器中计算缺少对齐次空间的裁剪,对屏幕空间进行采样的时候需要 /w,depth的计算过程中使用的tex2Dproj 就默认使用了这一点

采样出来的深度图不是线性变化的,需要使用线性化方法处理,LinearEyeDepth就是线性化到视角空间

1
half depth = LinearEyeDepth(tex2Dproj(_CameraDepthTexture,UNITY_PROJ_COORD(IN.proj)).r);

Unity渲染物体根据渲染循序从小到大进行渲染,这里的水使用的是透明通道,Transparent,放在最后渲染,这个时候的深度图就是纯地面,结果就是depth,IN.proj的通道在顶点着色器中保存的是水平面的深度值,相减就是水深,即从水面水底的距离。

1
half deltaDepth = depth - IN.proj.z;

较好的法线移动效果

属性

1
2
float _WaterSpeed;//水法线流动速度
float _Refract;//法线的密集度

surf方法

两个采样步骤

获取偏移,加上偏移采样

1
2
3
4
5
6
7
float4 bumpOffset1 = tex2D(_Normal,IN.uv_Normal + float2(_WaterSpeed * _Time.x,0));
float4 bumpOffset2 = tex2D(_Normal,IN.uv_Normal + float2(1-IN.uv_Normal.y,IN.uv_Normal.x)+float2(_Time.x * _WaterSpeed,0));
float4 offsetColor = (bumpOffset1+ bumpOffset2)/2;
float2 offset = UnpackNormal(offsetColor).xy * _Refract;
float4 bumpColor1 = tex2D(_Normal,IN.uv_Normal +offset+ float2(_WaterSpeed * _Time.x,0));
float4 bumpColor2 = tex2D(_Normal,offset+float2(1-IN.uv_Normal.y,IN.uv_Normal.x)+float2(_Time.x * _WaterSpeed,0));
o.Normal = UnpackNormal((bumpColor1 + bumpColor2)/2);

光照模型

自定义光照模型,仅加入半兰伯特光照和BlinnPhone

顺便复习自定义光照函数的输入数据结果

1
2
3
4
5
6
7
8
9
10
11
fixed4 LightingWaterLight(SurfaceOutput s,fixed3 lightDir,half3 viewDir,fixed atten)
{
float diffuse = dot(normalize(lightDir),s.Normal) * 0.5 + 0.5;
half3 halfView = normalize(lightDir + viewDir);
float nh = max(0,dot(halfView,s.Normal));
float spec = pow(nh,s.Specular * 128) * s.Gloss;
fixed4 color;
color.rgb = (s.Albedo * _LightColor0.rgb * diffuse + _SpecularColor.rgb * spec * _LightColor0.rgb) * atten;
color.a = s.Alpha + spec * _SpecularColor.a;
return color;
}

波浪和菲涅尔反射

抓屏的声明

1
GrabPass{"GrabPass"}

抓屏属性定义

1
2
 sampler2D GrabPass;
float4 GrabPass_TexelSize;

实现波浪和菲涅尔反射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
         //波浪
half waveB = 1 - min(_WaveRangeA, deltaDepth) / _WaveRangeA;
fixed4 noiserColor = tex2D(_NoiseTex, IN.uv_NoiseTex);

fixed4 waveColor = tex2D(_WaveTex, float2(waveB + _WaveRange * sin(_Time.x * _WaveSpeed + noiserColor.r), 1) + offset);
// waveColor.rgb *= (1 - (sin(_Time.x * _WaveSpeed + noiserColor.r) + 1) / 2) * noiserColor.r;
fixed4 waveColor2 = tex2D(_WaveTex, float2(waveB + _WaveRange * sin(_Time.x * _WaveSpeed + _WaveDelta + noiserColor.r), 1) + offset);
// waveColor2.rgb *= (1 - (sin(_Time.x * _WaveSpeed + _WaveDelta + noiserColor.r) + 1) / 2) * noiserColor.r;

//抓屏操作
//offset利用前几步的定义
offset = normal.xy * _Distortion * GrabPass_TexelSize.xy;
IN.proj.xy = offset * IN.proj.z + IN.proj.xy;//添加扰动

//充当折射光
//进行齐次裁剪
fixed3 refrCol = tex2D(GrabPass, IN.proj.xy / IN.proj.w).rgb;

//菲涅尔反射
fixed3 reflaction = texCUBE(_Cubemap, WorldReflectionVector(IN, normal)).rgb;

fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(IN.viewDir, WorldNormalVector(IN, normal)
), 5);

//使用菲涅尔系数进行插值
//菲涅尔差值计算,先反射,后折射
fixed3 refrAndRefl = lerp(reflaction, refrCol, saturate(fresnel));

o.Albedo = (c + (waveColor.rgb + waveColor2.rgb) * waveB) * refrAndRefl;;

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
Shader "Custom/28"
{
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)
_DeepPower("Deep",Range(0,100)) = 2
_TranAmount("TranAmount",Range(0,100)) = 0.5//水的透明度
_Normal("Normal",2D) = "bump" {}
_WaterSpeed("WaterSpeed",float) = 2 //水流动的速度
_Refract("Refract",Float) = 0.2
_Metallic ("Specular", Float) = 0.0
_Glossiness ("Gloss", Float) = 0.5
_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
_Distortion("Distortion",float) = 0.5
_Cubemap("Cubemap",Cube) = "_Skybox"{}
_FresnelScale("Fresnel",Range(0,1)) = 0.5

}
SubShader
{
Tags { "RenderType"="Transparent" "Queue" = "Transparent"}

LOD 200

GrabPass{"GrabPass"}
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

sampler2D _MainTex;
sampler2D _CameraDepthTexture;
sampler2D _Normal;
sampler2D _WaveTex;//波浪贴图
sampler2D _NoiseTex;//噪声贴图
sampler2D GrabPass;
float4 GrabPass_TexelSize;
samplerCUBE _Cubemap;


struct Input
{
float2 uv_MainTex;
float4 proj;//屏幕坐标
float2 uv_Normal;
float2 uv_WaveTex;
float2 uv_NoiseTex;
float3 worldRefl;
float3 viewDir;
float3 worldNormal;
INTERNAL_DATA
};

half _Glossiness;
half _Metallic;
fixed4 _Color;

fixed4 _WaterShallowColor;//浅水区的颜色
fixed4 _WaterDeepColor;//深水区的颜色
half _DeepPower;//水深强度
half _TranAmount;//透明度数
float _WaterSpeed;//水法线流动速度
float _Refract;//法线的密集
fixed4 _SpecularColor;

float _WaveSpeed;
float _WaveRange;
float _WaveRangeA;
float _WaveDelta;
float _Distortion;
float _FresnelScale;


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



// 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)


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)
{
//下面的宏的第二个宏是接受一个float4 的变量,返回适合深度图采样的变量,大部分平台都是原路返回
// half depth = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture,UNITY_PROJ_COORD(IN.proj));
half depth = LinearEyeDepth(tex2Dproj(_CameraDepthTexture,UNITY_PROJ_COORD(IN.proj)).r);//投影矩阵,将深度转换为一个线性值,转换到屏幕空间的第一个线性值
//摄像机的深度减去当前物体的深度,得到结果是水深
//实际上还
half deltaDepth = depth - IN.proj.z;
// Albedo comes from a texture tinted by color
fixed4 c = lerp(_WaterShallowColor,_WaterDeepColor,min(_DeepPower,deltaDepth)/_DeepPower);

float4 bumpOffset1 = tex2D(_Normal,IN.uv_Normal + float2(_WaterSpeed * _Time.x,0));
float4 bumpOffset2 = tex2D(_Normal,IN.uv_Normal + float2(1-IN.uv_Normal.y,IN.uv_Normal.x)+float2(_Time.x * _WaterSpeed,0));
float4 offsetColor = (bumpOffset1+ bumpOffset2)/2;
float2 offset = UnpackNormal(offsetColor).xy * _Refract;
float4 bumpColor1 = tex2D(_Normal,IN.uv_Normal +offset+ float2(_WaterSpeed * _Time.x,0));
float4 bumpColor2 = tex2D(_Normal,offset+float2(1-IN.uv_Normal.y,IN.uv_Normal.x)+float2(_Time.x * _WaterSpeed,0));

float3 normal = UnpackNormal((bumpColor1 + bumpColor2)/2);

//波浪
half waveB = 1 - min(_WaveRangeA, deltaDepth) / _WaveRangeA;
fixed4 noiserColor = tex2D(_NoiseTex, IN.uv_NoiseTex);

fixed4 waveColor = tex2D(_WaveTex, float2(waveB + _WaveRange * sin(_Time.x * _WaveSpeed + noiserColor.r), 1) + offset);
// waveColor.rgb *= (1 - (sin(_Time.x * _WaveSpeed + noiserColor.r) + 1) / 2) * noiserColor.r;
fixed4 waveColor2 = tex2D(_WaveTex, float2(waveB + _WaveRange * sin(_Time.x * _WaveSpeed + _WaveDelta + noiserColor.r), 1) + offset);
// waveColor2.rgb *= (1 - (sin(_Time.x * _WaveSpeed + _WaveDelta + noiserColor.r) + 1) / 2) * noiserColor.r;

//抓屏操作
offset = normal.xy * _Distortion * GrabPass_TexelSize.xy;
IN.proj.xy = offset * IN.proj.z + IN.proj.xy;

//充当折射光
fixed3 refrCol = tex2D(GrabPass, IN.proj.xy / IN.proj.w).rgb;

//菲涅尔反射
fixed3 reflaction = texCUBE(_Cubemap, WorldReflectionVector(IN, normal)).rgb;

fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(IN.viewDir, WorldNormalVector(IN, normal)
), 5);

//使用菲涅尔系数进行插值
//菲涅尔差值计算,先反射,后折射
fixed3 refrAndRefl = lerp(reflaction, refrCol, saturate(fresnel));

o.Albedo = (c + (waveColor.rgb + waveColor2.rgb) * waveB) * refrAndRefl;;
// Metallic and smoothness come from slider variables
o.Normal = normal;
o.Specular = _Metallic;
o.Gloss = _Glossiness;
o.Alpha = min(_TranAmount,deltaDepth)/_TranAmount;
}
ENDCG
}
}

效果示例

image-20241115175131218 image-20241115175602292 2024-11-21 18-04-02 -big-original