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); 
}

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

相关推荐
康康的幸福生活13 小时前
webgl2 方法解析: shaderSource()
webgl
魂断蓝桥66621 小时前
如何基于three.js(webgl)引擎架构,实现3D医院、3D园区导航,3D科室路径导航
webgl·数字孪生·threejs·3d定位、三维室内定位、3d建筑·three.js路径规划、三维a*算法、javascript三维导航,·3d医院·3d导航·园区导航
康康的幸福生活11 天前
webgl2 方法解析: SCISSOR_TEST
javascript·webgl
魂断蓝桥66612 天前
如何基于three.js(webgl)引擎架构,实现3D机房园区,数据中心消防系统
webgl·数字孪生·three.js·物联网3d·3d机房·、3d工厂、3d工业园区、智慧制造、智慧工业、智慧工厂·bim管理系·3d消防·消防演习模拟
康康的幸福生活14 天前
webgl2 方法解析: colorMask()
webgl
Mapmost14 天前
全新升级!3DTiles加载速度Mapmost完胜Cesium
性能优化·webgl·cesium
康康的幸福生活14 天前
webgl2 方法解析: texImage2D()
webgl
点量云实时渲染-小芹16 天前
UE/Unity/Webgl云渲染推流网址,如何与外部网页嵌套和交互?
unity·webgl·webgl云渲染网页交互·点量云流
小old弟18 天前
🚀🚀🚀WebGL 加载 glTF 模型
前端·webgl
放逐者-保持本心,方可放逐21 天前
webgl(three.js 与 cesium 等实例应用)之浏览器渲染应用及内存释放的关联与应用
开发语言·javascript·webgl·顶点着色器·three.js 释放·cesium 释放·片元着色器