2D SDF运算1: 变换

旋转移动

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

  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的代码

float opScale( in vec3 p, in float s, in sdf3d primitive )
{
    return primitive(p/s)*s;
}

圆角 Rounded

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

Star

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

D

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

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推导之后,也能感觉出来

D

下列代码中第44行为什么是要减去一个 roundRabs(cos(delta/2.))roundR * abs(cos(delta/2.)) 我猜测是为了让放大缩小弄到边上,这个值也是实验出来的…

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其实非常符合直接,函数如下表示

float op_annular_circle( in vec2 p, in float r1 , float r2)
{
  return abs(length(p) - r1 ) - r2;
}

D

拉长 Elongation

D

如果需要将一个圆沿着X轴拉长, 假定A1点坐标为(d, 0), 从坐标轴的视角出发,是将 CDCD C1D1C_1D_1 这两条线里内的所有点都映射到 CDCD 这条线上的投影点。也就是 CDCD C1D1C_1D_1 内所有点的x值变成0. 同时 C1D1C_1D_1 外所有点的x值减去 dd , 于是有代码

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轴负数方向也拉长

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轴没有区别,于是有

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函数

float sdf_elongate_circle3(vec2 p,  float r, vec2 h ) {
    p = p - clamp(p, -h, h);
    return length(p) - r;
}

最后有以下效果

D

维度变换 Change of Metric

距离场最核心的函数便是Length. 这个距离是欧氏距离。距离的计算方法为
length=x2+y2length=\sqrt{x^2+y^2}
如果这个距离的计算函数变成
lengthn=xn+ynnlength_n = \sqrt[n]{x^n + y^n}
n的不同取值会为图形带来一些奇妙的变化,视觉效果会更加圆一点.. 但是IQ大神是不推荐这种变换,因为会影响到raymarching的结果

原文链接:https://juejin.cn/post/7357301805568704523 作者:欧阳大盆栽

(0)
上一篇 2024年4月15日 上午10:41
下一篇 2024年4月15日 上午10:51

相关推荐

发表回复

登录后才能评论