旋转移动
- shadertoy: www.shadertoy.com/view/4cVGzc
坐标系中形状的变换可以由坐标系的逆变换完成. 但是有一个问题没搞清楚,旋转+平移 会出现一个无穷值,不知道是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
的结果