日积月累Shader - 11 噪声

提示

教程例子都可以到下面网址进行运行,不需要另外安装软件环境:
官方提供在线编写shader工具:https://thebookofshaders.com/edit.php
glslsandbox网站:http://glslsandbox.com/
shadertoy网站:https://www.shadertoy.com/

Noise 噪声

我们感知得到浮动在皮肤上的空气,晒在脸上的阳光。世界如此生动而丰富。颜色,质地,声音。当我们走在路上,不免会注意到路面、石头、树木和云朵的表面的样子。

这些纹理的不可预测性可以叫做“random"(随机),但是它们看起来不像是我们之前玩的 random。{真实世界}是如此的丰富而复杂!我们如何才能才能用计算机模拟这些多样的纹理呢?
这就是 Ken Perlin 想要解答的问题。在20世纪80年代早期,他被委任为电影 “Tron”(电子世界争霸战)制作现实中的纹理。为了解决这个问题,他想出了一个优雅的算法,且获得了奥斯卡奖(名副其实)。
下面这个并不是经典的 Perlin noise 算法,但是这是一个理解如何生成 noise 的好的出发点。

float i = floor(x);  // 整数(i 代表 integer)
float f = fract(x);  // 小数(f 代表 fraction)
y = rand(i); //rand() 在之前的章节提过
// y = mix(rand(i), rand(i + 1.0), f);
// y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));

默认生成一个随机数图形


  • 问题是随机数之间没有过度,直接就从另一个随机数变化到了另一个随机数
  • 解决方案:y = mix(rand(i), rand(i + 1.0), f); 将i和i+1之间进行过度,因为每段的百分比就是变量f存储的值,自然可以产生过度


  • 但还是不够平滑,值与值之间的变化不存在平滑过渡。
  • 解决方案:y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f)); 通过这种方式可以得到近似平滑的曲线(真正平滑还要考虑相邻曲线的曲率)


  • 试试把f换成 float u = f * f * (3.0 - 2.0 * f ); 会发现也产生曲线,而且和第三幅图一样,因为smoothstep就是这个公式 🤣

2D Noise

现在我们知道了如何在一维使用 noise,是时候进入二维世界了。在 2D 中,除了在一条线的两点(fract(x) 和 fract(x)+1.0)中插值,我们将在一个平面上的方形的四角(fract(st), fract(st)+vec2(1.,0.), fract(st)+vec2(0.,1.) 和 fract(st)+vec2(1.,1.))中插值。



同样,如果我们想要在三维中使用 noise,就需要在一个立方体的8个角中插值。这个技术的重点在于插入随机值,所以我们叫它 value noise。



就像一维的那个例子,这个插值不是线性的,而是三次方的,它会平滑地在方形网格中插入点。
#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
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.xy;

    // Scale the coordinate system to see
    // some noise in action
    vec2 pos = vec2(st*5.0);

    // Use the noise function
    float n = noise(pos);

    gl_FragColor = vec4(vec2(n),.5, 1.0);
}

我们先把空间大小变成五倍(第 45 行)以便看清栅格间的插值。然后在 noise 函数中我们把空间分成更小的单元。我们把它的整数部分和非整数部分都储存在这个单元里。我们计算整数位置的顶点的坐标,并给每个顶点生成一个随机值(第 23 - 26 行)。最后,在第 35 行用我们之前储存的小数位置的值,在四个顶点的随机值之间插值。

  • 每个被分割的格子,四个顶点以及四条公共边获得的随机值是相同的,可被近似平滑

生成式设计中的 noise 应用

Noise 算法的设计初衷是将难以言说的自然质感转化成数字图像。在目前我们看到的一维和二维的实践中,都是在random values(随机值)之间插值,所以它们才被叫做 Value Noise,但是还有很多很多获取 noise 的方法……


https://thebookofshaders.com/edit.php#11/2d-vnoise.frag

如你所见,value noise 看起来非常“块状”。为了消除这种块状的效果,在 1985 年 Ken Perlin 开发了另一种 noise 算法 Gradient Noise。Ken 解决了如何插入随机的 gradients(梯度、渐变)而不是一个固定值。这些梯度值来自于一个二维的随机函数,返回一个方向(vec2 格式的向量),而不仅是一个值(float格式)。点击下面的图片查看代码,看这个函数是如何运作的。


