前言
本节是算法绘图的最后一节,介绍了是如何运用简单图形,有规律地重复排布生成图案。
在本节的最后,综合使用本章的技巧可大概画出这样的图形。
正文
贴示例之前,先总结下本节中出现频率较多的几个函数。
// -------- 缩放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);
}
再来个六边形(轻喷,感觉还是得网上参考下大神们怎么写的。)
const float PI = 3.1415926;
mat2 rot2(float angle) {
return mat2(cos(angle), -sin(angle), sin(angle), cos(angle));
}
void main() {
vec2 uv = gl_FragCoord.xy / iResolution.xy;
uv.x *= iResolution.x / iResolution.y;
float sqrt3 = sqrt(3.0);
float pct = 0.0;
vec3 color = vec3(pct);
uv.y *= sqrt3 / 2.0;
uv *= 10.0;
vec2 i = floor(uv);
vec2 f = fract(uv);
f.x = (1.0 - f.x) * mod(i.x, 2.0) + f.x * mod(i.x + 1.0, 2.0);
float width = 0.05;
// -------- Line -------- //
f.x += 0.141;
pct = step(0.5 - width, f.y) * step(f.y, 0.5 + width) * step(f.x, 0.5);
pct += step(f.y, width) * step((3.+sqrt(3.))/ 6., f.x);
pct += step(1.0 - f.y, width) * step((3.+sqrt(3.))/ 6., f.x);
f = rot2(-2.0 * PI / 3.0) * (f - vec2(0.5)) + vec2(0.5);
pct += step(0.5 - width, f.y) * step(f.y, 0.5 + width) * step(f.x, 0.5);
f = rot2(-2.0 * PI / 3.0) * (f - vec2(0.5)) + vec2(0.5);
pct += step(0.5 - width, f.y) * step(f.y, 0.5 + width) * step(f.x, 0.5);
color = vec3(pct);
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 -------- //
最后是太极图案的一个小练习。
代码链接:
https://www.shadertoy.com/view/Ws3BRf