2D SDF推导(2): 线段

Shader Wave

首先给上文的Circle加上一个漂亮的波纹, 用于显示 <math xmlns="http://www.w3.org/1998/Math/MathML"> d i s t a n c e distance </math>distance

<math xmlns="http://www.w3.org/1998/Math/MathML"> f ( x ) = e x f(x) = e^x </math>f(x)=ex exp(x)可以将值贴近到1.0

<math xmlns="http://www.w3.org/1998/Math/MathML"> f ( x ) = s i n ( n × x ) f(x) = sin(n \times x) </math>f(x)=sin(n×x) sin产生了波

<math xmlns="http://www.w3.org/1998/Math/MathML"> f ( x ) = s i n ( n × x − m ∗ u T i m e ) f(x) = sin(n \times x - m * uTime) </math>f(x)=sin(n×x−m∗uTime)在sin中添加时间,产生了移动

Segment

假定有线段Segment(A, B), 空间中所有点到线段的最近距离分为三种情况。

  • P1 在AB中间,距离为点到线的垂直线
  • P2 在AB之外,距离B较近 距离为P2到B的距离
  • P3 在AB之外,距离A较近,距离为P3到A的距离

一种复合直觉的求法,便是把三个值\overrightarrow{PB}$$\overrightarrow{PA}$$\overrightarrow{PC}的长度都求出来。取最小的一个长度便可。\overrightarrow{PB}$$\overrightarrow{PA}长度非常好求。 <math xmlns="http://www.w3.org/1998/Math/MathML"> P C → \overrightarrow{PC} </math>PC 的长度可以通过\overrightarrow{PA}$$\overrightarrow{PB}的夹角 <math xmlns="http://www.w3.org/1998/Math/MathML"> θ \theta </math>θ,然后求 <math xmlns="http://www.w3.org/1998/Math/MathML"> s i n ( θ ) × ∣ ∣ P A → ∣ ∣ sin(\theta) \times ||\overrightarrow{PA}|| </math>sin(θ)×∣∣PA ∣∣。 <math xmlns="http://www.w3.org/1998/Math/MathML"> θ \theta </math>θ可以通过向量点积求得。 注意上图的 <math xmlns="http://www.w3.org/1998/Math/MathML"> h h hh </math>hh参数,表示 <math xmlns="http://www.w3.org/1998/Math/MathML"> P P </math>P在 <math xmlns="http://www.w3.org/1998/Math/MathML"> A B AB </math>AB上投影长度在 <math xmlns="http://www.w3.org/1998/Math/MathML"> A B AB </math>AB长度的占比,如果这个值 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( 0 , 1 ) (0, 1) </math>(0,1)区间,就说明我们需要求 <math xmlns="http://www.w3.org/1998/Math/MathML"> P C → \overrightarrow{PC} </math>PC 转化成为代码

glsl 复制代码
float sd_segment(vec2 pct, vec2 pa, vec2 pb) {
    vec2 v1 = pct - pa;
    vec2 v2 = pb - pa;
    vec2 v3 = pct - pb;
    
    
    float hh = dot(v1, v2) / length(v2) / length(v2);
    
    if (0. < hh && hh < 1.0) {
        float theta = acos(dot(v1 ,v2) /length(v1)/length(v2));
        float d1 = sin(theta) * length(v1);
        return d1;
    } else {
        return min(length(v1), length(v3)) ;
    }
}

这里可以对代码进行优化, <math xmlns="http://www.w3.org/1998/Math/MathML"> P C → \overrightarrow{PC} </math>PC 的长度其实 <math xmlns="http://www.w3.org/1998/Math/MathML"> P A → − A C → \overrightarrow{PA} - \overrightarrow{AC} </math>PA −AC 。 <math xmlns="http://www.w3.org/1998/Math/MathML"> A C → \overrightarrow{AC} </math>AC 可以有 <math xmlns="http://www.w3.org/1998/Math/MathML"> h h hh </math>hh决定 ,我们已经知道 <math xmlns="http://www.w3.org/1998/Math/MathML"> h h hh </math>hh的大小。并且当 <math xmlns="http://www.w3.org/1998/Math/MathML"> h h < 0 hh<0 </math>hh<0,距离就是 <math xmlns="http://www.w3.org/1998/Math/MathML"> L e n g t h ( P A → ) Length(\overrightarrow{PA}) </math>Length(PA ),当 <math xmlns="http://www.w3.org/1998/Math/MathML"> h h > 0 hh>0 </math>hh>0,距离就是 <math xmlns="http://www.w3.org/1998/Math/MathML"> L e n g t h ( P B → ) Length(\overrightarrow{PB}) </math>Length(PB ). 于是代码可以优化为

glsl 复制代码
float sd_segment2(vec2 pct, vec2 pa, vec2 pb) {
    vec2 v1 = pct - pa;
    vec2 v2 = pb - pa;
    vec2 v3 = pct - pb;
    
    
    float hh = dot(v1, v2) / length(v2) / length(v2);

    if (hh < 0.0) {      
      return length(v1 - 0. * v2);
    } else if (0. < hh && hh < 1.0) {
        return length(v1 - hh * v2);
    } else {
        // v3 = v1 - v2 = pct -pb;
        return length(v1 - v2);
    }
}

通过上面的优化可以发现,三个判断条件的相似性。 通过引入clamp函数去掉if判断优化性能,通过dot(v2, v2)减少一次length的计算。 最后得到IQ大神最终结果

glsl 复制代码
float sd_segment3(vec2 pct, vec2 pa, vec2 pb) {
    vec2 v1 = pct - pa;
    vec2 v2 = pb - pa;
    float h = clamp(dot(v1,v2)/dot(v2,v2), 0.0, 1.0);
    return length(v1-h*v2); 
}

继续使用波纹,可以达到下面的效果

相关推荐
_oP_i12 小时前
Unity Addressables 系统处理 WebGL 打包本地资源的一种高效方式
unity·游戏引擎·webgl
新中地GIS开发老师1 天前
WebGIS和WebGL的基本概念介绍和差异对比
学习·arcgis·webgl
_oP_i2 天前
Unity 中使用 WebGL 构建并运行时使用的图片必须使用web服务器上的
前端·unity·webgl
flying robot5 天前
Three.js简化 WebGL 的使用
webgl
小彭努力中5 天前
114. 精灵模型标注场景(贴图)
前端·3d·webgl·贴图
小彭努力中5 天前
109. 工厂光源(环境贴图和环境光)
前端·深度学习·3d·webgl·贴图
小彭努力中6 天前
112. gui辅助调节光源阴影
前端·深度学习·3d·webgl
refineiks8 天前
three.js绘制宽度大于1的线,并动态新增顶点
3d·图形渲染·webgl
小彭努力中8 天前
102. 管道漫游案例
前端·3d·webgl
小彭努力中8 天前
107. 阴影范围.shadow.camera
前端·深度学习·3d·webgl