最近为了给游戏添加一些背景,速通了一下Shader,虽然不知道为什么顺便把sdf和raymarching和dnl一起速通了。顺便研究了一下Shader golf(压缩代码数量),在研究Shader golf的时候,我发现可以利用位运算去读取存储在二进制里面的数据,由此来画出1-bit的像素图,并且Tiling。
二进制由于是由1和0组成的,可以用1表示有东西,0表示没东西在GLSL里面可以很简单的用三目运算来判断并且赋值。
为了读取每一个数字,在特定的位置需要位右移(»),同时%所画图像的宽高,就能实现Tiling。
void mainImage( out vec4 O, in vec2 I)
{
int art[18] = int[](18,63,30,63,30,12,
18,63,30,30,63,18,
12,12,63,63,45,12);
I /= 20.;
I.y += 9e4;
I.y += (int(I.x)/6)%2==1?iTime*2.:-iTime*2.;
O.rgb = (int(art[int(I.y)%18]>>int(I.x)%6)&1)>0?vec3(0.+float((int(I.y)/6)%3)*.2,.2,.3):vec3(.92);
}
Shadertoy地址:https://www.shadertoy.com/view/Dtyczd
这段代码不是很golf,而且事后发现有需要改动的地方,接下来会进行一一讲解。
int art[18] = int[](18,63,30,63,30,12,
18,63,30,30,63,18,
12,12,63,63,45,12);
首先要创建一个数组,里面存储三个图像的数据,图像是6*6。
以第一个图像举例:
18的二进制是010010,63的二进制是111111,30的二进制是011110,12的二进制是001100。所以合起来第一个图像会是
010010
111111
011110
111111
011110
001100
剩下三个以此类推,现在写好了数组之后,就是要想办法画出图像了。
O.rgb = (int(art[int(I.y)%18]>>int(I.x)%6)&1)>0?vec3(0.+float((int(I.y)/6)%3)*.2,.2,.3):vec3(.92);
这行代码看着吓人,但是实际上很简单。
首先O是输出,我替换了fragColor,在这里我们用了一个三目运算符,来做条件判断,首先看一下?前面的代码,int(art[int(I.y)%18])这里为了能够获取到所有的图像数据,并且我想竖向排布,所以使用了I.y,I替换了fragCoord,意思是当前的像素坐标。换句话就是说,每18个像素重复一次。
既然从列读取了图像,接下来该从行中获取了。
为了获取到往右一格的图像数据,这里使用了»,并且根据每六个像素重复一次,也就是int(I.x)%6,最后对这个结果进行bitwise AND判断,如果是1和1,那么自然会得到1,其值大于0,为true;如果为0和0,那么会得到0,其值不大于0,为false。
:左边的是vec3(0.+float((int(I.y)/6)%3)*.2,.2,.3),如果为true,并且每个周期内的一个图像的颜色就要发生一些小小的改变。右边就自然不用解释了,如果是0就画出白色。
为了让我们的图像看上去更大一点,可以用
I /= 20.;
为了让奇数列偶数列的图像程不同运动方向,我们可以简单这样写:
I.y += (int(I.x)/6)%2==1?iTime*2.:-iTime*2.;
因为一些绘制上的问题,我们要把坐标提前一点:
I.y += 9e4;
当然,这些代码很不美观,以上三行代码可以用一行代码代替:
I.y += mod(iTime*(int(I /= 20.)/6%2==0?-2.:2.),18.);
除18取余,18的意义在于上面的数组的大小,为了在适当的地方重复,保证无缝衔接。
这样就不需要I.y += 9e4了。