2D SDF运算1: 变换

旋转移动

坐标系中形状的变换可以由坐标系的逆变换完成. 但是有一个问题没搞清楚,旋转+平移 会出现一个无穷值,不知道是inverse内置函数的问题,还是逻辑上的问题, 体现在图像上是内圆不见了。 这里回顾一下平移,旋转矩阵(线性变换)

glsl 复制代码
  float dx = sin(iTime/3.0);
  float dy = cos(iTime/3.0);
  mat3 translationMatrix = mat3(
      1, 0, dx,
      0, 1, dy,
      0, 0, 0
  );
  
  float theta = PI * (abs(sin(iTime/3.0)) + 0.1);
  mat3 rotationMatrix = mat3(
      cos(theta), -sin(theta), 0,
      sin(theta), cos(theta), 0,
      0, 0, 0
  );

缩放

缩放是通过压缩/扩张空间来做的,所以在最后的distance需要反向扩张/压缩回来。均匀缩放的计算非常简单,通用的非均匀缩放且获得正确的SDF是不可能的。因为维度的信息丢失了。这里直接贴IQ的代码

glsl 复制代码
float opScale( in vec3 p, in float s, in sdf3d primitive )
{
    return primitive(p/s)*s;
}

圆角 Rounded

rounded,就是将角外的圆弧往里面收。要做的操作便是将坐标系沿着角的方向(类似于3D面的法线方向)往外拉扯,在最后取得距离在减去相应拉出去的量。 对应到代码中就是会有一个加法操作,然后有一个减法操作。

Star

在star的sdf中,往外拉的极坐标的半径

对比之前通用角SDF的函数,变化有

glsl 复制代码
float roundR = 0.3;

//+ polar_P = vec2(length(P) + roundR, theta2);
//- polar_P = vec2(length(P)         , theta2);

//+ return sign(cross2(v2, v1)) * length(v1-h*v2) - roundR; 
//- return sign(cross2(v2, v1)) * length(v1-h*v2)         ; 

Polygon

代码是通过不断的调试和观察图像获得,我现在没办法很明确的讲清楚,只能凭感觉能写出来,基本思路也是沿着极坐标下半径进行放大缩小。读者在完成前面的SDF推导之后,也能感觉出来

下列代码中第44行为什么是要减去一个 <math xmlns="http://www.w3.org/1998/Math/MathML"> r o u n d R ∗ a b s ( c o s ( d e l t a / 2. ) ) roundR * abs(cos(delta/2.)) </math>roundR∗abs(cos(delta/2.)) 我猜测是为了让放大缩小弄到边上,这个值也是实验出来的...

glsl 复制代码
float rounded_polygon(vec2 P, float radius, int sides) {
    // get polar angle
    float angle = atan(P.y, P.x);
    // make angle to range [0, 2*PI]
    angle = P.y > 0. ? angle: angle + PI * 2.;

    // get each piece angle
    float delta = 2. * PI / float(sides);
    // how many pieces?
    float areaNumber = floor(angle / delta);
    
    // start angle of current piece
    float theta1 = delta * areaNumber;
    // end angle of current piece
    float theta2 = delta * (areaNumber + 1.0);
   
    float roundR = -abs(sin(iTime / 2.)) * 1.5  ;
    float radius2 = -radius - roundR;
    radius2 = -abs(radius2);

    // start point on current piece
    vec2 POINT_A       = vec2(radius2  * cos(theta1), radius2 * sin(theta1) );
    // end point on current piece
    vec2 POINT_A_Prime = vec2(radius2 * cos(theta2), radius2 * sin(theta2) );
    // the middle of startPoint and endPoint
    vec2 POINT_D       = (POINT_A + POINT_A_Prime)/2.0;
    // corrdinate center
    vec2 POINT_O       = vec2(0.0); 
    // start axis of current piece
    vec2 iAxis1   =  vec2(-POINT_O+POINT_A);
    for (int i=0; i<2; i++) {
        vec2 PP = P;
        if (i==1) {     // symmetrical for area2
            PP = symmetrical_point(P, POINT_D);
        }      
        vec2 vector1  = vec2(PP - POINT_A);
        float a1 = vector_angle(iAxis1, vector1);
        if (a1 <  (delta / 2.0)) {
            return length(vector1)-roundR * abs(cos(delta/2.)) ;
        }
    }
    
    // area 3 
    float theta = mod(angle, delta) - delta / 2.0;
    float l1 =  (length(P) + roundR) * cos(theta) - length(POINT_D) - roundR;
    return l1;
}

镂空 Annular

