0x00 随机

我们不能预测天空中乌云的样子,因为它的纹理总是具有不可预测性。这种不可预测性叫做随机 (random)。

在计算机图形学中,我们通常使用随机来模拟自然界中的噪声。如何获得一个随机值呢,让我们从下面的函数入手:

y = fract(sin(x) * 10000.0);

这里,sin(x) 乘以了一个很大的数:10000.0,使得 x 值的一点微小变化也会引起计算结果的剧烈变动。同时,根据 sin 的图形我们可以知道,在一个小范围内,sin 函数的变化率总是不同的。结合这两点,再使用 fract() 函数提取整个表达式的小数部分,这样就能得到的一系列呈现出随机状态的值。

我们可以用这个函数来生成随机值。但是,这里的随机是伪随机的,因为对于相同的 x 计算的结果都相同。

0x01 噪声

上面函数得到的随机值是在一个维度变化的。为了将其应用到二维中,还需要将二维的坐标值转化为一维的浮点数。这一步可以使用点乘来实现。

y = fract(sin(dot(st, vec2(12.9898, 78.233))) * 43758.5453);

你可能注意到上面的表达式中有三个很奇怪的数字。有什么特殊含义吗?没有!这三个数字是我从其他地方复制粘贴来的。事实上,这三个数并不是固定的。也可以对其进行修改,从而得到不同的随机效果。

将 uv 坐标代入上面的表达式中,并将返回的结果赋值给该坐标点的颜色,就能得到一张噪声图了。这样生成的噪声并不具备连续性,点与点之间的值存在着很大的差异。

这样的噪声被称作白噪声,完整代码如下:

#ifdef GL_ES
precision mediump float;
#endif uniform vec2 u_resolution; float random(vec2 pos) {
return fract(sin(dot(pos.xy, vec2(12.9898,78.233)))* 43758.5453123);
} void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
float f = random(st);
gl_FragColor = vec4(vec3(f), 1.0);
}

0x02 平滑

和白噪声相比,自然界中的噪声并非如此杂乱无序。比如本文开头的乌云,就没有白噪声这样的颗粒感。因此,我们需要进一步加工,让噪声图变得更平滑,点与点之间的过渡更自然。

一种通常的做法是,对于任何一个点,都求它所在的单位格子的四个顶点的值。再使用平滑插值函数:\(f(x) = 6x^5 - 15x^4 + 10x^3\) 对这四个点的值进行插值,将插值的结果赋给原先的这个点。

对于原本归一化的 uv 坐标来说,这样会得到一幅完全平滑过渡的图,少了一些随机性。所以在代码中,还需要将 uv 的范围扩大,这样就能得到更多变化。

这样的噪声被称作 Value Noise,具体代码如下:

#ifdef GL_ES
precision mediump float;
#endif uniform vec2 u_resolution; float random(vec2 pos) {
return fract(sin(dot(pos.xy, vec2(12.9898,78.233)))* 43758.5453123);
} vec2 smooth(vec2 t) {
return t * t * t * (t * (6.0 * t - 15.0) + 10.0);
} float noise(in vec2 st) {
// vec2 i = floor(st);
vec2 s = smooth(fract(st)); // float bl = random(i);
// float br = random(i + vec2(1.0, 0.0));
// float tl = random(i + vec2(0.0, 1.0));
// float tr = random(i + vec2(1.0, 1.0)); float tl = random(vec2(floor(st.x), ceil(st.y)));
float tr = random(vec2(ceil(st.x), ceil(st.y)));
float bl = random(vec2(floor(st.x), floor(st.y)));
float br = random(vec2(ceil(st.x), floor(st.y))); float t = mix(tl, tr, s.x);
float b = mix(bl, br, s.x);
return mix(b, t, s.y);
} void main() {
vec2 st = gl_FragCoord.xy / u_resolution.xy;
st *= 10.0;
// st *= 100.0;
float n = noise(st);
gl_FragColor = vec4(vec3(n), 1.0);
}

