GLSL立方体签名距离字段实现说明?

Fab*_*tel 6 shader geometry raytracing glsl raymarching

我一直在寻找并试图理解下面的代码

float sdBox( vec3 p, vec3 b )
{
  vec3 d = abs(p) - b;
  return min(max(d.x,max(d.y,d.z)),0.0) +
         length(max(d,0.0));
}
Run Code Online (Sandbox Code Playgroud)

我理解length(d)处理SDF的情况,其中点离开'拐角'(即所有组件d都是正的)并且max(d.x, d.y, d.z)在所有其他情况下给我们适当的距离.我不明白的是,如果不使用if语句检查d组件的符号,这两者是如何组合在一起的.

当所有d组件都是正数时,返回表达式可以减少到length(d)因为min/max评估的方式- 当所有d组件都是负数时,我们得到max(d.x, d.y, d.z).但我怎么理解中间案件呢?那些组成部分d有混合迹象的那些?

我一直试图把它绘制成无济于事.如果有人能用几何/数学术语向我解释,我真的很感激.谢谢.

Sey*_*ali 6

如果您想知道它的工作原理最好执行以下步骤:

首先,你应该知道形状的定义

2.考虑它们的2D形状总是更好,因为三维可能对你来说很复杂.

所以让我来解释一些形状:

圈

圆形是一种简单的闭合形状.它是一个平面中距离给定点(中心)给定距离的所有点的集合.

您可以使用distance(),length()sqrt()以计算到广告牌的中心的距离.

着色书 - 第7章

广场

广场

在几何中,正方形是规则的四边形,这意味着它具有四个相等的边和四个相等的角(90度角).


我描述2D形状现在让我来描述3D定义.

球

球体是三维空间中完美圆形的几何物体,是完全圆球的表面.

像几何上是二维空间中的对象的圆一样,球体在数学上被定义为与给定点在相同距离r处但在三维空间中的点集. 参考 - 维基百科

立方体

立方体

在几何中,立方体是由六个正方形面,小平面或侧面限定的三维立体对象,在每个顶点处有三个会合. 参考:维基百科


使用距离函数建模

现在是时候了解距离函数的建模

如上所述.在下面的代码中,length()用于计算到广告牌中心的距离,您可以通过s参数缩放此形状.

//Sphere - signed - exact
/// <param name="p">Position.</param>
/// <param name="s">Scale.</param>
float sdSphere( vec3 p, float s )
{
  return length(p)-s;
}
Run Code Online (Sandbox Code Playgroud)

// Box - unsigned - exact
/// <param name="p">Position.</param>
/// <param name="b">Bound(Scale).</param> 
float udBox( vec3 p, vec3 b )
{
  return length(max(abs(p)-b,0.0));
}
Run Code Online (Sandbox Code Playgroud)

length()像前面的例子一样用 接下来我们max(x,0)称之为正面和负面部分 Positive_and_negative_parts

这意味着下面的代码是等价的:

float udBox( vec3 p, vec3 b )
{

   vec3 value = abs(p)-b;


   if(value.x<0.){
   value.x = 0.;  
   }

  if(value.y<0.){
  value.y = 0.;  
  }

  if(value.z<0.){
  value.z = 0.;  
  }  

  return length(value);
}
Run Code Online (Sandbox Code Playgroud)

步骤1

   if(value.x<0.){
   value.x = 0.;  
   }
Run Code Online (Sandbox Code Playgroud)

步骤1

第2步

  if(value.y<0.){
  value.y = 0.;  
  }
Run Code Online (Sandbox Code Playgroud)

第2步

第3步

  if(value.z<0.){
  value.z = 0.;  
  }  
Run Code Online (Sandbox Code Playgroud)

第三步:

第4步

接下来我们有绝对功能.它用于删除其他部分.

赦免

第4步


赦免步骤

第三步:

赦免步骤1

if(value.x < -1.){
   value.x = 1.; 
}
Run Code Online (Sandbox Code Playgroud)