理解2D图形的SDF是距离边缘的距离,图形内部为负数,外部为正数,Annular其实非常符合直接,函数如下表示

glsl 复制代码
float op_annular_circle( in vec2 p, in float r1 , float r2)
{
  return abs(length(p) - r1 ) - r2;
}

拉长 Elongation

如果需要将一个圆沿着X轴拉长, 假定A1点坐标为(d, 0), 从坐标轴的视角出发,是将 <math xmlns="http://www.w3.org/1998/Math/MathML"> C D CD </math>CD <math xmlns="http://www.w3.org/1998/Math/MathML"> C 1 D 1 C_1D_1 </math>C1D1 这两条线里内的所有点都映射到 <math xmlns="http://www.w3.org/1998/Math/MathML"> C D CD </math>CD 这条线上的投影点。也就是 <math xmlns="http://www.w3.org/1998/Math/MathML"> C D CD </math>CD <math xmlns="http://www.w3.org/1998/Math/MathML"> C 1 D 1 C_1D_1 </math>C1D1 内所有点的x值变成0. 同时 <math xmlns="http://www.w3.org/1998/Math/MathML"> C 1 D 1 C_1D_1 </math>C1D1 外所有点的x值减去 <math xmlns="http://www.w3.org/1998/Math/MathML"> d d </math>d , 于是有代码

glsl 复制代码
float sdf_elongate_circle1(vec2 p,  float r, float xv ) {
    p.x = p.x > 0.0 ? max(p.x - xv, 0.0 ) : p.x;
    return length(p) - r;
}

如果要往x轴负数方向也拉长

glsl 复制代码
float sdf_elongate_circle1(vec2 p,  float r, float xv ) {
    p.x = p.x > 0.0 ? max(p.x - xv, 0.0 ) : p.x;
    p.x = p.x < 0.0 ? min(p.x + xv, 0.0 ) : p.x;
    return length(p) - r;
}

y轴的处理与x轴没有区别,于是有

glsl 复制代码
float sdf_elongate_circle1(vec2 p,  float r, float xv, float yv ) {
    p.x = p.x > 0.0 ? max(p.x - xv, 0.0 ) : p.x;
    p.x = p.x < 0.0 ? min(p.x + xv, 0.0 ) : p.x;
    p.y = p.y > 0.0 ? max(p.y - yv, 0.0 ) : p.y;
    p.y = p.y < 0.0 ? min(p.y + yv, 0.0 ) : p.y;
    return length(p) - r;
}

四个判断条件可以合并为clamp函数

glsl 复制代码
float sdf_elongate_circle3(vec2 p,  float r, vec2 h ) {
    p = p - clamp(p, -h, h);
    return length(p) - r;
}

最后有以下效果

维度变换 Change of Metric

距离场最核心的函数便是Length. 这个距离是欧氏距离。距离的计算方法为 <math xmlns="http://www.w3.org/1998/Math/MathML"> l e n g t h = x 2 + y 2 length=\sqrt{x^2+y^2} </math>length=x2+y2

如果这个距离的计算函数变成 <math xmlns="http://www.w3.org/1998/Math/MathML"> l e n g t h n = x n + y n n length_n = \sqrt[n]{x^n + y^n} </math>lengthn=nxn+yn

n的不同取值会为图形带来一些奇妙的变化,视觉效果会更加圆一点.. 但是IQ大神是不推荐这种变换,因为会影响到raymarching的结果

相关推荐
GISer_Jing1 天前
WebGL跨端兼容实战:移动端适配全攻略
前端·aigc·webgl
Aurora@Hui4 天前
WebGL & Three.js
webgl
CC码码6 天前
基于WebGPU实现canvas高级滤镜
前端·javascript·webgl·fabric
ct9786 天前
WebGL 图像处理核心API
图像处理·webgl
ct9788 天前
Cesium 矩阵系统详解
前端·线性代数·矩阵·gis·webgl
ct97811 天前
WebGL Shader性能优化
性能优化·webgl
棋鬼王11 天前
Cesium(一) 动态立体墙电子围栏,Wall墙体瀑布滚动高亮动效,基于Vue3
3d·信息可视化·智慧城市·webgl
Longyugxq14 天前
Untiy的Webgl端网页端视频播放,又不想直接mp4格式等格式的。
unity·音视频·webgl
avi911114 天前
Unity毛玻璃渲染模糊渲染Shader数学入门
unity·aigc·图形学·shader·hlsl
花姐夫Jun14 天前
cesium基础学习-坐标系统相互转换及相应的场景
学习·webgl