携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第14天,点击查看活动详情
注意,下文,格子特别多,没有衫。
pattern就是一种图案,可重复的, 比如瓷砖。用shader来做,就是要设计好最小的那个单元,然后用各种方式去实现周期性重复。前面的网格就是一种pattern。下面是另一种方式实现的网格,是用gl_FragColor的坐标直接取余得到,缺陷就是会依赖绝对的物理坐标系,不能用作任意几何面的贴图。
#version 300 es
out highp vec4 Ocolor;
#define gl_FragColor Ocolor
#define varying in
#ifdef GL_ES
precision mediump float;
#endif
#define PI 3.14159265359
uniform vec2 u_CavasSize;
uniform vec2 u_mouse;
uniform float u_Time;
void main() {
vec2 st = gl_FragCoord.xy/u_CavasSize.xy;
vec3 color = vec3(0.0);
// 用取模实现 周期 [0,1]
vec2 s2 = vec2(mod(gl_FragCoord.x/50.0,1.0), mod(gl_FragCoord.y/50.0,1.0)) ;
vec3 c3y = vec3(s2.y) ;
float min = 0.8 ;
float max = 0.9 ;
// 而我现在想要条条框框, 我的想法就是先 分别限制x y ,然后再相加就可以得到
vec3 c4y =smoothstep(min, max, c3y ) ;
vec3 c4x =smoothstep(min, max, c3x ) ;
gl_FragColor = vec4(c4x+c4y,1.0);
}
fract
之所以提到这个,是因为我发现直接放大uv坐标,然后用fract取小数位,会有问题. 问题大概在于,乘以10的时候,小数位就少了一位,这意味着数据的密度稀疏了十倍,以致于在某些情况下本来应该绘制的结果因为取不到值没能绘制。 因为计算机里的[1,0]并不是理想中那么连续,它也是离散的,是有最小精度的。
之前绘制函数是这样的。就是把放大后的xy 限制在[0,w] [1-w,1]的范围内,但是由于uv被放大了,比如说现在宽度是0.1,但是放大后,10倍,区间就是[0,0,01],uv在这个区间内的数量不足,以至于最后这条线就画不出来,或者说视觉上看不见。
vec2 grid2 (vec2 uv , float w) {
uv-=vec2(.5 );
return step(vec2(.5 -w) , abs(uv) );
}
下面这个图是放大15倍,w=.5的时候,刚好有些线比较细,如果我再放大或者w再小一些就会消失。主要还是线不能太细,线宽足够的话,一般顶多出现这样情况,不过这种情况显然也不是我想出现的,所以,如果用这种方式绘制,要注意精度问题,当然可以调高精度 highp, 能支持更多的细分。
格子
网格画腻了,就画他那个例子吧。 这个例子十分简单,就是画了一个正方形,然后旋转45度。这个正方形的边长为√2, 这样刚好就卡在那里了。
float block2(vec2 uv, float angle){
uv-=.5;
uv*=rotate(-angle);
float w = sqr2/4.;
vec2 xy = smoothstep(w +.01,w -.01 ,abs(uv) ) ;
return xy.x *xy.y;
}
偏移
之前的格子太整齐了,我想让他们错开。就像下面这样。实际上就分奇偶,让奇数和偶数行的x有偏移。
st本身的值域只有01,但是在放大之后值域就变成了[0,n], 判断奇偶就用取模。
需要注意的是,mod函数并不会抹掉小数点,所以我会判断余数是否大于1,而不是其他。 step就是判断大小,结果只有0或者1, 那么只要在后面乘上一个偏移量就可以达成只有奇数行偏移的效果,如果把偏移量和时间t关联起来就能得到动画。
从0行开始。
但是,我觉得只有奇数行有动画并不好看,于是,减去了.5, 这样偶数就是往右偏移,奇数往左偏移,因为是直接作用在原坐标上, 加就等于是把可视区域往右移,视觉上就在左移。
float offsetY = (step(1. , mod(st.y, 2.)) -.5)*(.5);// 奇偶
float offsetY = (step(1. , mod(st.y, 2.))*(.5);// 只有偶数行
st.x+= offsetY;
最后,同样给x加上偏移动画,我是直接用正弦周期,正负xy,所以切换的时候比较突兀,懒得去用三角函数作为偏移量,在导函数为0的切换。
float offsetY = (step(1. , mod(st.y, 2.)) -.5)*(.5 +t);
float offsetX = (step(1. , mod(st.x, 2.)) -.5)*(.5 +t);
sin(t)>0.?(st.y +=offsetX):(st.x+= offsetY);
正弦的导数就是余弦,我意识到这点之后就发现改起来还是挺容易的。
float sint = sin(t),cost = cos(t) ;
float offsetY = (step(1. , mod(st.y, 2.)) -.5)*(cost *2.);
float offsetX = (step(1. , mod(st.x, 2.)) -.5)*(cost *-2.);
sint>0.?(st.y +=offsetX):(st.x+= offsetY);
拆分组合
书上的标题是某种瓷砖,大概的做法就是,把一份空间再分成四份,然后每份同样的图案,只不过旋转角度不同。
关键技巧就是这个一分为四。 把01放大为[0,2]之后,我们还是用取余来判断当前是哪一个区域,直接用2取余,如果某个方向上,余数大于1,说明在他在这个方向属于第二个区域。
其实,如果能保证入参是[0,1],那么在放大之后是可以直接判断是否大于1,不用取余。
float triangles(vec2 uv){
return smoothstep( 0.99,1.01 ,uv.x + uv.y );
}
vec2 title (vec2 uv){
uv*=2. ;
float ind =0.;
ind += step(1., uv.x)*.5 ;
ind += step(1., uv.y) ;
uv =fract (uv) ;
uv-=.5;
uv*=rotate(-PI * ind);
uv+=.5;
return uv ;
}
由于我的三角初始位置和书上的不同,结果成了这样,先看我的初始位置。
uv*=rotate(PI *ind)
我发现了真正问题所在,按xy的大小加1,结果是0132, 我需要交换23的位置,如果我不想按书上写if else,结果还是写了if else.
//2 3 交换
float t ;
if(ind == 2.){ t =3.;}
if(ind == 3.){ t =2.;}
if(t >1.){ ind = t ;}
然后,我又改变了初始角度,得到和书上一样的效果
结束
往后还要多多练习才是,练习不同的图案,不同的写法。