5

I want to render a planet like sphere. The general idea is as follows:

  • Generate a bunch of unit length vertices which make up a sphere.
  • While rendering the sphere the shader evaluates the 3D simplex noise at the point on the unit sphere.
  • The result is used as the "height" to displace the current vertex along its direction.

Up to here everything is working like it should.

Now I want to add lighting and therefore need normals to the surface.

While implementing the lighting related parts I quickly added a method to estimate the normals of the terrain using partial derivatives in the fragment shader like this:

vec3 X = dFdx(ins.position);
vec3 Y = dFdy(ins.position);
vec3 normal = normalize(cross(X,Y));

where ins.position is the interpolated world position.

While this works it does not look very good, because it essentially results in per-face normals.

Bad normal approximation

Now to the actual questions:

  • Calculating per-vertex normals would result in smooth normals, unlike in the picture, correct?
  • One of the advantages of Simplex Noise over Perlin Noise is that is has a "well-defined and continuous gradient everywhere that can be computed quite cheaply" (to cite the excellent Simplex Noise demystified) and with the gradient one should be able to compute the normal, correct?

If the seconds question is a "yes" I have two problems:

  • The simplex noise algorithm was taken from a popular source, which sadly does not include the gradient calculation. I will post my attempt of adding it below, but I have no idea if it is correct.
  • Even if I had the gradient I am stuck on deriving the normal from there.

Any help is greatly appreciated!

My shot at the gradient implementation (replaced the last few lines of snoise):

float snoise(vec3 v, out vec3 grad)
{
    ......

    // Mix final noise value
    vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
    vec4 m2 = m * m;
    vec4 m4 = m2 * m2;

    vec4 pdotx = vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3));

    vec4 temp = m2 * m * pdotx;
    grad = -8.0 * (temp.x * x0 + temp.y * x1 + temp.z * x2 + temp.w * x3);
    grad += m4.x * p0 + m4.y * p1 + m4.z * p2 + m4.w * p3;
    grad *= 42.0;

    return 42.0 * dot(m4, pdotx);
}

UPDATE:

The part about calculating the surface normal from the gradient has been answered here: Surface normal to point on displaced sphere.

The remaining question now is how to implement the gradient calculation into the GLSL version of the 3D Simplex Noise, because my implementation seems to have issues.

UPDATE 2:

The gradient calculation seems to be almost right, just the scaling seems to be off.
Instead of multiplying by 42, dividing by 5 gives pretty good results, but that was found out by trial and error. A proper scaling factor would be nice.

Community
  • 1
  • 1
Gigo
  • 3,188
  • 3
  • 29
  • 40
  • To answer your first question: yes and no. Per-vertex normals can be smooth if you have information about adjacent triangles, or they can be flat if you compute them using a single triangle. Using geometry shaders and a triangle strip with adjacency you can get up to 4 shared triangles per-vertex, which ***might*** (considering that is *not* actually the number of triangles that each vertex belongs to) be sufficient to compute an approximate smooth per-vertex normal. You will have to structure your input vertex indices in a very particular order to accomplish this and it requires GL 3.2+. – Andon M. Coleman Dec 17 '14 at 01:02
  • If I understood that correctly, the resulting normals will be smooth if the normal for a given vertex is the same, no matter what face is currently processed. Assuming the calculation works out the way I described that will be the case, because the normals will only depend on the information of the single vertex they belong to. – Gigo Dec 17 '14 at 01:58
  • Well, the (smooth) per-vertex normal depends on the other 2 vertices in each triangle that shares that vertex. I look at your mesh and notice some vertices with as many as 10 triangles sharing the same vertex. If you wanted to smooth those per-vertex normals correctly, you would have to compute the face normal for all 10 of those triangles. That is why it does not seem practical. – Andon M. Coleman Dec 17 '14 at 02:52
  • You could avoid the issue by adjusting the spherical normal using GPU-calculated gradients in the fragment shader, via `dFdx` and `dYdx`. – Justin Dec 17 '14 at 12:08
  • @Justin you mean like the way I described in the question? ;) – Gigo Dec 17 '14 at 16:25
  • You could calculate the normal in the vertex shader by evaluating the simplex noise at two more positions with small offsets from the vertex. – GuyRT Dec 17 '14 at 16:38
  • @GuyRT Yes I could, but evaluating the noise is expensive in comparison to the gradient and the latter would give perfect normals, not an approximation. – Gigo Dec 17 '14 at 16:41
  • Inigo Quilez discusses getting the derivatives from perlin noise here: http://iquilezles.org/www/articles/morenoise/morenoise.htm - perhaps you could adapt his ideas – GuyRT Dec 17 '14 at 16:53
  • @Gigo whoops. I don't suppose you want to evaluate the noise function per-fragment. – Justin Dec 17 '14 at 17:09

1 Answers1

3

Alright, turns out my problem was almost purely math related.

If anyone is interested:
The GLSL implementation of the gradient calculation posted in the question is totally correct.
Calculating the normals directly from the gradient is possible as described here.

And the result looks exactly like I wanted it to be, I am happy ;)

Smooth per-vertex normals

Community
  • 1
  • 1
Gigo
  • 3,188
  • 3
  • 29
  • 40