【问题标题】:GLSL cube signed distance field implementation explanation?GLSL立方体有符号距离场实现解释?
【发布时间】:2016-05-21 11:07:54
【问题描述】:

我一直在查看并试图理解以下代码

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 分量都为负时,我们得到@987654330 @。但是我应该如何理解中间情况呢? d 的成分有混合符号的地方?

我一直在尝试绘制它,但无济于事。如果有人能用几何/数学术语向我解释这一点,我将不胜感激。谢谢。

【问题讨论】:

    标签: geometry glsl shader raytracing raymarching


    【解决方案1】:

    如果您想了解它的工作原理,最好执行以下步骤:

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

    2. 最好考虑它们的 2D 形状,因为三个维度对您来说可能很复杂。

    让我来解释一些形状:

    圈子

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

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

    The book of shaders - Chapter 7

    方形

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


    我描述了 2D 形状 在前面的部分现在让我来描述 3D 定义。

    球体

    球体是三维空间中的完美圆形几何对象,是一个完全圆形球的表面。

    就像一个圆,它在几何上是二维空间中的一个对象,一个球体在数学上被定义为与给定点的距离 r 相同但在三维空间中的点的集合。 Refrence - Wikipedia

    立方体

    在几何学中,立方体是由六个正方形面、小平面或边组成的三维立体对象,每个顶点有三个交汇点。 Refrence : Wikipedia


    使用距离函数建模

    现在是时候了解modeling with distance functions

    球体

    如上节所述。在下面的代码中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;
    }
    

    // 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));
    }
    

    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);
    }
    

    步骤 1

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

    第二步

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

    第三步

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

    第四步

    接下来我们有absolution函数。它用来移除额外的部分。


    赦免步骤

    赦免步骤1

    if(value.x < -1.){
       value.x = 1.; 
    }
    

    赦免步骤2

    if(value.y < -1.){
    value.y = 1.; 
    }
    

    赦免步骤 3

    if(value.z < -1.){
    value.z = 1.; 
    }
    


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

    CSG 建立在 3 个基本操作之上:交集 ()、并集 () 和差集 (-)。

    事实证明,当组合两个表示为 SDF 的表面时,这些操作都可以简洁地表达。

    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);
    }
    

    【讨论】:

    • 非常详尽的解释。你的前两张截图是什么软件?
    • @DanielA.Thompson ShaderForge 是统一的可视化脚本着色器工具ShaderToy
    • 虽然我很感激您花时间写这个答案,但我的问题是关于有符号距离字段的,您的解释针对的是无符号变量,这避免了解释如何计算负距离。
    • @FabCastel 我会更新我的答案,我也问过同样的问题here我希望它对你有用。
    【解决方案2】:

    我不久前想通了,并在此处的博客文章中广泛地写了这篇文章: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]
    

    好的,到目前为止没有什么特别的。当 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) 所做的;一个组件方式的最大操作。只要向量至少有一个正分量,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));
    }
    

    你有它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-12
      • 2021-08-06
      • 2021-09-11
      相关资源
      最近更新 更多