Merge remote-tracking branch 'upstream/master'

pull/190/head
Sergey Karchevsky 7 years ago
commit 6b830bd008

@ -19,7 +19,7 @@ uniform float u_time; // 时间(加载后的秒数)
```glsl
uniform vec3 iResolution; // 视口分辨率(以像素计)
uniform vec4 iMouse; // 鼠标坐标 xy 当前位置, zw 点击位置
uniform float iGlobalTime; // shader 运行时间(以秒计)
uniform float iTime; // shader 运行时间(以秒计)
```
好了说的足够多了,我们来看看实际操作中的 uniform 吧。在下面的代码中我们使用 ```u_time``` 加上一个 sin 函数,来展示图中红色的动态变化。

@ -19,7 +19,7 @@ Man kann sich Uniforms als eine Brücke zwischen der CPU und der GPU vorstellen.
```glsl
uniform vec3 iResolution; // Groesse der Malflaeche
uniform vec4 iMouse; // Mausposition
uniform float iGlobalTime; // Zeit seit dem Start
uniform float iTime; // Zeit seit dem Start
```
Aber nun genug geredet. Lass uns die Uniforms in Aktion betrachten. Der folgende Programmcode nutzt ```u_time``` - die Anzahl der Sekunden, seitdem der Shader gestartet wurde - in Verbindung mit einer Sinus-Funktion, um die Intensität der Rotfärbung der Malfläche pulsieren zu lassen.

@ -19,7 +19,7 @@ Podemos imaginar que los uniforms son como pequeños puentes entra la CPU y la G
```glsl
uniform vec3 iResolution; // viewport resolution (in pixels)
uniform vec4 iMouse; // mouse pixel coords. xy: current, zw: click
uniform float iGlobalTime; // shader playback time (in seconds)
uniform float iTime; // shader playback time (in seconds)
```
Ya hemos hablado mucho, vamos a ver los uniforms en acción. En el código siguiente usamos ```u_time``` - el número de segundos desde que el shader comenzó a ejecutarse - junto con una función del seno para animar en transición la cantidad de rojo en la pantalla.

