Inigo Quilez 的网站有一个 3D 射线表面相交 页面,用于带符号的距离函数,其中一个用于基本的 3D 盒子:
// axis aligned box centered at the origin, with size boxSize
vec2 boxIntersection( in vec3 ro, in vec3 rd, vec3 boxSize, out vec3 outNormal )
{
vec3 m = 1.0/rd; // can precompute if traversing a set of aligned boxes
vec3 n = m*ro; // can precompute if traversing a set of aligned boxes
vec3 k = abs(m)*boxSize;
vec3 t1 = -n - k;
vec3 t2 = -n + k;
float tN = max( max( t1.x, t1.y ), t1.z );
float tF = min( min( t2.x, t2.y ), t2.z );
if( tN>tF || tF<0.0) return vec2(-1.0); // no intersection
outNormal = (tN>0.0) ? step(vec3(tN),t1)) : // ro ouside the box
step(t2,vec3(tF))); // ro inside the box
outNormal *= -sign(rd);
return vec2( tN, tF );
}
除了计算交点外,它还计算交点处的表面法线。计算法线是我感兴趣的部分。我想了解它是如何做到这一点的,但该页面根本没有分解数学,而且我还没有找到任何其他来源(也不是理解这个过程限制了我对搜索内容的理解)。
有些部分在概念上是有意义的,比如计算射线的近/远交点。但是构成该计算的常数的计算对我来说是迷失的。例如,它似乎在做
ray_origin/ray_direction
,将一个位置除以一个(可能是归一化的)方向。这会产生什么样的结果?我原以为这是荒谬的。
我想了解它是如何工作的,而不是把它当作一个黑盒子。
我发现另一个版本,修改为只返回法线,但共享许多相同的计算。解释起来可能更容易,我只是希望我知道它是如何工作的。
vec3 boxNormal( in vec3 ro, in vec3 rd, vec3 boxSize)
{
vec3 m = 1.0/rd;
vec3 n = m*ro;
vec3 k = abs(m)*boxSize;
vec3 t1 = -n - k;
return -sign(rd)*step(t1.yzx,t1.xyz)*step(t1.zxy,t1.xyz);
}