形状与图案

常见形状

矩形

img

如图,若要画出上述矩形;正常思路就是扣去y>ay > ay<by < bx<cx < cx>dx > d的所有像素点,剩下的像素点就是我们想要的矩形区域;

具体到GLSL中,可以使用step()函数来进行扣去像素点的操作;如:

1
step(b, y);

就代表y<by < b的像素点会返回00;那么y>ay > a呢?如下:

1
1.0 - step(a, y);

综上,利用step()抠出一个矩形区域的做法为:

1
step(b, y) - step(a, y) - step(d, x) - (1.0 - step(c, x));

当然,这样得到矩形区域的边缘会出现很明显的锯齿,可以使用smoothstep()函数来替代step()函数,得到一个比较平滑的边缘。

圆形

圆形区域就比较容易获取了,只需要计算像素点到圆心的距离就可以确定是不是在区域内了;

1
1.0 - step(r, length(pos - center));

正多边形

画出任意边数的正多边形可以根据一个距离场函数来进行;这个距离场函数实际上就是利用极坐标来进行计算的;

根据正多边形的内切圆或外接圆,将角度均分为相应的份数,在每份角度周期圆心到边上的距离相同的规律变化

img

如上图所示,以『正三角形』为例;在正三角形内得到一个内切圆,从圆心到顶点的边可以将内切圆均分为3份(即120°120\degree)。以0号边为起点,1号边为终点,对应内切圆的[0, 120°][0,\ 120\degree],将该区间转化为[60°, 60°][-60\degree,\ 60\degree],很自然地得到该段三角形边的极坐标方程为:

r=ρcosαρ=rcosαr = \rho \cdot cos\alpha \\ \Rightarrow \rho = \frac{r}{cos\alpha}

而对应到整个正三角形,关键就在于边上的点的角度到底位于[60°, 60°][-60\degree,\ 60\degree]区间的哪个位置。显然,只需要对角度取余即可。同理,该极坐标方程可以推广到任何正多边形;除此之外,也可以利用正多边形的外接圆来确定极坐标方程,精髓就在于确定一个周期内不变的三角函数关系即可!

上述极坐标方程只是确定了正多边形边上的点,若要得到一个正多边形区域,只需要满足到内切圆圆心的距离小于ρ\rho即可。

GLSL中的实现

1
2
3
4
5
6
7
8
9
10
11
// 根据内切圆法得到正多边形的极坐标方程
float polygon (float n, float r, vec2 p) {
float per = TWO_PI / n; // 均分一圈得到的弧度
float x = p.x;
float y = p.y;
float radius = length(vec2(x, y)); // 该点距离圆心的距离
float angle = atan(y, x); // 该点相对于圆心的角度
float delta = angle - floor(angle / per + 0.5) * per; // 该点处于[-per/2, per/2]区间的位置

return 1.0 - smoothstep(r, r * 1.01, radius * cos(delta)); // 判断 r = ρ * cosα
}

上述方法可以画出一个正多边形区域,且对区域边缘部分进行了光滑处理;pp参数可以看做内切圆心(即多边形中心位置)到正多边形边上的向量,而rr参数则是内切圆的半径,稍微处理一下就可以当做多边形的边长。

关于使用内切圆来绘制正多边形,可以查看这个demo32. Polygon

极坐标

ρ=ρ(θ)\rho = \rho(\theta)

ρ\rho就是当前点距离中心点的长度;而θ\theta则是当前点相对于中心点的角度(弧度);只要图形边缘满足上述特点即可使用极坐标进行绘制,如最常见的圆形,可以使用如下极坐标方程:

ρ=a\rho = a

aa是任意常数;

可以通过以下方式来计算某点的ρ\rhoθ\theta

1
2
3
vec2 pos = p - center; // p是当前坐标点,center为图形中心点坐标
float len = length(); // 计算长度
float angle = atan(pos.y, pos.x); // 计算角度

组合图形

  1. 叠加
  2. 扣除

图案(patterns)

广义指对某种器物的造型结构、色彩、纹饰进行工艺处理而事先设计的施工方案,制成图样,通称图案。有的器物(如某些木器家具等)除了造型结构,别无装饰纹样,亦属图案范畴(或称立体图案)。狭义则指器物上的装饰纹样和色彩而言。

—— https://baike.baidu.com/item/图案/52183

img

几何图案

网格

通过将屏幕从水平和垂直方向分成一个nmn*m的网格,其中每个网格单元可以进行图形的独立绘制,得到一个重复排列的图案;

假设最初得到的屏幕坐标为标准设备坐标(即坐标范围为[1, 1][-1, \ 1]),可以通过fract()方法来进行网格单元的切割:

1
2
3
vec2 num = vec2(n, m); // 水平及竖直方向单元的个数
pos *= num; // 放大坐标
pos = fract(pos); // 得到网格中的独立坐标

通过上述方法可以将坐标分成n * m个网格,其中每个网格内的坐标都是[0, 1][0, \ 1]之间;然后对每个单元格进行相同的图形绘制,就可以得到重复填充的图案。可以查看这个根据网格进行重复填充的demo35. Pattern Test

不仅如此,还可以进一步获取每个单元格位于水平和竖直方向上的索引,然后进行偏移或其它操作,得到更加丰富的填充效果;

参考文档

  1. The Book of Shaders: Patterns