[unity shader/stylized water surface rendering/basic notes] urp code version 04-normal and vertex wave animation

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

        }

insert image description here

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
insert image description here
you can see that this is generated _CameraOpaqueTexturebefore the translucent rendering, that is to say, there will be no translucent objects in this data, this part needs attention
insert image description here
Now use it _CameraOpaqueTextureto achieve screen capture

float2 uvSS = screenPos.xy;
real4 grab_col = SAMPLE_TEXTURE2D(_CameraOpaqueTexture, sampler_CameraOpaqueTexture, uvSS);

insert image description here
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;

insert image description here

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

insert image description here

2 Vertex animation


Use the subdivided plane for vertex animation
insert image description here

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 GerstnerWavethe 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
insert image description here

Guess you like

Origin blog.csdn.net/qq_43544518/article/details/128757913