0

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.

tankin
  • 1
  • 1
  • if `p` is surface point in Cartesian of some sphere with center `p0` and radius `r` then the normal is simply `n = (p-p0)/r` your codes are too complicated what is `p` and what `sdSphere` does? – Spektre Sep 14 '22 at 06:11
  • @Spektre yeah, you're right with n = (p-p0)/r, but it isn't general case for all surfaces, p is the point on the surface, the Cartesian coordinate in 3d world, and sdSphere is the sdf (sign distance field) sphere. Thank you for reply! – tankin Sep 19 '22 at 12:33
  • what are the `sdSphere` parameters? is it `center, radius`? what is then the signed distance refer to? current fragment position perhaps? or some ray? does it return scalar or vector? as I would expect scalar however you translated it as `sign distance field` instead of `sign distance function` which implies vector. the obvious way of doing normal to surface is to take tangent and its bitangent and do cross product on them however with SDF is this tricky as you got scalar instead of real position instead – Spektre Sep 21 '22 at 07:09
  • If I see it right the terms like `e.xyy * sdSphere(p + e.xyy, r)` will return position of surface (with some epsilon movement in certain axis) relative to the same point (probably shape center assuming sphere like shape) – Spektre Sep 21 '22 at 07:18
  • @Spektre, thank you reply twice, sorry for late reply for your question, the output of the sdSphere is a scalar, it's just a distance value, if the point in the radius of the circle, it is negative, and 0 on the circle surface, and positive if it is outside of the circle. e.xyy * sdSphere(p + e.xyy, r) will not return the position of the surface, it's a vector with the sdf scalar value, and e.xyy indicate the direction of the vector, it's the most I want to know why it could be write like that. – tankin Sep 26 '22 at 07:44
  • I know what SDF is but you still did not clear up what the distance relates to which is root of your problem because you pass only object propertires into SDF and not the stuff to which you want to know the distance... that is most likely passed by global variables which you did not share so I have no idea if its a point or ray or something else ... – Spektre Sep 26 '22 at 12:03
  • @Spektre I don't get your point, and if you're really interest in this question, could you have a visit of the link at the top line of the question, I'm sorry for my poor expression, maybe the author's article could give you a nice one. – tankin Sep 29 '22 at 11:39
  • my point is if the SDF is against ray then the `e.xyy * sdSphere(p + e.xyy, r)` gets you relative position in some local reference frame (nearby along the ray) and you can use 3 of them to construct normal However if the SDF is against point then the return value is not a position but just a bunch of distances instead and in such case i have no idea how it works – Spektre Sep 29 '22 at 13:54

1 Answers1

0

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!

inspirnathan
  • 351
  • 3
  • 10