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行为什么是要减去一个 r o u n d R ∗ a b s ( c o s ( d e l t a / 2. ) ) roundR * abs(cos(delta/2.)) 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), 从坐标轴的视角出发,是将 C D CD CD C 1 D 1 C_1D_1 C1D1 这两条线里内的所有点都映射到 C D CD CD 这条线上的投影点。也就是 C D CD CD C 1 D 1 C_1D_1 C1D1 内所有点的x值变成0. 同时 C 1 D 1 C_1D_1 C1D1 外所有点的x值减去 d d 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. 这个距离是欧氏距离。距离的计算方法为 l e n g t h = x 2 + y 2 length=\sqrt{x^2+y^2} length=x2+y2

如果这个距离的计算函数变成 l e n g t h n = x n + y n n length_n = \sqrtn{x^n + y^n} lengthn=nxn+yn

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

相关推荐
八荒启·交互动画1 天前
# Web特效020—让 Web 特效真正动起来:时间循环应该怎么接
前端·webgl·网页特效·八荒启-交互动画·八荒启
八荒启·交互动画1 天前
Web特效019-Web特效的定义与边界核心算法手写一遍:先跑起来,再谈高级效果
前端·webgl·网页特效·八荒启-交互动画·八荒启
八荒启·交互动画2 天前
Web特效011—Web特效的定义与边界为什么忽快忽慢:时间、频率与采样的秘密
webgl·网页特效·八荒启-交互动画·八荒启·web特效
八荒启·交互动画2 天前
Web特效008—一个 `getContext(‘webgl‘)`,浏览器到底交出了什么?
webgl·八荒启-交互动画
玖玥拾3 天前
Unity Shader 基础与图形学(一)
unity·图形渲染·图形学·shader
xhload3d3 天前
图扑智慧工厂 | 继电器产线仿真态势管控平台
物联网·低代码·webgl·数字孪生·可视化·智慧工厂·工业互联网·hightopo
新的瑞拉公主6 天前
Unity游戏发布微信小游戏:从构建到提审
unity·webgl·微信小游戏·开放数据域
慧都小妮子8 天前
实时大屏掉帧排查:WebGL 图表库选型的 5 项可实测检查
webgl·scichart.js·实时大屏掉帧·前端图表选型
❀͜͡傀儡师8 天前
移动端 360° 商品展示方案:从 20MB 雪碧图到 800KB 丝滑视频
ffmpeg·webgl·sprite
平行云8 天前
国产GPU云渲染适配实战:驱动兼容、编码调优与多路并发
unity·ue5·webrtc·webgl·实时云渲染·云桌面·像素流送