以下的图案都是基于Hexagonal Tiling创作出的一些艺术图形,怎么样,非常动人吧,只用了大概不到200行的代码。
六边形SDF
两个向量的 <math xmlns="http://www.w3.org/1998/Math/MathML"> d o t dot </math>dot用来表示一个向量在另外一个向量上的投影大小,同时点积的正负号可以表示两个向量的方向。
glsl
float d = dot(uv, vec2(1.));
if (d < 0.25 + sin(iTime*3.0) * 0.25) {
col.bg = vec2(0.8);
} else {
col = vec3(0.0);
}
下图中的白线表示vec2(1.0)向量的方向,也是 y = x
的函数图像, 可以看出颜色块都是垂直与白线的。
这个时候我们在加上 abs
就可以获得一个正方形了。
同样的原理如果我们花一个六边形的斜线,只需要找到下图的 <math xmlns="http://www.w3.org/1998/Math/MathML"> u ⃗ \vec{u} </math>u 向量即可。 从图中不难得出向量 <math xmlns="http://www.w3.org/1998/Math/MathML"> u ⃗ = ( c o s ( 60 ° ) , s i n ( 60 ° ) ) \vec{u} = (cos(60\degree), sin(60\degree)) </math>u =(cos(60°),sin(60°)) 修改点积函数
glsl
float d = dot(uv, vec2(cos(0.3 * PI), sin(0.3 * PI)));
可以得到菱形 要的到六边形就非常简单点,只需要在x轴垂直方向切一刀,
ini
d = max(d, uv.x);
这里面的 d
到底是什么呢? 其实是沿着六边形边的垂直方向距离 零点的长度,所以其实我们略微修改,就可以变成一个距离边的距离场函数
glsl
float SdfHexgon(vec2 p, float r) {
p = abs(p);
float d = dot(p, vec2(cos(0.3 * PI), sin(0.3 * PI)));
d = max(d, p.x);
return d - r;
}
col += step(SdfHexgon(uv, 0.24), 0.0);
于是有
六边形镶嵌
上一节,理解如何画出一个六边形,并推倒了SDF, 但是为了做镶嵌,我们需要将六边形铺满整个屏幕,这也是为什么不直接用sdf的方式, 这里我们将采用 坐标系偏移的思路 画出六边形的镶嵌,里面最核心的 两个坐标系分界方法 就是 上节的画菱形的方法。
为了能够让六边形铺满整个平面,需要以下图的方式镶嵌
在前面很多文章中有提过制作grid的方法,
ini
vec2 gv = fract(uv) - .5;
vec2 gv2 = fract(uv - .5) - .5;
假设我们在grid基础上,在做一个offset(.5)的grid 会产生如下的两个grid.
这个时候我们对两个坐标做一个比较判断
scss
if (length(gv) < length(gv2)) {
col += vec3(.5);
}
就得到了正方形
不过六边形有一个特点,就是他的高度和宽度是不一样的,高度与宽度比为 <math xmlns="http://www.w3.org/1998/Math/MathML"> 2 / 3 2/\sqrt{3} </math>2/3 . 也就是说如果我们吧六边形放到正方形中会导致高溢出或者宽度不够, 如下图所示
而为了解决溢出的问题,我们的grid不能是一个正方形了,grid应该是一个被拉长的长方形 就像下图
grid出不同长高非常简单,就是将fract
函数改为本来的 mod
函数
ini
vec2 r = vec2(1., sqrt(3.));
vec2 h = r * .5;
vec2 a = mod(uv, r) - h;
vec2 b= mod(uv - h, r) - h;
于是有余下的两个偏移grid
将分割与颜色补充就可以看到六边形镶嵌了
glsl
vec2 gv = length(a) < length(b) ? a : b;
col.rg = gv;
属性
ID
一样的,对于这些grid类型, 我们都需要获得tile相关的坐标,以便后续的艺术创作。坐标非常简单,在 fract
做的正方形grid里面,我们通过 floor
获得坐标, 在这里其实一样
ini
vec2 id = uv - gv;
// 这里抽象了一个HexCoords的函数,返回坐标与id
col.rb = (HexCoords(uv).zw + vec2(4.0)) / vec2(8.0);
可以得到图案
Distance
目前返回都是坐标,如果我们能够返回距离六边形边缘的distance, 那么会有更多好的艺术效果可以实现。 这里就用到了我们第一节讲到的方法。
ini
gv.y = SdfHexgon(gv, 0.5);
col.x = abs(HexCoords(uv).y);
角度
ini
gv.x = atan(gv.x, gv.y);
col.x = HexCoords(uv).x/ PI;
引用
- 本文shader: www.shadertoy.com/view/X3K3zR
- 本文geogebra: www.geogebra.org/classic/kzy...
- The art of shader: www.youtube.com/watch?v=Vmr...