提示
教程例子都可以到下面网址进行运行,不需要另外安装软件环境:
官方提供在线编写shader工具:https://thebookofshaders.com/edit.php
glslsandbox网站:http://glslsandbox.com/
shadertoy网站:https://www.shadertoy.com/
随机
y = fract(sin(x)*1.0);
y = fract(sin(x*10000.)*10000.0);
我们提取sin函数其波形的分数部分。
sin()
函数值在-1.0
到1.0
之间浮点分布,返回作业在0.0
到1.0
间的正值。(这里翻译有些奇怪)我们可以用这种效果通过把sin(x)
打散成小片段来得到一些伪随机数。如何实现?乘以大些的sin值。在上面函数的相应位置加些0.
当你加到 100000.0 (方程看起来是这样的:y = fract(sin(x)*100000.0) ),你再也区分不出sin波了。小数部分的粒度将sine的循环变成了伪随机的混沌。
控制混沌
》使用随机会很难;它不是太混沌难测就是有时又不够混乱。看看下面的图例。要实现这样的效果,我们像之前描述的那样应用用 rand() 函数。
细看,你可以看到 sin()
在 -1.5707
和 1.5707
到拐点。???我打赌一定理解为什么——那就是sin最大值和最小值的地方。
如果你仔细观察随机分布,你会注意到相比边缘,中部更集中。
···
y = rand(x);
···
试试下面这几个
···
y = rand(x)*rand(x);
y = sqrt(rand(x));
y = pow(rand(x),5.);
···
如果你读下 Pixelero 的文章,一定谨记我们用的
rand()
是确定性随机,也被称作是伪随机。这就意味着, 就rand(1.)
为例,总会返回相同的值。Pixelero 用 ActionSript 函数做了些参考,Math.random()
,一个非确定性随机;每次调用都返回不同的值。
2D 随机
》现在我们对随机有了深入的理解,是时候将它应用到二维,x
轴和 y
轴。为此我们需要将一个二维向量转化为一维浮点数。这里有几种不同的方法来实现,但 dot()
函数在这个例子中尤其有用。它根据两个向量的方向返回一个 0.0
到 1.0
之间的值。
···
ifdef GL_ES
precision mediump float;
endif
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
float random (vec2 st) {
return fract(sin(dot(st.xy,
vec2(12.9898,78.233)))*
43758.5453123);
}
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
float rnd = random( st );
gl_FragColor = vec4(vec3(rnd),1.0);
}
···
- vec2(12.9898,78.233)换成vec2(u_mouse.x,u_mouse.y), 你会看到一台雪花电视
使用混沌
我们的第一步是在网格上的应用;用
floor()
函数,我们将会产生一个单元整数列表。看下下面的代码,尤其是22行和23行。
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
float random (vec2 st) {
return fract(sin(dot(st.xy,
vec2(12.9898,78.233)))*
43758.5453123);
}
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
st *= 10.0; // Scale the coordinate system by 10
vec2 ipos = floor(st); // get the integer coords
vec2 fpos = fract(st); // get the fractional coords
// Assign a random value based on the integer coord
vec3 color = vec3(random( ipos ));
// Uncomment to see the subdivided grid
// color = vec3(fpos,0.0);
gl_FragColor = vec4(color,1.0);
}
- 这个例子告诉你,random输入到值是固定的,输出值也是固定的
- 把小数部分补上去看看色彩 color += vec3(fpos.5,fpos.5);
看下这个著名的 10 PRINT CHR$(205.5+RND(1)); : GOTO 10迷宫生成器的GLSL代码块
#ifdef GL_ES
precision mediump float;
#endif
#define PI 3.14159265358979323846
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
float random (in vec2 _st) {
return fract(sin(dot(_st.xy,
vec2(12.9898,78.233)))*
43758.5453123);
}
vec2 truchetPattern(in vec2 _st, in float _index){
_index = fract(((_index-0.5)*2.0));
if (_index > 0.75) {
_st = vec2(1.0) - _st;
} else if (_index > 0.5) {
_st = vec2(1.0-_st.x,_st.y);
} else if (_index > 0.25) {
_st = 1.0-vec2(1.0-_st.x,_st.y);
}
return _st;
}
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
st *= 10.0;
// st = (st-vec2(5.0))*(abs(sin(u_time*0.2))*5.);
// st.x += u_time*3.0;
vec2 ipos = floor(st); // integer
vec2 fpos = fract(st); // fraction
vec2 tile = truchetPattern(fpos, random( ipos ));
float color = 0.0;
// Maze
color = smoothstep(tile.x-0.3,tile.x,tile.y)-
smoothstep(tile.x,tile.x+0.3,tile.y);
// Circles
// color = (step(length(tile),0.6) -
// step(length(tile),0.4) ) +
// (step(length(tile-vec2(1.)),0.6) -
// step(length(tile-vec2(1.)),0.4) );
// Truchet (2 triangles)
// color = step(tile.x,tile.y);
gl_FragColor = vec4(vec3(color),1.0);
}
掌握随机
Ikeda 的作品并试试看下面的练习:
- 做按行随机移动的单元(以相反方向)。只显示亮一些的单元。让各行的速度随时间变化。
https://thebookofshaders.com/edit.php#10/ikeda-00.frag - 同样地,让某几行以不同的速度和方向。用鼠标位置关联显示单元的阀值
https://thebookofshaders.com/edit.php#10/ikeda-03.frag - 创造其他有趣的效
https://thebookofshaders.com/edit.php#10/ikeda-04.frag