距离场 distacnce filed 就是用一个描述模型表面到模型中心距离的函数,来绘制图形的方式。
当然,今天只是进门,我们抛开z分量不谈,只看二维的。
比如最简单的圆形, 就是所有满足 d = r 的点的集合。一般来说,我们会使用极坐标来写距离场函数。
直线
圆形就不用说了吧。 我最近才悟了那个三角形的画法,其实就是直线,然后加上周期偏移。
以下是源代码和效果。
我要画的直线是x = b, b>0。 因为这个最好画了。 对于这条直线,直线上任意一点(x,y)或者说(d,a) d是距离 a是角度,它们的关系是什么呢?
我之前没有想过用极坐标和直角坐标的换算直接做,但是现在可以试一下,这个是比较通用的一种方式。 y = d * sin(a) ,x = d * cos(a) 。 所以我们这个直线的极坐标方程就是 d * cos(a) = b ;
我之前是看代码的实现,以及几何画图领悟到的。我们现在来看图。
极轴就等于是x 轴, 角度a是点和极点的连线与极轴的夹角, 这个夹角是极轴为起始边, 连线为终边。
所以,我们可以看到所以在直线的点都满足 d * cos(a) = b 这个条件, 而d * cos(a) < b的点都在左边,cos值在[pi/2, 3pi/3]之间是小于零的,所以这里能画出一条直线,可以想象一下,当a 趋于 90度的时候,这个点的y值是不是就是无穷大,的确是一条直线,而不是线段。
这样就可以写出如下代码,用delta 作角度偏移。 当然,最好就是用直角坐标进行变换,然后再求极坐标。极坐标的平移变换十分不便。
float line (float a , float d , float f, float deltaA){
d *=cos(a+deltaA) ;
return smoothstep(-.01 ,.001 ,d - f ) - smoothstep(-.001 ,.01 ,d - f ) ;
}
。。。。。。
color = mix(color, color1, line(a,d0,.5,0.));
color = mix(color, color3, circle(d0, .5));
下图绿色的线就是结果,那个圆是用来参照的。
如果按照直角坐标的画法,肯定就是三条线取一个交集,得到一个填充的三角形,用两个填充的三角形相减就得到描边的三角形。
但是这里,我们可以用另一种方式,那就是周期。极坐标很容易就能对角度进行周期处理。
我们尝试一下,修改上面的line 函数,输入一个重复系数N d *=cos(a *N) ; ,暂且取N = 3 。
结果是这样的。 好像不符合预期啊, 实际上就是如此,直接放大角度等于是压缩了绘图区域,所以才会出现,一条线被压缩成一条折线的效果,中间还自带平滑过渡。
周期变小导致压缩
这个压缩绘图区,就像是一把圆扇,本来是完全打开的360度,现在合起来只剩下 120度。
为此,我写了个动画来说明问题,逐渐增加放大系数k的值。
float k = (sin(t)*3.) + 4.; // +4 保证系数大于等于1
float aN = a * k;
if( abs(aN) < PI / 2.){ // 这个判断是为了只显示一个周期的图形
color = mix(color, color1, line(aN ,d0,.2,0.));
}
改成填充
内部颜色改成角度渐变。
if( abs(aN) < PI / 2.){ color = mix(color, hsbToRgb(vec3(aN/PI ,d0, 1.)), line2(aN ,d0,.2,0.)); }
多边形
现在切回正题, 既然不能压缩绘图区域,我们就不压缩。 实际上,我们要的是周期性偏移。
比如说现在就是放大三倍,那么在第一个周期内正常绘制,到了第二个周期,我还想画直线,实际上,不就是把第一个周期的直线做一个旋转,旋转量为一个周期的弧度, 第三个周期同样如此,旋转量为两个周期。
看代码。 通过取整函数来确定是第几个周期,从0开始。第N个周期的偏移量是 N-1个周期。
// r是内切圆的半径
float polygon (float a , float d ,float N, float r){
float c = floor(a/2./PI * N) ;
d*=cos( a- c * PI *2./N); // 待修复
return smoothstep(-.01 ,.001 ,d - r ) - smoothstep(-.001 ,.01 ,d - r ) ;
}
补充说明,为啥不能用取模直接实现 。实际上取模也可以实现,只不过这个被除数不是整数,所以需要一点处理。
float b = a/PI/2. ;// 01之间 就是大家喜欢说的归一化 单位化
b *=N;
b= fract(b)/N; // 之前缩放了的再给他缩放回去
来看效果。 还差了一点东西。 因为上面的取整逻辑是从 0 开始的,也就是第一个周期是 [0,2* PI/5] , 这样就只绘制了极轴上方的部分,实际上,我们要的周期应该是 [- PI/5, PI/5]。
color = mix(color, color2, polygon(a,d0,5., .2));
修复一波。
d*=cos( a- (c + .5) * PI *2./N);
到这里这个正多边形就绘制出来了,需要填充的就改成填充就好了,代码的数学运算,简化以后就是文章开头的截图。
到这里,差不多就进门了。