foreword
The previous section colored the water surface, and this section sorts out the generation of waves on the water surface. It is divided into two parts: normal and vertex-
the knowledge points involved
screen capture
GerstnerWave algorithm
1 normal
Since lighting does not participate in the calculation in this case, the normals only distort the underwater content (implemented by screen capture). The normal is sampled twice here, and the animation speed and tile are different to simulate the irregularity of wave motion.
1.1 Get tangent and binormal
o.tanW = normalize(TransformObjectToWorldDir(v.tangent.xyz));
o.biW = normalize(cross(v.normal.xyz, v.tangent.xyz)
* v.tangent.w * unity_WorldTransformParams.w);
1.2 Normal sampling and blending
- Normal blend function
_blendNormal
BlendedNormal = normalize( float3( A.xy + B.xy, A.z*B.z ).
- Normals in tangent space
This section only uses normal information in tangent space.
float3 _getNormalTS(float2 uv)
{
uv = uv / _NormalScale;
float speed = _Time.y * _NormalSpeed * 0.1;
real4 normalData1 = SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, uv+speed);
float3 normalT1 = UnpackNormalScale(normalData1, _NormalStr);
real4 normalData2 = SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap,
uv*0.2 + speed * (-0.35));
float3 normalT2 = UnpackNormalScale(normalData2, _NormalStr);
return _blendNormal(normalT1, normalT2);
}
1.3 Screen capture
The usage of Grabpass in the built-in pipeline is as follows
GrabPass {
"_GrabTex"}
Urp does not have a direct screen capture function, so the purpose of this section is to achieve a function similar to grabpass . There are several ways to achieve it. Here we only list the most convenient method. For other methods and details, please refer to my urp Yuque notes.
Go back to the pipeline settings, check it, Opaque Texture
you can see that this is generated _CameraOpaqueTexture
before the translucent rendering, that is to say, there will be no translucent objects in this data, this part needs attention
Now use it _CameraOpaqueTexture
to achieve screen capture
float2 uvSS = screenPos.xy;
real4 grab_col = SAMPLE_TEXTURE2D(_CameraOpaqueTexture, sampler_CameraOpaqueTexture, uvSS);
Sample coordinates are screen-space coordinates that undergo a homogeneous division so that they map correctly.
Then we use the previous tangent space normal to do the distortion
uvSS += 0.01 * normalTS * _UWDistort;
1.4 Mixing with water surface color
The underwater distortion is only in the transparent part of the mesh on the water surface, and then add the shore foam, so the code is as follows
// =========Mix Results=========== //
base_col = lerp(grab_col, base_col, base_col.a);
base_col = lerp(base_col, base_col+sinWave, sinWave.a);
2 Vertex animation
Use the subdivided plane for vertex animation
GerstnerWave
Create the GerstnerWave function here and call it three times.
The custom variables waveA, waveB, and waveC are Vector data types, and the components represent:
SpeedXY, Steepness, and wavelength
float3 GerstnerWave( float3 position, inout float3 tangent, inout float3 binormal, float4 wave )
{
float steepness = wave.z * 0.01;
float wavelength = wave.w;
float k = 2 * 3.14159 / wavelength;
float c = sqrt(9.8 / k);
float2 d = normalize(wave.xy);
float f = k * (dot(d, position.xz) - c * _Time.y);
float a = steepness / k;
tangent += float3(
-d.x * d.x * (steepness * sin(f)),
d.x * (steepness * cos(f)),
-d.x * d.y * (steepness * sin(f))
);
binormal += float3(
-d.x * d.y * (steepness * sin(f)),
d.y * (steepness * cos(f)),
-d.y * d.y * (steepness * sin(f))
);
return float3(
d.x * (a * cos(f)),
a * sin(f),
d.y * (a * cos(f))
);
}
The horizontal plane uses the default world tangent and normal, we use GerstnerWave
the function to modify the vertices and normal of the model space
void _getVertexData(inout a2v v)
{
float3 tangent = float3( 1,0,0 );
float3 binormal = float3( 0,0,1 );
float3 posW = TransformObjectToWorld(v.vertex.xyz);
float3 wave1 = GerstnerWave(posW, tangent,binormal, _WaveA);
float3 wave2 = GerstnerWave(posW, tangent,binormal, _WaveB);
float3 wave3 = GerstnerWave(posW, tangent,binormal, _WaveC);
posW = posW + wave1 + wave2 + wave3;
v.vertex.xyz = TransformWorldToObject(posW.xyz);
v.vertex.w = 1;
float3 normalW = normalize(cross(binormal, tangent));
v.normal = mul( unity_WorldToObject, float4(normalW, 0 ) ).xyz;
}
final adjustment result