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有混合迹象的那些?
我一直试图把它绘制成无济于事.如果有人能用几何/数学术语向我解释,我真的很感激.谢谢.
如果您想知道它的工作原理最好执行以下步骤:
首先,你应该知道形状的定义
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;
}
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)称之为正面和负面部分

这意味着下面的代码是等价的:
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)
if(value.x<0.){
value.x = 0.;
}
Run Code Online (Sandbox Code Playgroud)
if(value.y<0.){
value.y = 0.;
}
Run Code Online (Sandbox Code Playgroud)
if(value.z<0.){
value.z = 0.;
}
Run Code Online (Sandbox Code Playgroud)
接下来我们有绝对功能.它用于删除其他部分.
if(value.x < -1.){
value.x = 1.;
}
Run Code Online (Sandbox Code Playgroud)
if(value.y < -1.){
value.y = 1.;
}
Run Code Online (Sandbox Code Playgroud)
if(value.z < -1.){
value.z = 1.;
}
Run Code Online (Sandbox Code Playgroud)
您还可以使用构造实体几何体来制作任何形状.
CSG建立在3个基本操作上:intersection(?),union(?)和difference(- ).
事实证明,当组合表示为SDF的两个表面时,这些操作都是简明扼要的.
float intersectSDF(float distA, float distB) {
return max(distA, distB);
}
Run Code Online (Sandbox Code Playgroud)
float unionSDF(float distA, float distB) {
return min(distA, distB);
}
Run Code Online (Sandbox Code Playgroud)
float differenceSDF(float distA, float distB) {
return max(distA, -distB);
}
Run Code Online (Sandbox Code Playgroud)
我不久前就发现了这一点,并在博客文章中对此进行了广泛的讨论: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)
现在你就得到了它。