shader pattern

912 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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);
}

image.png

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, 能支持更多的细分。

image.png

格子

网格画腻了,就画他那个例子吧。 这个例子十分简单,就是画了一个正方形,然后旋转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;
}

image.png

image.png

image.png

偏移

之前的格子太整齐了,我想让他们错开。就像下面这样。实际上就分奇偶,让奇数和偶数行的x有偏移。

st本身的值域只有01,但是在放大之后值域就变成了[0,n], 判断奇偶就用取模。

需要注意的是,mod函数并不会抹掉小数点,所以我会判断余数是否大于1,而不是其他。 step就是判断大小,结果只有0或者1, 那么只要在后面乘上一个偏移量就可以达成只有奇数行偏移的效果,如果把偏移量和时间t关联起来就能得到动画。

从0行开始。

但是,我觉得只有奇数行有动画并不好看,于是,减去了.5, 这样偶数就是往右偏移,奇数往左偏移,因为是直接作用在原坐标上, 加就等于是把可视区域往右移,视觉上就在左移。

image.png


    float offsetY = (step(1. , mod(st.y, 2.)) -.5)*(.5);// 奇偶
    float offsetY = (step(1. , mod(st.y, 2.))*(.5);// 只有偶数行
    st.x+= offsetY;

chrome-capture-2022-7-27.gif

chrome-capture-2022-7-27.gif

最后,同样给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);

chrome-capture-2022-7-27 (1).gif 正弦的导数就是余弦,我意识到这点之后就发现改起来还是挺容易的。

 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);

chrome-capture-2022-7-27 (2).gif

chrome-capture-2022-7-27 (3).gif

拆分组合

书上的标题是某种瓷砖,大概的做法就是,把一份空间再分成四份,然后每份同样的图案,只不过旋转角度不同。

关键技巧就是这个一分为四。 把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 ; 
}

由于我的三角初始位置和书上的不同,结果成了这样,先看我的初始位置。

image.png

image.png

image.png

uv*=rotate(PI *ind)

image.png

我发现了真正问题所在,按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 ;}

然后,我又改变了初始角度,得到和书上一样的效果

image.png

image.png

结束

往后还要多多练习才是,练习不同的图案,不同的写法。