———————————————————— 前序 ———————————————————
AndroidLearnOpenGL是本博主自己实现的LearnOpenGL练习集合:
Github地址:GitHub - wangyongyao1989/AndroidLearnOpenGL: OpenGL基础及运用
系列文章:
1、LearnOpenGL之入门基础
2、LearnOpenGL之3D显示
3、LearnOpenGL之摄像机
4、LearnOpenGL之光照
5、LearnOpenGL之3D模型加载
6、LearnOpenGL之文字渲染
7、LearnOpenGL之高级OpenGL(1)
8、LearnOpenGL之高级OpenGL(2)
9、LearnOpenGL之骨骼动画
10、LearnOpenGL之Shader编程用算法绘画
11、LearnOpenGL之Shader编程——生成设计
——————————————————————————————————————————
—————————————————— 效果展示 ————————————————————
随机性是熵的最大表现。我们如何在看似可预测而且严苛的代码环境中生成随机性呢?
sin函数其波形的分数部分。值域为-1.0
到 1.0
之间的sin() 函数被取了小数点后的部分(这里实际是指模1)),返回0.0
到 1.0
间的正值。我们可以用这种效果通过把正弦函数打散成小片段来得到一些伪随机数。如何实现呢?通过在sin(x)的值上乘以大些的数。
使用随机会很难;它不是太混沌难测就是有时又不够混乱。谨记我们用的 rand()
是确定性随机,也被称作是伪随机。这就意味着, 就 rand(1.)
为例,总会返回相同的值。
现在我们对随机有了深入的理解,是时候将它应用到二维,x
轴和 y
轴。为此我们需要将一个二维向量转化为一维浮点数。这里有几种不同的方法来实现,但 dot() 函数在这个例子中尤其有用。它根据两个向量的方向返回一个 0.0
到 1.0
之间的值。
用 dot() 函数在这个例子中尤其有用。它根据两个向量的方向返回一个 0.0
到 1.0
之间的值。
#version 320 es
precision mediump float;
out vec4 FragColor;//out代表输出至下一个着色器
uniform vec2 u_resolution;//视窗的分辨率
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;
vec3 color = vec3(0.0f);
float rnd = random(st);
color = vec3(rnd);
FragColor = vec4(color, 1.0f);
}
第一步是在网格上的应用;用 floor() 函数,我们将会产生一个单元整数列表。
#version 320 es
precision mediump float;
out vec4 FragColor;//out代表输出至下一个着色器
uniform vec2 u_resolution;//视窗的分辨率
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;
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));
FragColor = vec4(color, 1.0f);
}
#version 320 es
precision mediump float;
#define PI 3.14159265358979323846;
out vec4 FragColor;//out代表输出至下一个着色器
uniform vec2 u_resolution;//视窗的分辨率
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;
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);
FragColor = vec4(vec3(color), 1.0f);
}
理解如何生成 noise 的好的出发点。
float i = floor(x); // 整数(i 代表 integer)
float f = fract(x); // 小数(f 代表 fraction)
y = rand(i);
y = mix(rand(i), rand(i + 1.0), f);
y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
#version 320 es
precision mediump float;
out vec4 FragColor;//out代表输出至下一个着色器
uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量
// 2D Random
float random (in vec2 st) {
return fract(sin(dot(st.xy,
vec2(12.9898, 78.233)))
* 43758.5453123);
}
// 2D Noise based on Morgan McGuire @morgan3d
// https://www.shadertoy.com/view/4dS3Wd
float noise (in vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
// Four corners in 2D of a tile
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));
// Smooth Interpolation
// Cubic Hermine Curve. Same as SmoothStep()
vec2 u = f*f*(3.0-2.0*f);
// u = smoothstep(0.,1.,f);
// Mix 4 coorners percentages
return mix(a, b, u.x) +
(c - a)* u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
void main() {
vec2 st = gl_FragCoord.xy / u_resolution;
// Scale the coordinate system to see
// some noise in action
vec2 pos = vec2(st*20.0);
// Use the noise function
float n = noise(pos);
FragColor = vec4(vec3(n), 1.0f);
}
Noise 算法的设计初衷是将难以言说的自然质感转化成数字图像。在目前我们看到的一维和二维的实践中,都是在random values(随机值)之间插值,所以它们才被叫做 Value Noise,但是还有很多很多获取 noise 的方法
value noise 看起来非常“块状”。为了消除这种块状的效果,在 1985 年 Ken Perlin 开发了另一种 noise 算法 Gradient Noise。Ken 解决了如何插入随机的 gradients(梯度、渐变)而不是一个固定值。这些梯度值来自于一个二维的随机函数,返回一个方向(vec2
格式的向量),而不仅是一个值(float
格式)。
#version 320 es
precision mediump float;
out vec4 FragColor;//out代表输出至下一个着色器
uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量
vec2 random2(vec2 st){
st = vec2(dot(st, vec2(127.1, 311.7)),
dot(st, vec2(269.5, 183.3)));
return -1.0 + 2.0*fract(sin(st)*43758.5453123);
}
// Gradient Noise by Inigo Quilez - iq/2013
// https://www.shadertoy.com/view/XdXGW8
float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
vec2 u = f*f*(3.0-2.0*f);
return mix(mix(dot(random2(i + vec2(0.0, 0.0)), f - vec2(0.0, 0.0)),
dot(random2(i + vec2(1.0, 0.0)), f - vec2(1.0, 0.0)), u.x),
mix(dot(random2(i + vec2(0.0, 1.0)), f - vec2(0.0, 1.0)),
dot(random2(i + vec2(1.0, 1.0)), f - vec2(1.0, 1.0)), u.x), u.y);
}
void main() {
vec2 st = gl_FragCoord.xy / u_resolution;
st.x *= u_resolution.x/u_resolution.y;
vec3 color = vec3(0.0);
vec2 pos = vec2(st*10.0);
color = vec3(noise(pos)*.5+.5);
FragColor = vec4(color, 1.0f);
}
#version 320 es
precision mediump float;
out vec4 FragColor;//out代表输出至下一个着色器
uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量
float random (in vec2 st) {
return fract(sin(dot(st.xy,
vec2(12.9898, 78.233)))
* 43758.5453123);
}
// Value noise by Inigo Quilez - iq/2013
// https://www.shadertoy.com/view/lsf3WH
float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
vec2 u = f*f*(3.0-2.0*f);
return mix(mix(random(i + vec2(0.0, 0.0)),
random(i + vec2(1.0, 0.0)), u.x),
mix(random(i + vec2(0.0, 1.0)),
random(i + vec2(1.0, 1.0)), u.x), u.y);
}
mat2 rotate2d(float angle){
return mat2(cos(angle), -sin(angle),
sin(angle), cos(angle));
}
float lines(in vec2 pos, float b){
float scale = 10.0;
pos *= scale;
return smoothstep(0.0,
.5+b*.5,
abs((sin(pos.x*3.1415)+b*2.0))*.5);
}
void main() {
vec2 st = gl_FragCoord.xy / u_resolution;
st.x *= u_resolution.x/u_resolution.y;
vec3 color = vec3(0.0);
vec2 pos = st.yx*vec2(10., 3.);
float pattern = pos.x;
// Add noise
pos = rotate2d(noise(pos)) * pos;
// Draw lines
pattern = lines(pos, .5);
color = vec3(pattern);
FragColor = vec4(color, 1.0f);
}
#version 320 es
precision mediump float;
out vec4 FragColor;//out代表输出至下一个着色器
uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量
vec2 random2(vec2 st){
st = vec2(dot(st, vec2(127.1, 311.7)),
dot(st, vec2(269.5, 183.3)));
return -1.0 + 2.0*fract(sin(st)*43758.5453123);
}
// Gradient Noise by Inigo Quilez - iq/2013
// https://www.shadertoy.com/view/XdXGW8
float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
vec2 u = f*f*(3.0-2.0*f);
return mix(mix(dot(random2(i + vec2(0.0, 0.0)), f - vec2(0.0, 0.0)),
dot(random2(i + vec2(1.0, 0.0)), f - vec2(1.0, 0.0)), u.x),
mix(dot(random2(i + vec2(0.0, 1.0)), f - vec2(0.0, 1.0)),
dot(random2(i + vec2(1.0, 1.0)), f - vec2(1.0, 1.0)), u.x), u.y);
}
void main() {
vec2 st = gl_FragCoord.xy / u_resolution;
st.x *= u_resolution.x/u_resolution.y;
vec3 color = vec3(0.0);
float t = 1.0;
// Uncomment to animate
t = abs(1.0-sin(u_time*.1))*5.;
// Comment and uncomment the following lines:
st += noise(st*2.)*t;// Animate the coordinate space
color = vec3(1.) * smoothstep(.18, .2, noise(st));// Big black drops
color += smoothstep(.15, .2, noise(st*10.));// Black splatter
color -= smoothstep(.35, .4, noise(st*10.));// Holes on splatter
FragColor = vec4(1.0-color, 1.0f);
}
#version 320 es
precision mediump float;
out vec4 FragColor;//out代表输出至下一个着色器
uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量
vec2 random2(vec2 st){
st = vec2(dot(st, vec2(127.1, 311.7)),
dot(st, vec2(269.5, 183.3)));
return -1.0 + 2.0*fract(sin(st)*43758.5453123);
}
// Gradient Noise by Inigo Quilez - iq/2013
// https://www.shadertoy.com/view/XdXGW8
float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
vec2 u = f*f*(3.0-2.0*f);
return mix(mix(dot(random2(i + vec2(0.0, 0.0)), f - vec2(0.0, 0.0)),
dot(random2(i + vec2(1.0, 0.0)), f - vec2(1.0, 0.0)), u.x),
mix(dot(random2(i + vec2(0.0, 1.0)), f - vec2(0.0, 1.0)),
dot(random2(i + vec2(1.0, 1.0)), f - vec2(1.0, 1.0)), u.x), u.y);
}
mat2 rotate2d(float _angle){
return mat2(cos(_angle), -sin(_angle),
sin(_angle), cos(_angle));
}
float shape(vec2 st, float radius) {
st = vec2(0.5)-st;
float r = length(st)*2.0;
float a = atan(st.y, st.x);
float m = abs(mod(a+u_time*2., 3.14*2.)-3.14)/3.6;
float f = radius;
m += noise(st+u_time*0.1)*.5;
a *= 1.+abs(atan(u_time*0.2))*.1;
a *= 1.+noise(st+u_time*0.1)*0.1;
f += sin(a*50.)*noise(st+u_time*.2)*.1;
f += (sin(a*20.)*.1*pow(m, 2.));
return 1.-smoothstep(f, f+0.007, r);
}
float shapeBorder(vec2 st, float radius, float width) {
return shape(st, radius)-shape(st, radius-width);
}
void main() {
vec2 st = gl_FragCoord.xy / u_resolution;
vec3 color = vec3(1.0) * shapeBorder(st, 0.8, 0.02);
FragColor = vec4(1.0-color, 1.0f);
}
#version 320 es
precision mediump float;
out vec4 FragColor;//out代表输出至下一个着色器
uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec3 permute(vec3 x) { return mod289(((x*34.0)+1.0)*x); }
float snoise(vec2 v) {
const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
-0.577350269189626, // -1.0 + 2.0 * C.x
0.024390243902439);// 1.0 / 41.0
vec2 i = floor(v + dot(v, C.yy));
vec2 x0 = v - i + dot(i, C.xx);
vec2 i1;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
i = mod289(i);// Avoid truncation effects in permutation
vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0))
+ i.x + vec3(0.0, i1.x, 1.0));
vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0);
m = m*m;
m = m*m;
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
void main() {
vec2 st = gl_FragCoord.xy / u_resolution;
st.x *= u_resolution.x/u_resolution.y;
vec3 color = vec3(0.0);
vec2 pos = vec2(st*3.);
float DF = 0.0;
// Add a random position
float a = 0.0;
vec2 vel = vec2(u_time*.1);
DF += snoise(pos+vel)*.25+.25;
// Add a random position
a = snoise(pos*vec2(cos(u_time*0.15), sin(u_time*0.1))*0.1)*3.1415;
vel = vec2(cos(a), sin(a));
DF += snoise(pos+vel)*.25+.25;
color = vec3(smoothstep(.7, .75, fract(DF)));
FragColor = vec4(1.0-color, 1.0f);
}
#version 320 es
precision mediump float;
out vec4 FragColor;//out代表输出至下一个着色器
uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量
#define PI 3.14159265359f;
#define TWO_PI 6.28318530718f;
const float F3 = 0.3333333;
const float G3 = 0.1666667;
// Copyright (c) Patricio Gonzalez Vivo, 2015 - http://patriciogonzalezvivo.com/
// I am the sole copyright owner of this Work.
//
// You cannot host, display, distribute or share this Work in any form,
// including physical and digital. You cannot use this Work in any
// commercial or non-commercial product, website or project. You cannot
// sell this Work and you cannot mint an NFTs of it.
// I share this Work for educational purposes, and you can link to it,
// through an URL, proper attribution and unmodified screenshot, as part
// of your educational material. If these conditions are too restrictive
// please contact me and we'll definitely work it out.
float shape(vec2 st, int N){
st = st*2.-1.;
float a = atan(st.x, st.y)+PI;
float r = (1.0 / float(N)) * TWO_PI;
return cos(floor(0.5+a/r)*r-a)*length(st);
}
float box(vec2 st, vec2 size){
return shape(st*size, 4);
}
float hex(vec2 st, bool a, bool b, bool c, bool d, bool e, bool f){
st = st*vec2(2., 6.);
vec2 fpos = fract(st);
vec2 ipos = floor(st);
if (ipos.x == 1.0) fpos.x = 1.-fpos.x;
if (ipos.y < 1.0){
return a? box(fpos-vec2(0.03, 0.), vec2(1.)) : box(fpos, vec2(0.84, 1.));
} else if (ipos.y < 2.0){
return b? box(fpos-vec2(0.03, 0.), vec2(1.)) : box(fpos, vec2(0.84, 1.));
} else if (ipos.y < 3.0){
return c? box(fpos-vec2(0.03, 0.), vec2(1.)) : box(fpos, vec2(0.84, 1.));
} else if (ipos.y < 4.0){
return d? box(fpos-vec2(0.03, 0.), vec2(1.)) : box(fpos, vec2(0.84, 1.));
} else if (ipos.y < 5.0){
return e? box(fpos-vec2(0.03, 0.), vec2(1.)) : box(fpos, vec2(0.84, 1.));
} else if (ipos.y < 6.0){
return f? box(fpos-vec2(0.03, 0.), vec2(1.)) : box(fpos, vec2(0.84, 1.));
}
return 0.0;
}
float hex(vec2 st, float N){
bool b[6];
float remain = floor(mod(N, 64.));
for (int i = 0; i < 6; i++){
b[i] = mod(remain, 2.)==1.?true:false;
remain = ceil(remain/2.);
}
return hex(st, b[0], b[1], b[2], b[3], b[4], b[5]);
}
vec3 random3(vec3 c) {
float j = 4096.0*sin(dot(c, vec3(17.0, 59.4, 15.0)));
vec3 r;
r.z = fract(512.0*j);
j *= .125;
r.x = fract(512.0*j);
j *= .125;
r.y = fract(512.0*j);
return r-0.5;
}
float snoise(vec3 p) {
vec3 s = floor(p + dot(p, vec3(F3)));
vec3 x = p - s + dot(s, vec3(G3));
vec3 e = step(vec3(0.0), x - x.yzx);
vec3 i1 = e*(1.0 - e.zxy);
vec3 i2 = 1.0 - e.zxy*(1.0 - e);
vec3 x1 = x - i1 + G3;
vec3 x2 = x - i2 + 2.0*G3;
vec3 x3 = x - 1.0 + 3.0*G3;
vec4 w, d;
w.x = dot(x, x);
w.y = dot(x1, x1);
w.z = dot(x2, x2);
w.w = dot(x3, x3);
w = max(0.6 - w, 0.0);
d.x = dot(random3(s), x);
d.y = dot(random3(s + i1), x1);
d.z = dot(random3(s + i2), x2);
d.w = dot(random3(s + 1.0), x3);
w *= w;
w *= w;
d *= w;
return dot(d, vec4(52.0));
}
void main() {
vec2 st = gl_FragCoord.xy / u_resolution;
st.x *= u_resolution.x/u_resolution.y;
vec3 color = vec3(0.0);
float t = u_time*0.5;
float df = 1.0;
df = mix(hex(st,t),hex(st,t+1.),fract(t));
df += snoise(vec3(st*75.,t*0.1))*0.03;
color =mix(vec3(0.),vec3(1.),step(0.7,df));
FragColor = vec4(color, 1.0f);
}
要理解它背后的原理,我们需要从迭代开始思考。你可能已经知道迭代是什么意思:对,就是使用 for
循环。GLSL 的 for
循环中,只有一个需要注意的:我们检查循环是否继续的次数必须是一个常数(const
). 所以,没有动态循环——迭代的次数必须是固定的。
网格噪声基于距离场,这里的距离是指到一个特征点集最近的点的距离。比如说我们要写一个 4 个特征点的距离场,我们应该做什么呢?对每一个像素,计算它到最近的特征点的距离。也就是说,我们需要遍历所有 4 个特征点,计算他们到当前像素点的距离,并把最近的那个距离存下来。
// A variable to store the closest distance to a point
float min_dist = 100.;
min_dist = min(min_dist, distance(st, point_a));
min_dist = min(min_dist, distance(st, point_b));
min_dist = min(min_dist, distance(st, point_c));
min_dist = min(min_dist, distance(st, point_d));
用一个 for
循环遍历特征点集的数组,用一个 min() 函数来获得最小距离