How to understand the simpler calcNormal function?

125 Views Asked by At

I'm reading shadertoy tutorial here: https://inspirnathan.com/posts/52-shadertoy-tutorial-part-6

there is a normal method to calculate the normal of the sphere:

vec3 calcNormal(vec3 p) {
  float e = 0.0005; // epsilon
  float r = 1.; // radius of sphere
  return normalize(vec3(
    sdSphere(vec3(p.x + e, p.y, p.z), r) - sdSphere(vec3(p.x - e, p.y, p.z), r),
    sdSphere(vec3(p.x, p.y + e, p.z), r) - sdSphere(vec3(p.x, p.y - e, p.z), r),
    sdSphere(vec3(p.x, p.y, p.z  + e), r) - sdSphere(vec3(p.x, p.y, p.z - e), r)
  ));
}

then, he got a simpler one:

vec3 calcNormal(vec3 p) {
    vec2 e = vec2(1.0, -1.0) * 0.0005; // epsilon
    float r = 1.; // radius of sphere
    return normalize(
      e.xyy * sdSphere(p + e.xyy, r) +
      e.yyx * sdSphere(p + e.yyx, r) +
      e.yxy * sdSphere(p + e.yxy, r) +
      e.xxx * sdSphere(p + e.xxx, r)
      );
}

and the sdSphere function:

// p is the point location, r is radius, sdSphere calculate the distance of the point in the world and the origin point(0,0) with the radius of r.
float sdSphere(vec3 p, float r)
{
  return length(p) - r; // p is the test point and r is the radius of the sphere
}

I can understand the normal method, but the simpler one, How could he do it, and it's correct?
I search for a while, can't get the answer, need some help, thanks.

1

There are 1 best solutions below

0
inspirnathan On

I am the author of this tutorial. Sorry for the late response to this question . The second calcNormal function is an alternative approach for creating a small gradient. The normal vector can be approximated by finding the distance between two close points on a sphere.

Both the first and second approaches for implementing the calcNormal function are not exactly equivalent. I have updated this on my blog to prevent future confusion. However, both functions get the job done for finding a small gradient because they both find two close points on the surface of the sphere or near the surface of the sphere.

I have created a small JavaScript program that emulates some behavior of GLSL code in case you wanted to compare the differences between each calcFunction implementation.

const p = new Vector3(1, 2, 3);

console.log('calcNormal1:', calcNormal1(p));
console.log('calcNormal2:', calcNormal2(p));

/* OUTPUT:
calcNormal1: Vector3 {
  x: 0.26726124089009934,
  y: 0.534522482802048,
  z: 0.8017837267599155
}
calcNormal2: Vector3 {
  x: 0.26721624351172774,
  y: 0.5345183943192493,
  z: 0.8018014500721813
}
*/

As we can see, the results are very close!