下面这是SDF常用的三个函数:
// intersect(求交)
vec2 mult(vec2 tA, vec2 tB) {
if(tA.x > tB.x) return tA;
return tB;
}
// union(合并)
vec2 add(vec2 tA, vec2 tB) {
if(tA.x < tB.x) return tA;
return tB;
}
// difference(差异)
vec2 subtract(vec2 tA, vec2 tB) {
tB.x = -tB.x;
if(tA.x > tB.x) return tA;
return tB;
}
这三个函数用于计算一个空间点位于一个一个空间分布函数所描述的空间的"里面"还是外面,如果是"里面",那这个点所对应的空间分布函数所描述的空间的对应位置就可见。
那么,他们的原理是什么呢?
先从2D平面说起。一个2D平面,默认是无限大的,但是一条这个平面上的直线可以将它分为两个部分,也就是两个空间。如果直线定义了正方向,那么这两部分各自就有了正负空间这个称呼。
这个2D空间中的一个圆或者一个三角形,是由曲线围起来的一片区域:一个封闭空间,这时候,我们就可以断定一个2D平面上的点在这个封闭空间里面还是外面。
这里可以规定在这个区域里为负值,在其外面就为正值。
接着看3D空间。在3D空间里,原本3D空间是无限延伸的。而一个3D空间中的平面(当然也有曲面,这里先只看平面),将3D空间划分为两部分,我们都知道3D空间中的平面需要一个法线n(nx,ny,nz)来描述
可以认为法线的方向上的空间为正空间,反之为负空间。例如一个球体或者长方体,球体是个封闭空间是个曲面,将3D空间分为两部分,球体里面和外面,按照上述规定,里面为负值外面为正值。
同理,长方体也一样,展开来讲只要空间分布函数(你可以任意构造这个函数)能描述都可以。
因为3D空间中的封闭空间只能由面来决定,所以空间分布函数的实现只能是面或者曲面的某种集合
现在来看上述三个函数
A.intersect(求交): vec2 mult(vec2 tA, vec2 tB),和乘法类似,因此可以这么理解:一个空间点,必须同时属于两个封闭空间,才能视为可见
B.add(求和): vec2 add(vec2 tA, vec2 tB), 和加法类似,因此可以这么理解:一个空间点,只要属于两个封闭空间中的任何一个,就能视为可见
C.subtract(求差): vec2 subtract(vec2 tA, vec2 tB), 和减法类似,因此可以这么理解:一个空间点,要属于封闭空间中A同时有不属于封闭空间B(也就是封闭空间B的外面),就能视为可见
有了这三个函数,就能通过这三个函数的组合运算实现各种几何体(封闭空间)
请看下面示例:
球体:
vec2 sphereSDF(in vec3 samplePoint, in vec3 pos, float radius,float color) {
return vec2(length(samplePoint - pos) - radius, color);
}
长方体:
vec2 cubeSDF(vec3 p, vec3 pos, float color, in vec3 size) {
vec3 d = abs(p - pos) - size;
float insideDistance = min(max(d.x, max(d.y, d.z)), 0.0);
float outsideDistance = length(max(d, 0.0));
return vec2(insideDistance + outsideDistance, color);
}
球体和长方体求差运算:
vec2 txv = sphereSDF(samplePoint, vec3(0.0,0.0,0.0), 0.5, 888888.0);
vec2 txv1 = cubeSDF(samplePoint, vec3(0.0, -0.2, 0.0), 880088.0,vec3(0.4, 0.4, 0.4));
txv = subtract(txv1, txv);
效果截图(我前面几个文章中的demo有完整的源码js文件):