Book of Shaders 03 - 学习随机与噪声生成算法
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 - 学习随机与噪声生成算法的更多相关文章
- 【Unity Shaders】学习笔记——SurfaceShader(十一)光照模型
[Unity Shaders]学习笔记——SurfaceShader(十一)光照模型 转载请注明出处:http://www.cnblogs.com/-867259206/p/5664792.html ...
- 【Unity Shaders】学习笔记——SurfaceShader(十)镜面反射
[Unity Shaders]学习笔记——SurfaceShader(十)镜面反射 如果你想从零开始学习Unity Shader,那么你可以看看本系列的文章入门,你只需要稍微有点编程的概念就可以. 水 ...
- 【Unity Shaders】学习笔记——SurfaceShader(九)Cubemap
[Unity Shaders]学习笔记——SurfaceShader(九)Cubemap 如果你想从零开始学习Unity Shader,那么你可以看看本系列的文章入门,你只需要稍微有点编程的概念就可以 ...
- 【Unity Shaders】学习笔记——SurfaceShader(八)生成立方图
[Unity Shaders]学习笔记——SurfaceShader(八)生成立方图 转载请注明出处:http://www.cnblogs.com/-867259206/p/5630261.html ...
- 【Unity Shaders】学习笔记——SurfaceShader(七)法线贴图
[Unity Shaders]学习笔记——SurfaceShader(七)法线贴图 转载请注明出处:http://www.cnblogs.com/-867259206/p/5627565.html 写 ...
- 【Unity Shaders】学习笔记——SurfaceShader(六)混合纹理
[Unity Shaders]学习笔记——SurfaceShader(六)混合纹理 转载请注明出处:http://www.cnblogs.com/-867259206/p/5619810.html 写 ...
- 【Unity Shaders】学习笔记——SurfaceShader(五)让纹理动起来
[Unity Shaders]学习笔记——SurfaceShader(五)让纹理动起来 转载请注明出处:http://www.cnblogs.com/-867259206/p/5611222.html ...
- 【Unity Shaders】学习笔记——SurfaceShader(四)用纹理改善漫反射
[Unity Shaders]学习笔记——SurfaceShader(四)用纹理改善漫反射 转载请注明出处:http://www.cnblogs.com/-867259206/p/5603368.ht ...
- 【Unity Shaders】学习笔记——SurfaceShader(三)BasicDiffuse和HalfLambert
[Unity Shaders]学习笔记——SurfaceShader(三)BasicDiffuse和HalfLambert 转载请注明出处:http://www.cnblogs.com/-867259 ...
随机推荐
- 新手学习seo写原创文章的方法
http://www.wocaoseo.com/thread-102-1-1.html 前两天都是写自己公司克拉玛依电信公司年终活动和总结的事,今天继续学习seo技术,其实说难也难说容易也容易,关键的 ...
- 基于Appium的UI自动化测试
为什么需要UI自动化测试 移动端APP是一个复杂的系统,不同功能之间耦合性很强,很难仅通过单元测试保障整体功能.UI测试是移动应用开发中重要的一环,但是执行速度较慢,有很多重复工作量,为了减少这些工作 ...
- 常用的android弹出对话框 几乎包含了所有(1)
我们在平时做开发的时候,免不了会用到各种各样的对话框,相信有过其他平台开发经验的朋友都会知道,大部分的平台都只提供了几个最简单的实现,如果我们想实现自己特定需求的对话框,大家可能首先会想到,通过继承等 ...
- vue问题整理
生命周期面试题 1.什么是 vue 生命周期 vue 实例从创建到销毁的过程就是生命周期. 也就是从开始创建.初始化数据.编译模板.挂在 dom -> 渲染.更新 -> 渲染.卸载等一系列 ...
- 18 vue 动态路由传参
params形式 http://192.168.1.100:8080/#/infoDetailed/231 //定义路由{ path: "/infoDetailed/:newsId" ...
- Oracle重做日志和日志挖掘
重做日志-Redo log 首先给出参考资料: 1.Oracle官网-Managing the Redo Log 为什么需要redo log 内存中数据修改后,不必立即更新到磁盘---效率 由日志完成 ...
- Springboot中WebMvcConfigurer接口详解
Springboot 使用越来越多,企业的基本框架,到Springcloud分布式,可以说无论面试还是平常技术学习,一说到spring几乎就就代替了Java,可以说spring,springboot的 ...
- Vue和d3.js(v4)力导向图force结合使用,v3版本升级v4【一】
前段时间因为参与项目涉密,所以一直没有更新博客,有些博友给我私信或者留言要部分博文的源码,因为我的电脑更换,demo的源码没有备份 所以无法提供.大家可针对具体问题问我,有空我定会回复的.另外转发文章 ...
- Bootstrap学习第二天轮播插件
Bootstrap不仅提供了整套的样式,还提供了一系列插件,帮助开发者构建更受欢迎的网站. Bootstrap的轮播插件用起来很简单,代码如下. <!DOCTYPE html> <h ...
- Oracle sqlplus中退格键、DEL键、上下左右键无法使用乱码问题
功能描述:Oracle sqlplus中退格键.DEL键.上下左右键无法使用乱码 1.安装readline-8.0 ①下载readline-8.0.tar.gz文件,百度网盘下载路径: https:/ ...