@ -28,7 +28,7 @@ Par exemple [ShaderToy.com](https://www.shadertoy.com/) utilise les mêmes unifo
```glsl
uniform vec3 iResolution; // taille du canvas (en pixels)
uniform vec4 iMouse; // position de la souris. xy: courant, zw: au click
uniform float iGlobalTime; // temps écoulé depuis le lancement du shader (en secondes)
uniform float iTime; // temps écoulé depuis le lancement du shader (en secondes)
```
Notez qu'ils utilisent un `i` au lieu de notre `u_`.

@ -19,7 +19,7 @@ uniform float u_time; // tempo in secondi da quando lo shader è iniziato
```glsl
uniform vec3 iResolution; // dimensione del Canvas (in pixels)
uniform vec4 iMouse; // posizione del mouse in pixels. xy: corrente, zw: click
uniform float iGlobalTime; // tempo (in secondi) da quando lo shader è iniziato
uniform float iTime; // tempo (in secondi) da quando lo shader è iniziato
```
Ma ora basta chiacchiere, vediamo gli uniforms in azione. Nel seguente codice utilizziamo ```u_time``` - il numero di secondi da quando lo shader è iniziato - insieme ad una funzione seno per animare con una transizione la quantità di rosso sullo schermo.

@ -22,7 +22,7 @@ uniform変数はCPUとGPUの間の小さな架け橋だと考えることがで
```glsl
uniform vec3 iResolution; // viewport resolution (in pixels)
uniform vec4 iMouse; // mouse pixel coords. xy: current, zw: click
uniform float iGlobalTime; // shader playback time (in seconds)
uniform float iTime; // shader playback time (in seconds)
```
訳注uniform変数は開発者が自由に名前を決めることができ、上で挙げられている用途以外にも自由に使うことができます。C、JavascriptなどのCPUで走るプログラムからは、シェーダー側で決めた変数の名前を指定して値を渡すことができます

@ -19,7 +19,7 @@ uniform float u_time; // Time in seconds since load
```glsl
uniform vec3 iResolution; // viewport resolution (in pixels)
uniform vec4 iMouse; // mouse pixel coords. xy: current, zw: click
uniform float iGlobalTime; // shader playback time (in seconds)
uniform float iTime; // shader playback time (in seconds)
```
거두철미하고, 유니폼이 실제로 구현되는 부분을 보자. 아래 코드에서 ```u_time``` - 쉐이더가 구동된후 초 - 를 sine 함수에 인자로 넣어, 빨간색값을 조절하고 있는것을 볼수 있다.

@ -19,7 +19,7 @@ You can picture the uniforms like little bridges between the CPU and the GPU. Th
```glsl
uniform vec3 iResolution; // viewport resolution (in pixels)
uniform vec4 iMouse; // mouse pixel coords. xy: current, zw: click
uniform float iGlobalTime; // shader playback time (in seconds)
uniform float iTime; // shader playback time (in seconds)
```
Enough talking, let's see the uniforms in action. In the following code we use `u_time` - the number of seconds since the shader started running - together with a sine function to animate the transition of the amount of red in the billboard.

@ -1,6 +1,6 @@
## Running your shader
As part of the construction of this book and my art practice I made an ecosystem of tools to create, display, share and curate shaders. This tools works consistently across Linux Desktops, MacOS, [Raspberry Pi](https://www.raspberrypi.org/) and browsers without the need of changing your code.
As part of the construction of this book and my art practice I made an ecosystem of tools to create, display, share and curate shaders. These tools work consistently across Linux Desktops, MacOS, [Raspberry Pi](https://www.raspberrypi.org/) and browsers without the need of changing your code.
**Display**: all live examples in this book are displayed using [glslCanvas](https://github.com/patriciogonzalezvivo/glslCanvas) which makes the process of running standalone shader incredible easy.

@ -27,7 +27,7 @@
GLSLには多くのネイティブ関数が用意されており、[```pow()```](../glossary/?search=pow) はその中の1つです。ほとんどのネイティブ関数はハードウェアのレベルで高速に処理されるので、適切に使えばより速いコードを書くことができます。
19行目の指数関数を [```exp(st.x) - 1.0```](../glossary/?search=exp), [```log(st.x - 1.0)```](../glossary/?search=log), [```sqrt(st.x)```](../glossary/?search=sqrt) など他の関数で置き換えてみましょう。
22行目のべき関数を [```exp(st.x) - 1.0```](../glossary/?search=exp), [```log(st.x - 1.0)```](../glossary/?search=log), [```sqrt(st.x)```](../glossary/?search=sqrt) など他の関数で置き換えてみましょう。
### StepとSmoothstep

@ -67,7 +67,7 @@ To determine if our thread is in an odd or even row, we are going to use [```mod
// y = mod(x,2.0) < 1.0 ? 0. : 1. ;
// y = step(1.0,mod(x,2.0));"></div>
As you can see we can use a [ternary operator](https://en.wikipedia.org/wiki/%3F:) to check if the [```mod()```](../glossary/?search=mod) of ```2.0``` is under ```1.0``` (second line) or similarly we can use a [```step()```](../glossary/?search=step) function which does that the same operation, but faster. Why? Although is hard to know how each graphic card optimizes and compiles the code, it is safe to assume that built-in functions are faster than non-built-in ones. Everytime you can use a built-in function, use it!
As you can see we can use a [ternary operator](https://en.wikipedia.org/wiki/%3F:) to check if the [```mod()```](../glossary/?search=mod) of ```2.0``` is under ```1.0``` (second line) or similarly we can use a [```step()```](../glossary/?search=step) function which does the same operation, but faster. Why? Although is hard to know how each graphic card optimizes and compiles the code, it is safe to assume that built-in functions are faster than non-built-in ones. Everytime you can use a built-in function, use it!
So now that we have our odd number formula we can apply an offset to the odd rows to give a *brick* effect to our tiles. Line 14 of the following code is where we are using the function to "detect" odd rows and give them a half-unit offset on ```x```. Note that for even rows, the result of our function is ```0.0```, and multiplying ```0.0``` by the offset of ```0.5``` gives an offset of ```0.0```. But on odd rows we multiply the result of our function, ```1.0```, by the offset of ```0.5```, which moves the ```x``` axis of the coordinate system by ```0.5```.

@ -30,9 +30,9 @@ Randomness is a maximal expression of entropy. How can we generate randomness in
//y = pow(rand(x),5.);"></div>
不久前 [Pixelero](https://pixelero.wordpress.com) 出版了 [interesting article about random distribution](https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/)。 我添加了些前几张图所有的函数来供你试验,看看如何改变分布。取消函数的注释,看看发生什么变化。
不久前 [Pixelero](https://pixelero.wordpress.com) 发表了一篇[关于随机分布的有意思的文章](https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/)。 我添加了些前几张图所有的函数来供你试验,看看如何改变分布。取消函数的注释,看看发生什么变化。
如果你读下 [Pixelero's article](https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/),一定谨记我们用的 ```rand()``` 是确定性随机,也被称作是伪随机。这就意味着, 就 ```rand(1.)``` 为例,总会返回相同的值。[Pixelero](https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/) 用 ActionSript 函数做了些参考,```Math.random()```,一个非确定性随机;每次调用都返回不同的值。
如果你读下 [Pixelero 的文章](https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/),一定谨记我们用的 ```rand()``` 是确定性随机,也被称作是伪随机。这就意味着, 就 ```rand(1.)``` 为例,总会返回相同的值。[Pixelero](https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/) 用 ActionSript 函数做了些参考,```Math.random()```,一个非确定性随机;每次调用都返回不同的值。
## 2D 随机
@ -84,10 +84,10 @@ Randomness is a maximal expression of entropy. How can we generate randomness in
<a href="../edit.php#10/ikeda-03.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-03.frag" width="520px" height="200px"></canvas></a>
* 创其他有趣的效果。
* 创其他有趣的效果。
<a href="../edit.php#10/ikeda-04.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-04.frag" width="520px" height="200px"></canvas></a>
优雅的使用随机是困难的,尤其是你希望创作自然的模拟。随机仅仅是过于混乱了,真实生活中很少有东西看上去如此 ```random()```。如果观察(玻璃床上)雨滴的肌理或是股票的曲线 — 这两个都挺随机的 — 但是他们和我们在章开始的随机图案看起来不是同一对爹妈生的。原因?嗯,随机值是没有因果关系的,而大多数自然图案(肌理)都对前一个状态有所记忆(基于前一个状态)。
完美地掌握随机之美是困难的,尤其是你想要让作品看起来很自然。随机仅仅是过于混乱了,真实生活中很少有东西看上去如此 ```random()```。如果观察(玻璃床上)雨滴的肌理或是股票的曲线——这两个都挺随机的——但他们和本章开始的随机图案看起来不是同一对爹妈生的。原因?嗯,随机值之间没有什么相关性,而大多数自然图案(肌理)都对前一个状态有所记忆(基于前一个状态)。
下一章我们将学习噪,一种光滑 和 *自然的* 创作计算机混沌的方式。
下一章我们将学习噪,一种光滑 和 *自然的* 创作计算机混沌的方式。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 MiB

After

Width:  |  Height:  |  Size: 2.5 MiB

@ -0,0 +1,186 @@
![](dragonfly.jpg)
## 网格噪声Cellular Noise
1996 年,在原始的 Perlin Noise 发布六年后Perlin 的 Simplex Noise 发布五年前,[Steven Worley 写了一篇名为“A Cellular Texture Basis Function”的论文](http://www.rhythmiccanvas.com/research/papers/worley.pdf)。在这篇论文里,他描述了一种现在被广泛使用的程序化纹理技术。
要理解它背后的原理,我们需要从**迭代**开始思考。你可能已经知道迭代是什么意思:对,就是使用 ```for``` 循环。GLSL 的 ```for``` 循环中,只有一个需要注意的:我们检查循环是否继续的次数必须是一个常数(```const```. 所以,没有动态循环——迭代的次数必须是固定的。
网格噪声基于距离场,这里的距离是指到一个特征点集最近的点的距离。比如说我们要写一个 4 个特征点的距离场,我们应该做什么呢?**对每一个像素,计算它到最近的特征点的距离**。也就是说,我们需要遍历所有 4 个特征点,计算他们到当前像素点的距离,并把最近的那个距离存下来。
```glsl
float min_dist = 100.; // A variable to store the closest distance to a point
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));
```
![](cell-00.png)
这种做法不是很优雅,但至少行得通。现在让我们用数组和 ```for``` 循环重写。
```glsl
float m_dist = 100.; // minimum distance
for (int i = 0; i < TOTAL_POINTS; i++) {
float dist = distance(st, points[i]);
m_dist = min(m_dist, dist);
}
```
注意看我们用一个 ```for``` 循环遍历特征点集的数组,用一个 [```min()```](../glossary/?search=min) 函数来获得最小距离。下面是以上想法的简要的实现:
<div class="codeAndCanvas" data="cellnoise-00.frag"></div>
上面的代码中,其中一个特征点分配给了鼠标位置。把鼠标放上去玩一玩,你可以更直观地了解上面的代码是如何运行的。然后试试:
- 你可以让其余的几个特征点也动起来吗?
- 在读完[关于形状的章节](../07/?lan=ch)后,想象一些关于距离场的有意思的用法。
- 如果你要往距离场里添加更多的特征点怎么办?如果我们想动态地添加减少特征点数怎么办?
### 平铺和迭代
你可能注意到 GLSL 对 ```for``` 循环和 *数组* 似乎不太友好。如前所说,循环不接受动态的迭代次数。还有,遍历很多实例会显著地降低着色器的性能。这意味着我们不能把这个方法用在很大的特征点集上。我们需要寻找另一个策略,一个能利用 GPU 并行架构优势的策略。
![](cell-01.png)
解决这个问题的一个方法是把空间分割成网格。并不需要计算每一个像素点到每一个特征点的距离对吧已经知道每个像素点是在自己的线程中运行我们可以把空间分割成网格cells每个网格对应一个特征点。另外为避免网格交界区域的偏差我们需要计算像素点到相邻网格中的特征点的距离。这就是 [Steven Worley 的论文](http://www.rhythmiccanvas.com/research/papers/worley.pdf)中的主要思想。最后,每个像素点只需要计算到九个特征点的距离:他所在的网格的特征点和相邻的八个网格的特征点。我们已经在[图案](../09/?lan=ch)[随机](../10/?lan=ch)和[噪声](../11/?lan=ch)这些章节介绍了如何把空间分割成网格,希望你已经熟悉这项技术。
```glsl
// Scale
st *= 3.;
// Tile the space
vec2 i_st = floor(st);
vec2 f_st = fract(st);
```
那么,计划是什么呢?我们将使用网格坐标(存储在整数坐标 ```i_st``` 中)来构造特征点的随机位置。```random2f``` 函数接受一个 ```vec2``` 类型参数,返回给我们一个 ```vec2``` 类型的随机位置。所以,在每个网格内,我们有一个特征点在随机位置上。
```glsl
vec2 point = random2(i_st);
```
网格内的每个像素点(存储在浮点坐标 ```f_st``` 中)都会计算它到那个随机点的距离。
```glsl
vec2 diff = point - f_st;
float dist = length(diff);
```
结果看起来就像这样:
<a href="../edit.php#12/cellnoise-01.frag"><img src="cellnoise.png" width="520px" height="200px"></img></a>
我们还需要计算像素点到相邻网格中随机点的距离,而不只是当前的网格。我们需要 **遍历** 所有相邻网格。不是所有网格,仅仅是那些和当前网格相邻的网格。从网格坐标来说,就是 ```x``` 坐标从 ```-1``` (左)到 ```1``` (右), ```y``` 坐标从 ```-1``` (下)到 ```1``` (上)。一个 9 个网格的 3x3 区域可以用两个 ```for``` 循环遍历:
```glsl
for (int y= -1; y <= 1; y++) {
for (int x= -1; x <= 1; x++) {
// Neighbor place in the grid
vec2 neighbor = vec2(float(x),float(y));
...
}
}
```
![](cell-02.png)
现在,我们可以在双 ```for``` 循环中计算相邻网格中随机点的位置,只需要加上相邻网格对当前网格的偏移量。
```glsl
...
// Random position from current + neighbor place in the grid
vec2 point = random2(i_st + neighbor);
...
```
剩下的部分就是计算像素点到那个随机点的距离,并把最近的距离存到变量 ```m_dist```minimum distance里面.
```glsl
...
vec2 diff = neighbor + point - f_st;
// Distance to the point
float dist = length(diff);
// Keep the closer distance
m_dist = min(m_dist, dist);
...
```
上面的代码源自[这篇 Inigo's Quilez 的文章](http://www.iquilezles.org/www/articles/smoothvoronoi/smoothvoronoi.htm),他写道:
*“可能值得注意的是,上面的代码中有一个很漂亮的技巧。多数实现都存在精度问题,因为他们是在“域”空间(如“世界”或“对象”空间)内产生随机点,这可能是里原点任意远的。要解决这个问题,可以使用更高精度的数据类型,或更聪明些。我的实现不是在“域”空间(如“世界”或“对象”空间)内产生随机点,而是在“网格”空间内:一旦提取了着色点的整数和小数部分,我们当前的网格就确定了,我们所关心的就是这个网格周围发生了什么,意味着我们可以将所有坐标的整数部分放在一起,从而节省了许多精度位。事实上,一个常规的 voronoi 实现中,从着色点减去随机特征点时,点坐标的整数部分简单地消除掉了。上面的实现中,我们甚至不会让这种消除发生,因为我们正在把所有的计算移到“网格”空间。这个技巧可以让你处理这种情况: 你想要把 voronoi 用在整个星球上——可以简单地将输入替换为双精度,执行 floor() 和 fract() 计算,其余的计算仍使用浮点数,省去了将整个实现改成双精度的成本。当然,同样的技巧也适用于 Perlin Noise 模式(但是我还没有在任何地方看到过它的实现或记录)。”*
简要重述一遍:我们把空间分割成网格,计算每个像素点到它所在网格中的那个特征点的距离,和它到相邻的八个网格中的特征点的距离,结果是一个距离场,如下所示:
<div class="codeAndCanvas" data="cellnoise-02.frag"></div>
进一步探索:
- 缩放空间。
- 你有其它办法让那些特征点动起来吗?
- 如果我们想要加入一个鼠标位置作为其中一个特征点怎么办?
- 有没有其它办法构造这个距离场,除了 ```m_dist = min(m_dist, dist);``` 之外?
- 用这个距离场你可以创造出什么有意思的图案?
这个算法也可以从特征点而非像素点的角度理解。在那种情况下,算法可以表述为:每个特征点向外扩张生长,直到它碰到其它扩张的区域。这反映了自然界的生长规则。生命的形态是由内部扩张、生长的力量和限制性的外部力量共同决定的。模拟这种行为的算法以 [Georgy Voronoi](https://en.wikipedia.org/wiki/Georgy_Voronoy) 命名。
![](monokot_root.jpg)
### Voronoi 算法
用网格噪声构造 Voronoi 图远没有看上去的那么难。我们只需要*保留*一些关于最近的特征点的额外信息。我们将要用到一个叫 ```m_point``` 的 ```vec2``` 类型变量存储像素点到最近的特征点的向量,而不只是距离。
```glsl
...
if( dist < m_dist ) {
m_dist = dist;
m_point = point;
}
...
```
注意在下面的代码中,我们不再使用 ```min``` 来计算最近距离,而是用一个普通的 ```if``` 语句。为什么因为当一个新的更近的特征点出现的时候我们还需要保存它的位置32 行至 37行
<div class="codeAndCanvas" data="vorono-00.frag"></div>
注意那个移动的(鼠标位置下面那个)细胞的颜色是如何根据它的位置而改变的。那是因为它的颜色由最近特征点决定。
就像我们之前所做的那样,现在是扩大规模的时候,转而使用 [Steven Worley 的论文中的方法](http://www.rhythmiccanvas.com/research/papers/worley.pdf)。试着自己实现它。你可以通过点击下面的示例来获取帮助。注意 Steven Worley 的原始方法中,每个网格的特征点数是可变的,对大多数网格来说不止一个。在他的 C 语言实现中这是用来提早退出来加速循环。GLSL 循环不允许动态的迭代次数,所以你可能更希望一个网格对应一个特征点。
<a href="../edit.php#12/vorono-01.frag"><canvas id="custom" class="canvas" data-fragment-url="vorono-01.frag" width="520px" height="200px"></canvas></a>
一旦你弄清楚了这个算法,想想它有什么有趣、有创意的用途。
![Extended Voronoi - Leo Solaas (2011)](solas.png)
![Cloud Cities - Tomás Saraceno (2011)](saraceno.jpg)
![Accretion Disc Series - Clint Fulkerson](accretion.jpg)
![Vonoroi Puzzle - Reza Ali (2015)](reza.png)
### 优化 Voronoi
在 2011 年, [Stefan Gustavson 优化了 Steven Worley 的算法](http://webstaff.itn.liu.se/~stegu/GLSL-cellular/GLSL-cellular-notes.pdf),仅仅对一个 2x2 的矩阵作遍历(而不是 3x3 的矩阵)。这显著地减少了工作量,但是会在网格边缘制造人工痕迹。看看下面的例子。
<div class="glslGallery" data="12/2d-cnoise-2x2,12/2d-cnoise-2x2x2,12/2d-cnoise,12/3d-cnoise" data-properties="clickRun:editor,openFrameIcon:false"></div>
Later in 2012 [Inigo Quilez wrote an article on how to make precise Voronoi borders](http://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm).
<a href="../edit.php#12/2d-voronoi.frag"><img src="2d-voronoi.gif" width="520px" height="200px"></img></a>
Inigo 在 Voronoi 上的实验并没有就此停止。2014 年,他写了一篇非常漂亮的文章,提出一种他称作为 [voro-noise](http://www.iquilezles.org/www/articles/voronoise/voronoise.htm) 的噪声,可以让常规噪声和 voronoi 逐渐地融合。用他的话说:
*“尽管有这样的相似之处,但事实上,两种模式中网格的使用方式都是不同的。噪声会内插或平均随机值(如值噪声),而 Voronoi 是计算到最近特征点的距离。平滑双线性插值和最小值评估是两个非常不同的操作,或者……它们是否能用更广义的方法组合?如果是这样,那么噪声和 Voronoi 模式都可以被看作是一种更一般的以网格为基础的模式生成器?”*
<a href="../edit.php#12/2d-voronoise.frag"><canvas id="custom" class="canvas" data-fragment-url="2d-voronoise.frag" width="520px" height="200px"></canvas></a>
现在,是时候仔细观察事物,去接受自然的启发,并用这项技术发现你自己的风景!
![Deyrolle glass film - 1831](DeyrolleFilm.png)
<div class="glslGallery" data="12/metaballs,12/stippling,12/cell,12/tissue,12/cracks,160504143842" data-properties="clickRun:editor,openFrameIcon:false"></div>

@ -38,14 +38,14 @@ float noise (in vec2 st) {
float fbm (in vec2 st) {
// Initial values
float value = 0.0;
float amplitud = .5;
float amplitude = .5;
float frequency = 0.;
//
// Loop of octaves
for (int i = 0; i < OCTAVES; i++) {
value += amplitud * noise(st);
value += amplitude * noise(st);
st *= 2.;
amplitud *= .5;
amplitude *= .5;
}
return value;
}

@ -0,0 +1,111 @@
![Due East over Shadequarter Mountain - Matthew Rangel (2005) ](rangel.jpg)
## 分形布朗运动Fractal Brownian Motion
噪声对不同的人来说有不同的意义。音乐家把它当成一种令人不安的声响通信工程师把它当作干扰信号天体物理学家把它看作宇宙微波背景辐射。这些概念吸引着我们去探索处处可见的随机性的物理原因。但是让我们从更基础也更简单的开始波和波的属性。波就是某些属性随着时间波动变化。声波是气压的波动电磁波是电场和磁场的波动。波的两个重要特征是振幅amplitude和频率frequency。一个简单的线性波一维的方程如下
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = amplitude * sin(x * frequency);
"></div>
* 试着改变频率和振幅的值,理解他们如何影响波形。
* 使用造型函数,试着随时间改变振幅。
* 使用造型函数,试着随时间改变频率。
通过做后两个练习,你已经知道怎么“调节”一个正弦波。恭喜你,你刚刚创造了一个 AM调幅和 FM调频
波的另一个有趣的特性是可以相加,这一特性的正式说法叫叠加性。调一调下面几行代码,注意我们加上那些不同振幅和频率的正弦波的时候,总的波形是如何变化的。
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = sin(x * frequency);
float t = 0.01*(-u_time*130.0);
y += sin(x*frequency*2.1 + t)*4.5;
y += sin(x*frequency*1.72 + t*1.121)*4.0;
y += sin(x*frequency*2.221 + t*0.437)*5.0;
y += sin(x*frequency*3.1122+ t*4.269)*2.5;
y *= amplitude*0.06;
"></div>
* 试试改变加上去的波的振幅和频率。
* 有没有可能两个波正好相互抵消?如果是的话,会是什么样子?
* 有没有一种叠加波的方法,让他们互相放大?
从音乐理论上说每个音符都和一个特定的频率相关联。这些音符和频率的关系遵从一定的模式也就是我们所说的音阶一个八度octave对应着频率上的加倍或减半。
现在,让我们把正弦波放在一边,想想 Perlin 噪声Perlin 噪声的基本形式看起来和正弦波有点相似。它的振幅和频率有着某种变化,但振幅保持着合理的连续性,而且频率被限制在一个距离中心频率很小的范围内。尽管它不像正弦波那样规则,并且把几个不同缩放比例的 Perlin 噪声相加更容易制造出随机形态。把一些正弦波相加也是有可能制造随机形态的,但那需要很多不同的波叠加才能把他们的天生的周期性和规则性隐藏起来。
通过在循环(循环次数为 *octaves*,一次循环为一个八度)中叠加噪声,并以一定的倍数(*lacunarity*,间隙度)连续升高频率,同时以一定的比例(*gain*,增益)降低 **噪声** 的振幅最终的结果会有更好的细节。这项技术叫“分形布朗运动fractal Brownian Motion*fBM*或者“分形噪声fractal noise最简单的实现如下
<div class="simpleFunction" data="// Properties
const int octaves = 1;
float lacunarity = 2.0;
float gain = 0.5;
//
// Initial values
float amplitude = 0.5;
float frequency = 1.;
//
// Loop of octaves
for (int i = 0; i < octaves; i++) {
&#9;y += amplitude * noise(frequency*x);
&#9;frequency *= lacunarity;
&#9;amplitude *= gain;
}"></div>
* 从 1 到 24810 逐渐改变 octaves看看会发生什么。
* 当 octaves 大于 4 时,尝试改变 lacunarity 的值。
* 当 octaves 大于 4 时,改变 gain 的值,看看会发生什么。
注意,随着我们一个八度接一个八度地往上叠加,曲线看起来有越来越多的细节,同时,自相似性也越来越明显。如果你放大看看,曲线的局部和整体看起来很相似,并且,任选两段不同的部分看起来也多少有些相似。这是一个数学上的分形的重要性质,我们在上面的循环中模拟了这个性质。我们不是要创造一个*真的*分形,因为我们在几次循环之后就不再往上叠加了,但理论上说,如果我们一直继续这个循环,不断地往上叠加噪声,就会得到一个真正的数学意义上的分形。在计算机图形领域,我们能处理的细节总是有极限的,比如物体比一个像素还小的时候,所以没有必要不断地往上叠加来制造分形的形态。有时候我们确实需要叠加很多次,但不必叠加无限次。
下面的示例代码就是 fBm 的二维实现,生成了分形图案:
<div class='codeAndCanvas' data='2d-fbm.frag'></div>
* 在 37 行减小八度OCTAVES的数量
* 在 47 行调试 fBm 的间隙度lacumarity
* 在 47 行调试 fBm 的增益gain
这项技术被广泛地应用于构造程序化风景。fBm 的自相似性能够很完美地模拟山脉,因为山脉形成过程中的腐蚀形成了这种不同尺度上的自相似性。如果你对此感兴趣,你一定要去看看 [Inigo Quiles 这篇关于高级噪声的文章](http://www.iquilezles.org/www/articles/morenoise/morenoise.htm)。
![Blackout - Dan Holdsworth (2010)](holdsworth.jpg)
使用相同的技术,也可以获得其他效果,比如**湍流**turbulence效果。它本质上是一个 fBm但是由一个有符号噪声的绝对值构成从而在函数中创建了尖锐的山谷。
```glsl
for (int i = 0; i < OCTAVES; i++) {
value += amplitude * abs(snoise(st));
st *= 2.;
amplitude *= .5;
}
```
<a href="../edit.php#13/turbulence.frag"><img src="turbulence-long.png" width="520px" height="200px"></img></a>
这个算法家族中的另一个成员是**山脊**ridge就是把凹下去的山谷翻上来从而制造山脊
```glsl
n = abs(n); // create creases
n = offset - n; // invert so creases are at top
n = n * n; // sharpen creases
```
<a href="../edit.php#13/ridge.frag"><img src="ridge-long.png" width="520px" height="200px"></img></a>
这个算法的另外一个变种,把噪声分量乘起来(而不是叠加),可以创造一些很有用的东西。另外一个方法是,根据前一次循环中的噪声来缩放后续的噪声。当我们这样做的时候,我们已经走出严格的分形定义了,走入了一个叫“多重分形”的未知领域。多重分形虽不是按数学方式严格定义,但这并不意味着它的用处会更少些。 实际上,用多重分形模拟生成地形在商业软件中非常常见。要了解更多,你可以去读 Kenton Musgrave 的“Texturing and Modeling: a Procedural Approach”第三版的 16 章。很可惜,这本书几年前已经绝版,不过你还可以从图书馆和二手市场找到。(网上有卖这本书第一版的 PDF 版,但是别去买——只是浪费钱,是 1994 年的版本,不包括第三版包含的地形建模的部分。)
### 域翘曲Domain Warping
[Inigo Quiles 写了另一篇有趣的文章](http://www.iquilezles.org/www/articles/warp/warp.htm),关于如何用 fBm 来扭曲 fBm 空间。很有意思,不是吗?这就像《盗梦空间》里的梦中梦。
![ f(p) = fbm( p + fbm( p + fbm( p ) ) ) - Inigo Quiles (2002)](quiles.jpg)
下面的代码展示了这项技术的一个不那么极端的例子,用它生成类似云一样的纹理。注意自相似性如何表现在结果中。
<div class='codeAndCanvas' data='clouds.frag'></div>
用这种方法用噪声扭曲纹理坐标非常有用,非常有趣,也极难掌握。这是个很强大的工具,但要想用好它需要一些经验。一个有用的办法是,用噪声的导数(梯度)替换坐标。[Ken Perlin 和 Fabrice Neyret 的一篇非常著名的“流噪声”](http://evasion.imag.fr/Publications/2001/PN01/)就是以这个想法为基础。一些现代的 Perlin 噪声的实现不但计算噪声,还计算它的解析梯度。如果“真实”梯度对过程化函数来说不便计算,你总是可以计算出数值梯度来逼近它,尽管没那么精确而且要花费更多工夫。

@ -80,7 +80,7 @@ Using more or less the same technique, it's also possible to obtain other effect
for (int i = 0; i < OCTAVES; i++) {
value += amplitude * abs(snoise(st));
st *= 2.;
amplitud *= .5;
amplitude *= .5;
}
```

@ -87,14 +87,14 @@ float snoise(vec2 v) {
float turbulence (in vec2 st) {
// Initial values
float value = 0.0;
float amplitud = .5;
float amplitude = .5;
float frequency = 0.;
//
// Loop of octaves
for (int i = 0; i < OCTAVES; i++) {
value += amplitud * abs(snoise(st));
value += amplitude * abs(snoise(st));
st *= 2.;
amplitud *= .5;
amplitude *= .5;
}
return value;
}

@ -32,9 +32,10 @@
* [图案](09/?lan=ch)
* 生成设计
* [Random 函数](10/?lan=ch)
* [noise 函数](11/?lan=ch)
* 布朗运动
* [随机](10/?lan=ch)
* [噪声](11/?lan=ch)
* [网格噪声](12/?lan=ch)
* [分形布朗运动](13/?lan=ch)
* 分形
* 图像处理:

Loading…
Cancel
Save