0
\$\begingroup\$

Had plans to follow GPU Gems to gain knowledge in Shaders - but my journey came to an abrupt end.

float Wave(x, y, t){
  float w = 2 / _Wavelength;
  float phase = _Speed * w;
  return float(_Amplitude * sin(dot(_Direction.xz, float2(x, y)) * w + t * phase);
}

...
o.vertex.y += Wave(v.vertex.x, v.vertex.z, _Time.y);

Offsetting the vertices was no issue, but then I met partial derivatives to solve the normal for each vertex. Starting at equation 4a to equation 7.

I assumed looking at equation 7 that one didn't have to fully grasp derivatives seeing as the last step

Equation 7: $$\sum(w_i * D_i \cdot x * A_i * cos(D_i \cdot (x, y) * w_i + t * \varphi_i))$$

So I tried to update the normal (well actually color, but just to visualize) using this function;

float3 normal(float x, float y, float t){
  float w = 2 / _Wavelength;
  float phase = _Speed * w;
  return float3(
    w * dot(_Direction.xz, float2(x, 0)) * _Amplitude * cos(dot(_Direction.xz, float2(x, y)) * w + t * phase),
    1,
    w * dot(_Direction.xz, float2(0, y)) * _Amplitude * cos(dot(_Direction.xz, float2(x, y)) * w + t * phase)
  )
}

...
o.color.xyz = normalize(normal(v.vertex.x, v.vertex.z, _Time.y));

It does however not seem to display what I would describe as good normals for the wave surface. So obviously I'm doing something wrong.

Expecting a smooth blend between red and blue here for example.

Ignoring the green channel here using the full code pasted below.

Would expect the colors to blend more smoothly and uniformly. Seeing I only got one wave this far.

What I am wondering is how to get the normals one would expect or if this is the expected result following the math (or rather what I could make of it) in GPU Gems.

Note: I am attemping this in Unity, so I did adapt what I assumed to be Z-up to Y-up from the GPU Gem book.

Paste to current code

GPU Gems Chapter 1

\$\endgroup\$
  • 1
    \$\begingroup\$ What is your question? How do you mean the displayed normals aren't good; in what way are they bad? A screenshot would help. \$\endgroup\$ – Junuxx Oct 22 '18 at 16:31
0
\$\begingroup\$

This is expected behavior

Your shader is a vertex shader, which means color values are calculated per vertex and linearly interpolated across the triangle, giving the non-uniform result you see. If your mesh had more triangles, those inconsistencies would decrease as more points are being calculated and the harsh transitions would be smaller.

Alternatively, you could use a different shader type (eg surface shader), but as the underlying data for the slope is vertex based, you'd be taking assumptions that would end up giving the same result (but take longer to compute) or write code that non-linearly computes the slope across your surface-- no assumptions!--(which would take the longest to compute).

\$\endgroup\$
  • \$\begingroup\$ Not really addressing my issue however. To the best of my ability I think I am following the algorithm provided by the GPU Gems chapter, however I obviously fail at calculating the vertex normals correctly. Seeing the wave travels diagonally I would expect the color to be float3(0.5, 0.0, 0.5); for every vertex on the diagonal. \$\endgroup\$ – Sindri Oct 23 '18 at 8:13
  • \$\begingroup\$ Ok, your issue is different than what I was understanding from your question. I'll have to look at this later, as I don't have a functioning PC presently. \$\endgroup\$ – Draco18s Oct 23 '18 at 13:42
0
\$\begingroup\$

I would like to add this as a comment but I don't have enough reputation. To get something working I had to change the formula a bit:

float ger( vec2 uv )
{

    float ste = 1.5;
    float amp = 0.7;
    float wav = 1.9;
    float spe = 1.5;

    uv.x *= 0.1;
    uv.y *= 0.3;
    uv.x += 1.0 * amp * cos( uv.x - iTime * spe + ( uv.x + uv.y ) );
    uv.y += ste * amp * sin( uv.y - iTime * spe * 0.5 + ( uv.x + uv.y ) );

    float c = 0.3 * sin( wav * ( amp ) * ( uv.x + uv.y ) + iTime );

    return c;

}

By the way you can get normals from a finite differences method by feeding your function to this function, here EPS or epsilon is a really small number like 1e-4

vec3 nor( vec3 p )
{

    vec2 e = vec2( EPS, 0.0 );

    return normalize( vec3( map( p + e.xyy ) - map( p - e.xyy ),
                            map( p + e.yxy ) - map( p - e.yxy ),
                            map( p + e.yyx ) - map( p - e.yyx )
                           )
                     );

}

Here is a link to a fragment shader that does the same you are looking after: https://www.shadertoy.com/view/MtVcW1

\$\endgroup\$
  • \$\begingroup\$ Thanks for replying, also managed to solve it using finite differences after finding this However I'm still looking for a solution to solve it using the method described in GPU Gems. Also is there a restriction on variable name size? Or why do you keep them so short as to leave readers guessing to their meaning. \$\endgroup\$ – Sindri Nov 29 '18 at 10:10
  • \$\begingroup\$ You welcome! It helps me type faster, and although I keep the variable names much more explicit on the host I prefer my shaders to be easily iterable. If I had intellisense for glsl I probably wouldn't but I don't, and even if I had it in my IDE, much of my code is prototyped in Shadertoy that doesn't has Intellisense. It also helps keeping everything more concise but is a matter of opinion. \$\endgroup\$ – Felipe Gutierrez Nov 29 '18 at 14:37
  • \$\begingroup\$ This seems helpful aclockworkberry.com/shader-derivative-functions \$\endgroup\$ – Felipe Gutierrez Nov 29 '18 at 17:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.