Geometry GLSL多维数据集有符号距离字段实现说明?

Geometry GLSL多维数据集有符号距离字段实现说明?,geometry,glsl,shader,raytracing,raymarching,Geometry,Glsl,Shader,Raytracing,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)); } 我理解length(d)处理点偏离“拐角”的SDF情况(即d的所有分量均为正值),并且max(d.x,d.y,d.z)在所有其他情况下为我们提供了适当的距离。我不明白的是,如果不使用if语句来检查d组件的符号,

我一直在研究并试图理解以下代码

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));
}
我理解
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
的组件有混合符号的那些


我一直试图把它画出来,但没有用。如果有人能用几何/数学术语向我解释这一点,我将不胜感激。谢谢。

我不久前就发现了这一点,并在这里的一篇博文中对此进行了广泛的讨论:

这里有一段摘录(完整的解释见全文):

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

// 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]
好的,到目前为止没有什么特别的。当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]
如果你回顾上面的图表,你会注意到C'和D'。这些点分别具有坐标[0,1]和[1,0]。此方法使用两个距离场在轴上相交的事实,即D和D'与正方形的距离相同

如果我们将向量的所有负分量归零并计算其长度,我们将得到点与正方形之间的适当距离(仅适用于正方形以外的点)。这就是max(d,0.0)所做的;一种基于组件的max操作。只要向量至少有一个正分量,min(max(d.x,d.y),0.0)将解析为0,只剩下方程的第二部分。如果点在正方形内,我们希望返回方程的第一部分(因为它代表我们的第一个距离函数)。如果向量的所有分量都是负的,很容易看出我们的条件会得到满足

一旦你把头绕在3D上,这种理解就会无缝地转换回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));
}

这就是你想要的。

如果你想知道它是如何工作的,最好执行以下步骤:

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

P>2。考虑2D形状总是更好的,因为三个维度对你来说可能很复杂。

让我来解释一些形状:

圆圈

圆是一种简单的闭合形状。它是一个平面上所有点的集合,这些点与一个给定点(即中心)之间的距离是给定的

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

广场

在几何学中,正方形是规则四边形,这意味着它有四条等边和四个等角(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;
}
第一步



解释得很透彻。前两张截图中的是什么软件?@DanielA.Thompson是unity的可视化脚本着色器工具,虽然我很感激你花了这么多时间来写这个答案,但我的问题是关于有符号距离字段的,你的解释针对的是无符号变量,这避免了解释负距离是如何计算的。@FabCastel我会更新我的答案,我也问了同样的问题,希望对你们有用。
// 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));
}
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);
}
   if(value.x<0.){
   value.x = 0.;  
   }
  if(value.y<0.){
  value.y = 0.;  
  }
  if(value.z<0.){
  value.z = 0.;  
  }  
if(value.x < -1.){
   value.x = 1.; 
}
if(value.y < -1.){
value.y = 1.; 
}
if(value.z < -1.){
value.z = 1.; 
}
float intersectSDF(float distA, float distB) {
    return max(distA, distB);
}
float unionSDF(float distA, float distB) {
    return min(distA, distB);
}
float differenceSDF(float distA, float distB) {
    return max(distA, -distB);
}