2D SDF推导5: 通用星形

58 阅读1分钟

image.png

SDF推导通用星型与通用正多边形非常类似,主要有三个关键点

  1. 极坐标转换
  2. 利用对称性简化运算
  3. 把问题简化到Segement的SDF推导

如上图所示,P点的位置可以分为4个区域,实际上只需要求出 P1P1 P4P4 点的位置,再利用线段SDF加上向量方向判断距离正负号即可。 为了方便运算引入极坐标与笛卡尔坐标系的转化函数

vec2 cartesianToPolar(vec2 cartesianCoords) {
    float r = length(cartesianCoords); // Radial distance
    float theta = atan(cartesianCoords.y, cartesianCoords.x); // Angle in radians
    return vec2(r, theta);
}


vec2 polarToCartesian(vec2 polarCoords) {
    float x = polarCoords.x * cos(polarCoords.y); // r * cos(theta)
    float y = polarCoords.x * sin(polarCoords.y); // r * sin(theta)
    return vec2(x, y);
}

假定当前星形的边为N, 内径为r1, 外径为r2。 于是有 δ=2π/N\delta = 2\pi / N
P4$ 极坐标为$ P4 = (r1, \delta / 2)$$
于是有以下代码

float sdf_star(vec2 P, float outRadius, float inRadius, int sides) {
    float angle = atan(P.y, P.x);
    angle = P.y > 0. ? angle: angle + PI * 2.;
    
    float delta = 2. * PI / float(sides);
    float theta = mod(angle, delta) - delta / 2.0;
    float pieceIdx = floor(angle / delta);

    // start angle of current piece
    float theta3 = delta * pieceIdx;
    vec2 polar_P1 = vec2(outRadius, theta3);    
    vec2 polar_P4 = vec2(inRadius, delta/2.0+theta3);
    
    // p and symmetrical p
    float theta2 = delta / 2.0 - abs(theta) + theta3;    
    vec2 polar_P = vec2(length(P), theta2); 

    // point
    vec2 P0 = polarToCartesian(polar_P);
    // segment a,b
    vec2 P1=  polarToCartesian(polar_P1);
    vec2 P4=  polarToCartesian(polar_P4);

    //segment sdf
    vec2 v1 = P0 - P4;
    vec2 v2 = P1 - P4;
    float h = clamp(dot(v1,v2)/dot(v2,v2), 0.0, 1.0);
    
     
    return sign(cross2(v2, v1)) * length(v1-h*v2); 

}

Finally, We got this :)