SDF Line相关公式推导
线段是SDF形状的基元之一,可以被用来建模一些形状,比如昆虫的腿,植物的根茎等。
下面这篇文章介绍一下Line公式的推导,首先记住我们要求的变量,点到形状最近的距离。
那么对于空间中的点\(P_1, P_2, P_3\),他们的分布有如下三种
其中\(P_1\)到线段的距离是\(|\vec{P_1Q}|\),\(P_2\)到线段的距离是\(|\vec{P_2A|}\),\(P_3\)到线段的距离是\(|\vec{P_3B}|。我们先看\) \(|\vec{P_1Q}|\)的求法,
其实本质是一个向量在另一个向量上的投影长度,此处借用云飞Ran的推导过程:
这里我们采用第二种方法,因此定义向量,\(\vec{BA},\vec{ BP_1}\),然后使用如下公式便可以求出\(\vec{BQ}\)的长度。
\[|\vec{BQ}| = {\vec{BP_1} \cdot \vec{BA}\over |\vec{BA}| } \]此时投影长度的比例可以定义为:
\[|\vec{BQ}| = {\vec{BP_1} \cdot \vec{BA}\over |\vec{BA}|^2 } \]熟悉这个求解投影长度的代码之后,在看一下另外两个点\(P_2, P_3\)我们会发现一个投影长度占BA的比例小于0,另一个大于1。
熟悉完上面的内容之后,我们就可以看一下SDF Line的实现了:
// Original SDF line segment function
float sdSegmentRegular( in vec2 p, in vec2 a, in vec2 b)
{
vec2 bp = b-p, ba = b-a; //求解向量
float h = clamp( dot(bp,ba)/dot(ba,ba), 0.0, 1.0 );
return length(bp - ba*h );
}
void SDFLine(in vec2 p, in vec2 a, in vec2 b, in float r, out float sdf) {
sdf = sdSegmentRegular(p, a, b, r);
}
float h = clamp( dot(bp,ba)/dot(ba,ba), 0.0, 1.0 );
这段代码巧妙的地方在于通过这行代码统一了三种分布的点,当p在a,b点的左侧时,h = 1,此时求得是向量\(\vec{bp}-\vec{ba}\)也就是\(\vec{ap}\)的长度,这里大致如图:
但是这段代码运行起来是看不到任何东西的,我们还需要减去一个 width,才能得到LineSegment;
// Original SDF line segment function
float sdSegmentRegular( in vec2 p, in vec2 a, in vec2 b, in float r )
{
vec2 bp = b-p, ba = b-a;
float h = clamp( dot(bp,ba)/dot(ba,ba), 0.0, 1.0 );
return length( bp - ba*h ) - r;
}
void SDFLine(in vec2 p, in vec2 a, in vec2 b, in float r, out float sdf) {
sdf = sdSegmentRegular(p, a, b, r);
}
具体的原理我们可以看一下这篇博客的圆角矩形
小节。此处就不再赘述啦。