0
\$\begingroup\$

I am using a shader that is applying a visual deformation that looks like Gerstner waves.

It works great, but I need the mesh's actual vertices to deform based on these formulas as well.

How can this be transferred to a C# script to to deform the mesh's actual vertices in the same way?

Shader hlsl code:

struct WaveInfo
{
    float3 position;
    float3 normal;
};

float3 GerstnerOffset4(float2 xzVtx, float4 steepness, float4 amp, float4 freq, float4 speed, float4 dirAB, float4 dirCD)
{
    float3 offsets;

    float4 AB = steepness.xxyy * dirAB.xyzw * amp.xxyy;
    float4 CD = steepness.zzww * dirCD.xyzw * amp.zzww;

    float4 dotABCD = freq.xyzw * float4(dot(dirAB.xy, xzVtx), dot(dirAB.zw, xzVtx), dot(dirCD.xy, xzVtx), dot(dirCD.zw, xzVtx));

    float4 COS = cos(dotABCD + speed);
    float4 SIN = sin(dotABCD + speed);

    offsets.x = dot(COS, float4(AB.xz, CD.xz));
    offsets.z = dot(COS, float4(AB.yw, CD.yw));
    offsets.y = dot(SIN, amp); //Remap to only positive values;

    return offsets;
}

float3 GerstnerNormal4(float2 xzVtx, float4 amp, float4 freq, float4 speed, float4 dirAB, float4 dirCD)
{
    float3 nrml = float3(0, 2.0, 0);

    float4 AB = freq.xxyy * amp.xxyy * dirAB.xyzw;
    float4 CD = freq.zzww * amp.zzww * dirCD.xyzw;

    float4 dotABCD = freq.xyzw * float4(dot(dirAB.xy, xzVtx), dot(dirAB.zw, xzVtx), dot(dirCD.xy, xzVtx), dot(dirCD.zw, xzVtx));

    float4 COS = cos(dotABCD + speed);

    nrml.x -= dot(COS, float4(AB.xz, CD.xz));
    nrml.z -= dot(COS, float4(AB.yw, CD.yw));

    nrml.xz *= _WaveNormalStr;
    nrml = normalize(nrml);

    return nrml;
}

void Gerstner(inout float3 offs, inout float3 nrml,
    float2 position,
    float4 amplitude, float4 frequency, float4 steepness,
    float4 speed, float4 directionAB, float4 directionCD)
{
    offs += GerstnerOffset4(position, steepness, amplitude, frequency, speed, directionAB, directionCD);
    #ifdef CALCULATE_NORMALS
    nrml += GerstnerNormal4(position, amplitude, frequency, speed, directionAB, directionCD);
    #endif
}

#define WAVE_COUNT _WaveCount
#define MAX_WAVE_COUNT 5


#define STEEPNESS_SCALE 0.01

//v1.1.8+
WaveInfo GetWaveInfo(float2 position, float2 time, float height, float mask, float fadeStart, float fadeEnd)
{
    WaveInfo waves = (WaveInfo)0;

    float4 amp = float4(0.3, 0.35, 0.25, 0.25);
    float4 freq = float4(1.3, 1.35, 1.25, 1.25) * (1-_WaveDistance) * 3.0;
    const float4 speed = float4(1.2* time.x, 1.375* time.y, 1.1 * time.x, time.y) ; //Pre-multiplied with time
    const float4 dir1 = float4(0.3, 0.85, 0.85, 0.25) * _WaveDirection;
    const float4 dir2 = float4(0.1, 0.9, -0.5, -0.5) * _WaveDirection;
    const float4 steepness = float4(12.0, 12.0, 12.0, 12.0) * _WaveSteepness * lerp(1.0, MAX_WAVE_COUNT, 1/WAVE_COUNT);

    //Distance based scalar
    float pixelDist = length(GetCurrentViewPosition().xz - position.xy);
    float fadeFactor = saturate((fadeEnd - pixelDist ) / (fadeEnd-fadeStart));

    for (uint i = 0; i <= WAVE_COUNT; i++)
    {
        float t = 1+((float)i / (float)WAVE_COUNT);
        freq *= t;
        amp *= fadeFactor;
        
        Gerstner(/*out*/ waves.position, /*out*/ waves.normal, position, amp, freq, steepness, speed, dir1, dir2);
    }

    waves.normal = normalize(waves.normal);
    //Average
    waves.position.y /= WAVE_COUNT;
    //waves.normal.xz *= WAVE_COUNT;

    waves.position.xz *= STEEPNESS_SCALE * height * mask;
    waves.position.y *= height * mask;
    
    return waves;
}

//Depricated
WaveInfo GetWaveInfo(float2 position, float2 time, float fadeStart, float fadeEnd)
{
    return GetWaveInfo(position, time, 1.0, 1.0, fadeStart, fadeEnd);
}
\$\endgroup\$
3
  • \$\begingroup\$ You may be interested in this previous question about matching the CPU-side shape of a water mesh to the vertex shader output. tl;dr: that's probably not the best way to achieve what you want. If you need to simulate collision with the water / buoyancy, you're better off just sampling the height function around the objects that need to interact with the water, not burning the CPU transforming every vertex and updating mesh collisions. \$\endgroup\$
    – DMGregory
    Commented Jan 27, 2023 at 0:31
  • \$\begingroup\$ The issue I'm having is that I need(or maybe don't need but don't know how to do it another way) to use Mesh Deformers like 'whirlpool' deformers in certain locations of the water mesh. The whirlpool locations should be dynamic because it is based on a boss attacking player location. I don't know how to make the water shader add deformations at certain areas like that, so being able to query for the height at a 'whirlpool' location seems to require using actual mesh deformation as a base. Very interested in any suggestions. \$\endgroup\$
    – yukjaly7
    Commented Jan 27, 2023 at 1:40
  • \$\begingroup\$ Want to edit your question to ask "how to add whirlpools to my water"? If you have a relatively small number of whirlpools, I'd say it's still easier to add them in the shader and steer them with uniforms, and modify your height samples by proximity. There might also be interesting things you could do with decals, depending on your visual target. \$\endgroup\$
    – DMGregory
    Commented Jan 27, 2023 at 2:52

0

You must log in to answer this question.

Browse other questions tagged .