上面代码中注释的部分来自 The Book of Shader 给出的例子,该例子对于格子的边界如 y = 3.0 处,将会取到 floor(3.0) + 1.0 = 4.0 的值,如果两个点的噪声值差距较大,则会造成格子间出现明显的分割线。因此,改用 ceil 来计算原点所处格子右边和上边的边界值,可以保证其位于格子中。

另外,不妨试着将 st 乘上更大的系数,比如再乘上 100。可以发现,当 st 来到一个更大值的时候,噪声图又会重新变成白噪声。

0x03 柏林

Perlin Noise (柏林噪声) 是由 Ken Perlin 发明的自然噪声生成算法。简单来说,将空间划分成大小相同的格子。对于一个输入点 (x, y),取该点所在格子的每个顶点的梯度向量与顶点到该点的方向向量的点乘,作为一个顶点对于该点的贡献值。最后使用类似 Value Noise 的插值方式计算出输入点的值。

下图中的红色向量即是每个顶点的梯度,绿色向量是四个顶点到输入位置的方向向量。对于梯度,可以使用预先计算的梯度表,也可以使用随机函数计算出一个随机的二维向量。

为了简单,这里使用随机方法生成顶点的梯度。完整代码如下:

#ifdef GL_ES
precision mediump float;
#endif uniform vec2 u_resolution; vec2 random2(vec2 pos) {
vec2 vec = vec2(dot(pos, vec2(12.9898,78.233)));
return -1.0 + 2.0 * fract(sin(vec) * 43758.5453123);
} float grad(vec2 vert, vec2 pos) {
return dot(random2(vert), pos - vert);
} vec2 smooth(vec2 t) {
return t * t * t * (t * (6.0 * t - 15.0) + 10.0);
} float perlinNoise(in vec2 st) {
vec2 s = smooth(fract(st)); float tl = grad(vec2(floor(st.x), ceil(st.y)), st);
float tr = grad(vec2(ceil(st.x), ceil(st.y)), st);
float bl = grad(vec2(floor(st.x), floor(st.y)), st);
float br = grad(vec2(ceil(st.x), floor(st.y)), st); float t = mix(tl, tr, s.x);
float b = mix(bl, br, s.x);
return mix(b, t, s.y);
} void main() {
vec2 st = gl_FragCoord.xy / u_resolution.xy;
st *= 10.0;
float n = perlinNoise(st) + 0.5;
gl_FragColor = vec4(vec3(n), 1.0);
}

最终生成的噪声图效果如下。

参考资料:

Book of Shaders 03 - 学习随机与噪声生成算法的更多相关文章

  1. 【Unity Shaders】学习笔记——SurfaceShader(十一)光照模型

    [Unity Shaders]学习笔记——SurfaceShader(十一)光照模型 转载请注明出处:http://www.cnblogs.com/-867259206/p/5664792.html ...

  2. 【Unity Shaders】学习笔记——SurfaceShader(十)镜面反射

    [Unity Shaders]学习笔记——SurfaceShader(十)镜面反射 如果你想从零开始学习Unity Shader,那么你可以看看本系列的文章入门,你只需要稍微有点编程的概念就可以. 水 ...

  3. 【Unity Shaders】学习笔记——SurfaceShader(九)Cubemap

    [Unity Shaders]学习笔记——SurfaceShader(九)Cubemap 如果你想从零开始学习Unity Shader,那么你可以看看本系列的文章入门,你只需要稍微有点编程的概念就可以 ...

  4. 【Unity Shaders】学习笔记——SurfaceShader(八)生成立方图

    [Unity Shaders]学习笔记——SurfaceShader(八)生成立方图 转载请注明出处:http://www.cnblogs.com/-867259206/p/5630261.html ...

  5. 【Unity Shaders】学习笔记——SurfaceShader(七)法线贴图

    [Unity Shaders]学习笔记——SurfaceShader(七)法线贴图 转载请注明出处:http://www.cnblogs.com/-867259206/p/5627565.html 写 ...

  6. 【Unity Shaders】学习笔记——SurfaceShader(六)混合纹理

    [Unity Shaders]学习笔记——SurfaceShader(六)混合纹理 转载请注明出处:http://www.cnblogs.com/-867259206/p/5619810.html 写 ...

  7. 【Unity Shaders】学习笔记——SurfaceShader(五)让纹理动起来

    [Unity Shaders]学习笔记——SurfaceShader(五)让纹理动起来 转载请注明出处:http://www.cnblogs.com/-867259206/p/5611222.html ...

  8. 【Unity Shaders】学习笔记——SurfaceShader(四)用纹理改善漫反射

    [Unity Shaders]学习笔记——SurfaceShader(四)用纹理改善漫反射 转载请注明出处:http://www.cnblogs.com/-867259206/p/5603368.ht ...

  9. 【Unity Shaders】学习笔记——SurfaceShader(三)BasicDiffuse和HalfLambert

    [Unity Shaders]学习笔记——SurfaceShader(三)BasicDiffuse和HalfLambert 转载请注明出处:http://www.cnblogs.com/-867259 ...