https://thebookofshaders.com/edit.php#11/2d-gnoise.frag

花一分钟来看看 Inigo Quilez 做的两个例子,注意 value noisegradient noise的区别。

就像一个画家非常了解画上的颜料是如何晕染的,我们越了解 noise 是如何运作的,越能更好地使用 noise。比如,如果我们要用一个二维的 noise 来旋转空间中的直线,我们就可以制作下图的旋涡状效果,看起来就像木头表皮一样。同样地,你可以点击图片查看代码。
https://thebookofshaders.com/edit.php#11/wood.frag

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);
  • 先对顶部和底部进行水平插值, 在对这两条线进行垂直插值, 就得到了连续的扭曲~~ 精神污染,脑壳疼

另一种用 noise 制作有趣的图案的方式是用 distance field(距离场)处理它,用用 第七章提到的招数。

https://thebookofshaders.com/edit.php#11/splatter.frag

    color += smoothstep(.15,.2,noise(st*10.)); // 黑色的泼溅点
    color -= smoothstep(.35,.4,noise(st*10.)); // 泼溅点上的洞

第三种方法是用 noise 函数来变换一个形状。这个也需要我们在第七章学到的技术。
https://thebookofshaders.com/edit.php#11/circleWave-noise.frag

Simplex Noise

对于 Ken Perlin 来说他的算法所取得的成功是远远不够的。他觉得可以更好。在 2001 年的 Siggraph(译者注:Siggraph是由美国计算机协会{计算机图形专业组}组织的计算机图形学顶级年度会议)上,他展示了 “simplex noise”,simplex noise 比之前的算法有如下优化:

  • 它有着更低的计算复杂度和更少乘法计算。
  • 它可以用更少的计算量达到更高的维度。
  • 制造出的 noise 没有明显的人工痕迹。
  • 有着定义得很精巧的连续的 gradients(梯度),可以大大降低计算成本。
  • 特别易于硬件实现。

我们已经知道在二维中他是如何在四个点(正方形的四个角)之间插值的;所以没错你已经猜到了,对于三维(这里有个示例)和四维我们需要插入 8 个和 16 个点。对吧?也就是说对于 N 维你需要插入 2 的 n 次方个点(2^N)。但是 Ken 很聪明地意识到尽管很显然填充屏幕的形状应该是方形,在二维中最简单的形状却是等边三角形。所以他把正方形网格(我们才刚学了怎么用)替换成了单纯形等边三角形的网格。

这时 N 维的形状就只需要 N + 1 个点了。也就是说在二维中少了 1 个点,三维中少了 4 个,四维中则少了 11 个!巨大的提升!
在二维中插值过程和常规的 noise 差不多,通过在一组点之间插值。但是在这种情况下,改用单纯形网格,我们只需要给总共 3 个点插值。

这个单纯形网格是如何制作的?这是另一个聪明绝顶而十分优雅的做法。可以先把常规的四角网格分成两个等腰三角形,然后再把三角形歪斜成等边三角形。

另一个 Simplex Noise 的优化是把三次 Hermite 函数(Cubic Hermite Curve:f(x) = 3x2-2x3,和 smoothstep() 一样)替换成了四次 Hermite 函数( f(x) = 6x5-15x4+10x^3 )。这就使得函数曲线两端更“平”,所以每个格的边缘更加优雅地与另一个衔接。也就是说格子的过渡更加连续。你可以取消下面例子的第二个公式的注释,亲眼看看其中的变化(或者看这个例子)。

// 三次 Hermite 曲线。和 SmoothStep() 一样
y = x*x*(3.0-2.0*x);
// 四次 Hermite 曲线
y = x*x*x*(x*(x*6.-15.)+10.);

所有这些进展汇聚成了算法中的杰作 Simplex Noise。下面是这个算法在 GLSL 中的应用,作者是 Ian McEwan,以这篇论文发表,对于我们的教学而言太复杂了,但你可以点开看看,也许没有你想象得那么晦涩难懂。
https://thebookofshaders.com/edit.php#11/2d-snoise-clear.frag

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容