2D SDF推导(2): 线段

Shader Wave

首先给上文的Circle加上一个漂亮的波纹, 用于显示 d i s t a n c e distance distance

f ( x ) = e x f(x) = e^x f(x)=ex exp(x)可以将值贴近到1.0

f ( x ) = s i n ( n × x ) f(x) = sin(n \times x) f(x)=sin(n×x) sin产生了波

f ( x ) = s i n ( n × x − m ∗ u T i m e ) f(x) = sin(n \times x - m * uTime) 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}长度非常好求。 P C → \\overrightarrow{PC} PC 的长度可以通过\\overrightarrow{PA}\overrightarrow{PB}的夹角 θ \theta θ,然后求 s i n ( θ ) × ∣ ∣ P A → ∣ ∣ sin(\theta) \times ||\overrightarrow{PA}|| sin(θ)×∣∣PA ∣∣。 θ \theta θ可以通过向量点积求得。 注意上图的 h h hh hh参数,表示 P P P在 A B AB AB上投影长度在 A B AB AB长度的占比,如果这个值 ( 0 , 1 ) (0, 1) (0,1)区间,就说明我们需要求 P C → \overrightarrow{PC} 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)) ;
    }
}

这里可以对代码进行优化, P C → \overrightarrow{PC} PC 的长度其实 P A → − A C → \overrightarrow{PA} - \overrightarrow{AC} PA −AC 。 A C → \overrightarrow{AC} AC 可以有 h h hh hh决定 ,我们已经知道 h h hh hh的大小。并且当 h h < 0 hh<0 hh<0,距离就是 L e n g t h ( P A → ) Length(\overrightarrow{PA}) Length(PA ),当 h h > 0 hh>0 hh>0,距离就是 L e n g t h ( P B → ) Length(\overrightarrow{PB}) 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); 
}

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

相关推荐
总要学点什么6 天前
第92篇:三维特效-圆(逐渐消逝)——从圆心开始,让一个洞把整片光啃到只剩外圈
gis·webgl·cesium·空间分析·1024程序员节
总要学点什么6 天前
第89篇:三维特效-螺旋圆效果——让螺旋臂卷起一圈圈外扩的涟漪
开发语言·javascript·ecmascript·webgl·cesium·空间分析·1024程序员节
GoldenSpider.AI7 天前
一张户型图做成浏览器 3D 漫游:house.json 统一模型、1.2 m 剖切图纸与分阶段验收
ai编程·webgl·three.js·3d漫游·claude code
Jmyd01239 天前
WebGL 三维党史馆怎么扛住 720 度全景与移动端弱网?
webgl·三维数字化
limit007511 天前
三维 WebGIS 工具选型:加载、样式、分析、交付,一套界面能做完什么
android·前端·web3·安卓·webgl
总要学点什么12 天前
第79篇:数据可视化-模拟迁徙效果——把三十条航线画成会飞的弧
3d·信息可视化·数据挖掘·数据分析·gis·webgl·cesium
limit007513 天前
三维模型悬空与切片偏移的排查顺序
低代码·3d·arcgis·web3·安卓·webgl
Sakura_j_h_13 天前
Three.js 单文件离线机械臂工作台:爆炸图、透明外壳与剖面
前端·机械臂·webgl·three.js·1024程序员节·esbuild
总要学点什么20 天前
第65篇:地图卷帘控件——点「关闭卷帘」,消失的却是右边那半张地图
信息可视化·gis·webgl·cesium·空间分析
总要学点什么21 天前
第63篇:3DTiles-大雁塔模型——190 块瓦片不是八叉树,抬高的却是球心那条法线
3d·数据挖掘·数据分析·gis·webgl