随机推荐

  1. IDEA创建MAVEN项目并使用tomcat启动

    一.开发环境准备 1.JDK1.8,已经配置好环境变量 2.IDEA2019.2,目前稳定版里面个人认为还不错的 3.tomcat服务器,笔者使用的是apache-tomcat-8.5.57 4.使用 ...

  2. 详细分析链表的数据结构的实现过程(Java 实现)

    目录 链表的数据结构的实现过程(Java 实现) 前言 基本概念 链表的基本结构 链表的基本操作的实现 在链表中添加元素 在链表头添加元素 在链表指定位置处添加元素 链表的虚拟头节点 链表的查询和修改 ...

  3. 记一些Python(Pymysql)建表、增删改查等基础操作(小白适用)

    1.读取sql文件创建数据表 有一个形如下图的sql文件,使用python读取文件并在数据库中创建所有的表. 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道 ...

  4. 推荐一款强大的前端CLI命令行工具

    背景 命令行界面交互开源工具 CloudBase CLI 自发布以来,受到了大量开发者的欢迎.近期,我们发布了新鲜打磨的 CloudBase CLI 1.0 Beta,引入了许多利于开发者的新特性,下 ...

  5. APICloud数据云3.0 -- 让后端业务更简单

    近年来,各类移动端应用层出不穷,app.小程序已成为企业业务数字化的必然选择,围绕互联网产品的技术创新与开发者生态,正在历经行业发展的又一次革新. APICloud作为国内领先的移动应用开发平台,一直 ...

  6. webdriver实现简单的窗口切换

    webdriver实现简单的窗口切换,也只能是简单的,因为目前处于学习阶段,复杂的情况现在还没碰到过.之前写过关于一个小demo的总结,就有提到过在新开窗口进行操作的情况,用以下一句就可以搞定了,la ...

  7. MySql密码的问题

    由于长时间没使用过MySql了,也由于之前没有做笔记的习惯,晚上因为MySQL的密码问题导致数据库长时间没连上.纠结了这么久还是决定记录下来,毕竟安装的东西多了,这年头到处都是密码,加上时间一长,很容 ...

  8. Mongos WoW

    http://blog.csdn.net/yuleslie/article/details/7430094 https://github.com/mangostwo/ https://www.getm ...

  9. Linux 操作系统 基础

    root: 管理员 /: 根目录[windows : 计算机] ~: 家目录: 管理员:/root/.. 非管理员:/home/... 命令提示符: [root@localhost abc]#: ro ...

  10. 3. 站在使用层面,Bean Validation这些标准接口你需要烂熟于胸

    乔丹是我听过的篮球之神,科比是我亲眼见过的篮球之神.本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈.MyBatis.JVM.中间件等小而美的专栏供以免 ...