《The Book of Shader》笔记 - Chapter 2.5 Patterns 图案
程序员文章站
2022-07-14 21:04:55
...
前言
本节是算法绘图的最后一节,介绍了是如何通过简单图形,有规律地重复排布生成图案。
然后在本节的最后,运用本节于前几节的技巧可大概画出这样的图形。
正文
贴示例之前,先总结下本节中出现频率较多的几个函数。
// -------- 缩放uv坐标系(本节的练习中基本都需要使用该函数) -------- //
vec2 tile(vec2 st, float zoom){
// 通过先给st 乘上一个倍数,将屏幕中的坐标范围增大,再调用fract 达到复制出重复区域的效果
st *= zoom;
return fract(st);
}
// -------- 画圆 -------- //
float circle(vec2 st, float radius){
vec2 l = st - vec2(0.5);
return 1.0 - smoothstep(0.99 * radius, radius * 1.01, dot(l,l)*4.0);
}
// -------- 画方 -------- //
float box (vec2 st, vec2 size, float smoothEdge) {
size = vec2(0.5) - size * 0.5;
vec2 aa = vec2(smoothEdge * 0.5);
vec2 uv = smoothstep(size, size + aa, st);
uv *= smoothstep(size, size + aa, vec2(1.0) - st);
return uv.x * uv.y;
}
// -------- 旋转二维图形 -------- //
vec2 rotate2D(vec2 st, float angle){
st -= 0.5;
st = mat2(cos(angle), -sin(angle),
sin(angle), cos(angle)) * st;
st += 0.5;
return st;
}
// -------- Chapter_2_5_Main 0 -------- //
首先是前言中介绍的图案。
#ifdef GL_ES
precision mediump float;
#endif
vec2 tile(vec2 st, float zoom){
// 通过先给st 乘上一个zoom,再调用fract 达到复制出好几个[0,1]范围的效果
st *= zoom;
return fract(st);
}
float circle (vec2 st, float radius) {
vec2 pos = vec2(0.5) - st;
radius *= 0.75;
return 1.-smoothstep(radius * 0.95, radius * 1.05, dot(pos, pos) * 3.14);
}
float circlePattern(vec2 st, float radius) {
// 用加法的方式,画四个小圆
return circle(st+vec2(0.,-.5), radius)+
circle(st+vec2(0.,.5), radius)+
circle(st+vec2(-.5,0.), radius)+
circle(st+vec2(.5,0.), radius);
}
void main() {
vec2 st = gl_FragCoord.xy / iResolution.xy;
st.x *= iResolution.x / iResolution.y;
vec3 color = vec3(0.0);
// ******** 画出第一个图层 ******** //
vec2 grid1 = tile(st, 8.0);
grid1 = tile(st + vec2(cos(iTime),sin(iTime))*0.01,7.); // 让图案运动起来, vec2(cos(iTime),sin(iTime))*a 这个公式让图片沿圆形轨迹运动,参数a表示周期运动的频率
color += mix(vec3(0.075,0.114,0.329),vec3(0.973,0.843,0.675),circlePattern(grid1,0.23)-circlePattern(grid1,0.01));
/*给图形上色
circlePattern(grid1,0.23)-circlePattern(grid1,0.01) 减法的方式画出同心圆
*/
// ******** 画出第二个图层,叠在第一个图层之上 ******** //
vec2 grid2 = tile(st, 3.0);
grid2 = tile(st + vec2(cos(iTime),sin(iTime))*0.02 ,3.);
color = mix(color, vec3(0.761,0.247,0.102), circlePattern(grid2,0.2)) - circlePattern(grid2,0.05),
gl_FragColor = vec4(color, 1.0);
}
// -------- Chapter_2_5_Main 3.glsl -------- //
#ifdef GL_ES
precision mediump float;
#endif
vec2 brickTile(vec2 st, float zoom) {
st *= zoom;
// 借助mod取余函数达到奇偶行图形错开的效果
st.x += step(1.0, mod(st.y, 2.0)) * 0.5;
return fract(st);
}
float box(vec2 st, vec2 size) {
size = vec2(0.5) - size * 0.5;
vec2 uv = smoothstep(size, size + vec2(1e-4), st);
uv *= smoothstep(size, size + vec2(1e-4), vec2(1.0) - st);
return uv.x * uv.y;
}
void main() {
vec2 st = gl_FragCoord.xy / iResolution.xy;
st.x *= iResolution.x/ iResolution.y;
vec3 color = vec3(0.0);
// Apply the brick tiling
st = brickTile(st, 5.0);
color = vec3(box(st, vec2(0.9)));
// Uncomment to see the
gl_FragColor = vec4(color, 1.0);
}
另外,这个示例后有个练习
// -------- Chapter_2_5_Task3.4.glsl -------- //
#ifdef GL_ES
precision mediump float;
#endif
float circle(vec2 st, float radius){
vec2 l = st - vec2(0.5);
return 1.0 - smoothstep(0.99 * radius, radius * 1.01, dot(l,l)*4.0);
}
void main() {
vec2 uv = gl_FragCoord.xy / iResolution.xy;
uv.x *= iResolution.x / iResolution.y;
vec3 color = vec3(0.0);
uv *= 10.0;
// 将放大后的坐标系分为整数与小数部分,分别提取出来
vec2 ipos = floor(uv);
vec2 fpos = fract(uv);
// 在fract 中加偏移量才能移动,在fract外面加偏移量会出现奇怪的现象
uv = fract(uv + (mod(ipos.x, 2.0) + mod(ipos.x, 2.0) - 1.0) * vec2(0.0, iTime * step(mod(iTime - 1.0, 2.0), 1.0)));
uv = fract(uv + (mod(ipos.y, 2.0) + mod(ipos.y, 2.0) - 1.0) * vec2(iTime * step(mod(iTime, 2.0), 1.0), 0.0));
color = vec3(1.0 - circle(uv, 0.5));
gl_FragColor = vec4(color, 1.0);
}
// -------- Chapter_2_5_Main 4.glsl -------- //
Truchet 瓷砖示例。对前一个示例中使用到的区分奇偶行的技巧,做进一步应用。
#ifdef GL_ES
precision mediump float;
#endif
#define PI 3.14159265358979323846
vec2 rotate2D (vec2 st, float angle) {
st -= 0.5;
st = mat2(cos(angle), -sin(angle),
sin(angle), cos(angle)) * st;
st += 0.5;
return st;
}
vec2 tile (vec2 st, float zoom) {
st *= zoom;
return fract(st);
}
vec2 rotateTilePattern(vec2 st) {
st *= 2.0;
// Give each cell an index number
// according to its position
float index = 0.0;
index += step(1.0, mod(st.x, 2.0));
index += step(1.0, mod(st.y, 2.0)) * 2.0;
// Make each cell between 0.0 - 1.0
st = fract(st);
// Rotate each cell according to the index
if (index == 1.0) {
// Rotate cell 1 by 90 degrees
st = rotate2D(st, PI * 0.5);
} else if (index == 2.0) {
st = rotate2D(st,PI*-0.5);
} else if (index == 3.0) {
st = rotate2D(st,PI);
}
return st;
}
void main(void) {
vec2 st = gl_FragCoord.xy / iResolution.xy;
st.x *= iResolution.x / iResolution.y;
st = tile(st, 3.0);
st = rotateTilePattern(st);
// Make more interesting combinations
st = tile(st,2.0);
//st = rotate2D(st,-PI*iTime*0.25);
//st = rotateTilePattern(st*2.);
st = rotate2D(st,PI*iTime*0.25);
gl_FragColor = vec4(vec3(step(st.x, st.y)), 1.0);
}
// -------- Chapter_2_5_Task4.6.glsl -------- //
最后是太极图案的练习(有时间可以让代码更规范些)
#ifdef GL_ES
precision mediump float;
#endif
#define PI 3.1415926
// -------- 常用图形函数 -------- //
mat2 rotate2d(float angle) {
return mat2(cos(angle), -sin(angle),
sin(angle), cos(angle));
}
mat2 scale(vec2 scale){
return mat2(scale.x, 0.0,
0.0, scale.y);
}
float circle(vec2 st, vec2 pos, float radius) {
vec2 l = st - pos;
return 1.0 - smoothstep(0.99 * radius * radius, 1.01 * radius * radius, dot(l,l));
}
float semicircle(vec2 st, vec2 pos, float radius) {
vec2 l = st - pos;
float pct = 1.0 - smoothstep(0.95 * radius * radius, 1.05 * radius * radius, dot(l,l));
pct *= step(0.0, l.y);
return pct;
}
float square(vec2 st, vec2 size, vec2 pos) {
size = 0.5 - 0.5 * size;
vec2 uv = smoothstep(size, size + vec2(1e-4), st - pos);
uv *= smoothstep(size, size + vec2(1e-4), vec2(1.0) - st + pos);
return uv.x * uv.y;
}
// 太极
float EightElemrnt(vec2 uv, vec2 pos, float n) {
float pct = 0.0;
vec2 st = uv - pos;
pct = square(st, vec2(0.5, 0.08), vec2(0.0, 0.15));
pct += square(st, vec2(0.5, 0.08), vec2(0.0));
pct += square(st, vec2(0.5, 0.08), vec2(0.0, -0.15));
pct -= (step(0.1,n) * step(n,1.0) + step(3.1,n) * step(n,4.1) + step(4.1,n) * step(n,5.) + step(6.1,n) * step(n,7.)) * square(st, vec2(0.1, 0.08), vec2(0.0,0.15)); // 1,4,5,7
pct -= (step(1.1,n) * step(n,2.1) + step(3.1,n) * step(n,4.1) + step(5.1,n) * step(n,6.) + step(6.1,n) * step(n,7.)) * square(st, vec2(0.1, 0.08), vec2(0.0)); // 2,4,6,7
pct -= (step(2.1,n) * step(n,3.1) + step(4.1,n) * step(n,5.1) + step(5.1,n) * step(n,6.) + step(6.1,n) * step(n,7.)) * square(st, vec2(0.1, 0.08), vec2(0.0,-0.15)); // 3,5,6,7
return pct;
}
void main() {
vec2 uv = gl_FragCoord.xy / iResolution.xy;
uv.x *= iResolution.x / iResolution.y;
uv = 2.0 * uv - 1.0;
// uv *= rotate2d(0.5 * iTime);
float pct = 0.0;
// -------- 太极 -------- //
vec2 st = uv * rotate2d(0.6 * iTime);
pct = semicircle(st * rotate2d(-PI / 2.0), vec2(0.0),0.5);
pct *= 1.0 - semicircle(st * rotate2d(-PI / 2.0), vec2(-0.25, 0.0), 0.25);
pct += semicircle(st * rotate2d(PI / 2.0), vec2(-0.25, 0.0), 0.25);
pct += circle(st, vec2(0.0, -0.25), 0.08);
pct -= circle(st, vec2(0.0, 0.25), 0.08);
// -------- 八卦 -------- //
pct += EightElemrnt(uv * 2.0, vec2(-0.5, -2.), 2.0);
pct += EightElemrnt(uv * 2.0 * rotate2d(PI * 0.25), vec2(-0.5, -2.), 3.0);
pct += EightElemrnt(uv * 2.0 * rotate2d(PI * 0.5), vec2(-0.5, -2.), 0.0);
pct += EightElemrnt(uv * 2.0 * rotate2d(PI * 0.75), vec2(-0.5, -2.), 1.0);
pct += EightElemrnt(uv * 2.0 * rotate2d(PI), vec2(-0.5, -2.), 5.0);
pct += EightElemrnt(uv * 2.0 * rotate2d(PI * 1.25), vec2(-0.5, -2.), 4.0);
pct += EightElemrnt(uv * 2.0 * rotate2d(PI * 1.5), vec2(-0.5, -2.), 7.0);
pct += EightElemrnt(uv * 2.0 * rotate2d(PI * 1.75), vec2(-0.5, -2.), 6.0);
vec3 color = vec3(pct);
gl_FragColor = vec4(color, 1.0);
}