ABS1

赦免步骤2

if(value.y < -1.){
value.y = 1.; 
}
Run Code Online (Sandbox Code Playgroud)

ABS2

赦免步骤3

if(value.z < -1.){
value.z = 1.; 
}
Run Code Online (Sandbox Code Playgroud)

第4步


您还可以使用构造实体几何体来制作任何形状.

构造性实体几何

CSG建立在3个基本操作上:intersection(?),union(?)和difference(- ).

事实证明,当组合表示为SDF的两个表面时,这些操作都是简明扼要的.

intersectSDF

float intersectSDF(float distA, float distB) {
    return max(distA, distB);
}
Run Code Online (Sandbox Code Playgroud)

unionSDF

float unionSDF(float distA, float distB) {
    return min(distA, distB);
}
Run Code Online (Sandbox Code Playgroud)

differenceSDF

float differenceSDF(float distA, float distB) {
    return max(distA, -distB);
}
Run Code Online (Sandbox Code Playgroud)


Fab*_*tel 5

我不久前就发现了这一点,并在博客文章中对此进行了广泛的讨论:http ://fabricecastel.github.io/blog/2016-02-11/main.html

这是摘录(请参阅完整帖子以获取完整解释):

考虑四个点,A、B、C 和 D。让我们粗略地减少距离函数,尝试去掉最小/最大函数,以便理解它们的效果(因为这就是该函数令人费解的地方)。下面的符号有点草率,我使用方括号来表示二维向量。

// 2D version of the function
d(p) = min(max(p.x, p.y), 0)
       + length(max(p, 0))

---

d(A) = min(max(-1, -1), 0)
       + length(max([-1, -1], 0))

d(A) = -1 + length[0, 0]

---

d(B) = min(max(1, 1), 0)
       + length(max([1, 1], 0))

d(B) = 0 + length[1, 1]
Run Code Online (Sandbox Code Playgroud)

好吧,到目前为止没有什么特别的。当 A 在正方形内部时,我们基本上得到基于平面/线的第一个距离函数,而当 B 位于第一个距离函数不准确的区域时,它会被清零,我们得到第二个距离函数(长度)。诀窍在于另外两种情况 C 和 D。让我们来解决它们。

d(C) = min(max(-1, 1), 0)
      + length(max([-1, 1], 0))

d(C) = 0 + length[0, 1]

---

d(D) = min(max(1, -1), 0)
       + length(max([-1, 1], 0))

d(D) = 0 + length[1, 0]
Run Code Online (Sandbox Code Playgroud)

如果您回头看一下上图,您会注意到 C' 和 D'。这些点的坐标分别为 [0,1] 和 [1,0]。此方法利用两个距离场在轴上相交的事实 - D 和 D' 距正方形的距离相同。

如果我们将向量的所有负分量归零并获取其长度,我们将得到该点和正方形之间的正确距离(仅适用于正方形之外的点)。这就是 max(d,0.0) 的作用;逐分量最大操作。只要向量至少有一个正分量,min(max(dx,dy),0.0) 就会解析为 0,只剩下方程的第二部分。如果该点位于正方形内,我们希望返回方程的第一部分(因为它代表我们的第一个距离函数)。如果向量的所有分量都是负数,那么很容易看出我们的条件得到满足。

一旦您认真思考,这种理解应该会无缝地转换回 3D。您可能需要也可能不需要手工绘制一些图表才能真正“理解”它 - 我知道我做到了,并且如果您对我的解释不满意,我会鼓励您这样做。

将这个实现应用到我们自己的代码中,我们得到:

float distanceToNearestSurface(vec3 p){
  float s = 1.0;
  vec3 d = abs(p) - vec3(s);
  return min(max(d.x, max(d.y,d.z)), 0.0)
      + length(max(d,0.0));
}
Run Code Online (Sandbox Code Playgroud)

现在你就得到了它。