如何從隱式方程計算光線法線的表面法線?


3

我想用射線行進來表示橢圓形。我有一個用隱式方程定義的橢圓。

例如,我們可以考慮橢圓體的方程,其給出方式為: $$ \ frac {x ^ 2} {a ^ 2} + \ frac {y ^ 2} {b ^ 2} + \ frac {z ^ 2} {c ^ 2} -1 = 0,$$ 其中 $ a,b,c $ 是恆定的。

鑑於此等式,我需要計算表面陰影的法線。在曲線和曲面的標準過程中,我將採用 $ f(x,y,z)= \ frac {x ^ 2} {a ^ 2} + \ frac {y ^ 2} {b ^ 2} + \ frac {z ^ 2} {c ^ 2} -1 $ 併計算 $ N(p)= \ frac {\ nabla f(p)} {| \ nabla f(p)|} $ 作為表面的法線。

在這裡,我必須檢查 $ 0 $ $ f $ 的常規值,並且 $ f $ 是可區分的。但是,第二項檢查不是自動化的。

  • 在實際系統中如何完成?
  • 您能解釋一下所使用的方法,理想情況下為我指出一些已存在的實現嗎?

請全面回答,而不僅要考慮上面給出的示例。

1

The usual way of doing this with raymarching is to define your surface as a signed-distance field and use finite differences to get the gradient of the distance function at the point you’re sampling. In other words, if you have a function map(p) that returns the signed distance value at a point p, the normal at p is given by:

float epsilon = 0.001; // arbitrary — should be smaller than any surface detail in your distance function, but not so small as to get lost in float precision
float centerDistance = map(p);
float xDistance = map(p + float3(epsilon, 0, 0));
float yDistance = map(p + float3(0, epsilon, 0));
float zDistance = map(p + float3(0, 0, epsilon));
float3 normal = (float3(xDistance, yDistance, zDistance) - centerDistance) / epsilon;

Note that you can improve the accuracy by taking an extra 3 samples on the other “side” of the center position and subtracting those from the first three, then dividing by 2× the epsilon value, but if your distance function is complicated then that can be a lot of additional work for not much visible benefit.

The sample function you provide isn’t a signed-distance function as-is, and I don’t know of a straightforward way to convert it into one, but there’s an article by Íñigo Quílez (kind of a legend in this field) here with various functions you can make use of, including one for an ellipsoid. There’s also a lot of reference material available on Shadertoy—search for “raymarching” and you’ll find plenty of examples.