diff --git a/10/README-ch.md b/10/README-ch.md index 03d793b..fd5b2ca 100755 --- a/10/README-ch.md +++ b/10/README-ch.md @@ -30,9 +30,9 @@ Randomness is a maximal expression of entropy. How can we generate randomness in //y = pow(rand(x),5.);"> -不久前 [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 -* 创早其他有趣的效果。 +* 创造其他有趣的效果。 -优雅的使用随机是困难的,尤其是你希望创作自然的模拟。随机仅仅是过于混乱了,真实生活中很少有东西看上去如此 ```random()```。如果观察(玻璃床上)雨滴的肌理或是股票的曲线 — 这两个都挺随机的 — 但是他们和我们在章开始的随机图案看起来不是同一对爹妈生的。原因?嗯,随机值是没有因果关系的,而大多数自然图案(肌理)都对前一个状态有所记忆(基于前一个状态)。 +完美地掌握随机之美是困难的,尤其是你想要让作品看起来很自然。随机仅仅是过于混乱了,真实生活中很少有东西看上去如此 ```random()```。如果观察(玻璃床上)雨滴的肌理或是股票的曲线——这两个都挺随机的——但他们和本章开始的随机图案看起来不是同一对爹妈生的。原因?嗯,随机值之间没有什么相关性,而大多数自然图案(肌理)都对前一个状态有所记忆(基于前一个状态)。 -下一章我们将学习噪波,一种光滑 和 *自然的* 创作计算机混沌的方式。 +下一章我们将学习噪声,一种光滑 和 *自然的* 创作计算机混沌的方式。 diff --git a/12/README-ch.md b/12/README-ch.md index 5556502..41f9e87 100644 --- a/12/README-ch.md +++ b/12/README-ch.md @@ -4,7 +4,7 @@ 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```). 所以,没有动态循环——迭代的次数必须是固定的。 +要理解它背后的原理,我们需要从**迭代**开始思考。你可能已经知道迭代是什么意思:对,就是使用 ```for``` 循环。GLSL 的 ```for``` 循环中,只有一个需要注意的:我们检查循环是否继续的次数必须是一个常数(```const```). 所以,没有动态循环——迭代的次数必须是固定的。 网格噪声基于距离场,这里的距离是指到一个特征点集最近的点的距离。比如说我们要写一个 4 个特征点的距离场,我们应该做什么呢?**对每一个像素,计算它到最近的特征点的距离**。也就是说,我们需要遍历所有 4 个特征点,计算他们到当前像素点的距离,并把最近的那个距离存下来。 @@ -112,7 +112,7 @@ for (int y= -1; y <= 1; y++) { 上面的代码源自[这篇 Inigo's Quilez 的文章](http://www.iquilezles.org/www/articles/smoothvoronoi/smoothvoronoi.htm),他写道: -*“可能值得注意的是,在上面的代码中有一个很漂亮的技巧。多数实现都存在精度问题,因为他们是在“域”空间(如“世界”或“对象”空间)内产生随机点,这可能是里原点任意远的。要解决这个问题,可以使用更高精度的数据类型,或更聪明些。我的实现不是在“域”空间(如“世界”或“对象”空间)内产生随机点,而是在“网格”空间内:一旦提取了着色点的整数和小数部分,我们当前的网格就确定了,我们所关心的就是在这个网格周围发生了什么,意味着我们可以将所有坐标的整数部分放在一起,从而节省了许多精度位。事实上,在一个常规的 voronoi 实现中,从着色点减去随机特征点时,点坐标的整数部分简单地消除掉了。在上面的实现中,我们甚至不会让这种消除发生,因为我们正在把所有的计算移到“网格”空间。这个技巧可以让你处理这种情况: 你想要把 voronoi 用在整个星球上——可以简单地将输入替换为双精度,执行 floor() 和 fract() 计算,其余的计算仍使用浮点数,不需要将整个实现改变成双精度的成本。当然,同样的技巧也适用于 Perlin Noise 模式(但是我还没有在任何地方看到过它的实现或记录)。”* +*“可能值得注意的是,上面的代码中有一个很漂亮的技巧。多数实现都存在精度问题,因为他们是在“域”空间(如“世界”或“对象”空间)内产生随机点,这可能是里原点任意远的。要解决这个问题,可以使用更高精度的数据类型,或更聪明些。我的实现不是在“域”空间(如“世界”或“对象”空间)内产生随机点,而是在“网格”空间内:一旦提取了着色点的整数和小数部分,我们当前的网格就确定了,我们所关心的就是这个网格周围发生了什么,意味着我们可以将所有坐标的整数部分放在一起,从而节省了许多精度位。事实上,一个常规的 voronoi 实现中,从着色点减去随机特征点时,点坐标的整数部分简单地消除掉了。上面的实现中,我们甚至不会让这种消除发生,因为我们正在把所有的计算移到“网格”空间。这个技巧可以让你处理这种情况: 你想要把 voronoi 用在整个星球上——可以简单地将输入替换为双精度,执行 floor() 和 fract() 计算,其余的计算仍使用浮点数,省去了将整个实现改成双精度的成本。当然,同样的技巧也适用于 Perlin Noise 模式(但是我还没有在任何地方看到过它的实现或记录)。”* 简要重述一遍:我们把空间分割成网格,计算每个像素点到它所在网格中的那个特征点的距离,和它到相邻的八个网格中的特征点的距离,结果是一个距离场,如下所示: diff --git a/13/README-ch.md b/13/README-ch.md new file mode 100644 index 0000000..4316bfb --- /dev/null +++ b/13/README-ch.md @@ -0,0 +1,111 @@ +![Due East over Shadequarter Mountain - Matthew Rangel (2005) ](rangel.jpg) + +## 分形布朗运动(Fractal Brownian Motion) + +噪声对不同的人来说有不同的意义。音乐家把它当成一种令人不安的声响,通信工程师把它当作干扰信号,天体物理学家把它看作宇宙微波背景辐射。这些概念吸引着我们去探索处处可见的随机性的物理原因。但是,让我们从更基础,也更简单的开始:波和波的属性。波就是某些属性随着时间波动变化。声波是气压的波动,电磁波是电场和磁场的波动。波的两个重要特征是振幅(amplitude)和频率(frequency)。一个简单的线性波(一维)的方程如下: + +
+ +* 试着改变频率和振幅的值,理解他们如何影响波形。 +* 使用造型函数,试着随时间改变振幅。 +* 使用造型函数,试着随时间改变频率。 + +通过做后两个练习,你已经知道怎么“调节”一个正弦波。恭喜你,你刚刚创造了一个 AM(调幅)和 FM(调频)波! + +波的另一个有趣的特性是可以相加,这一特性的正式说法叫叠加性。调一调下面几行代码,注意我们加上那些不同振幅和频率的正弦波的时候,总的波形是如何变化的。 + +
+ +* 试试改变加上去的波的振幅和频率。 +* 有没有可能两个波正好相互抵消?如果是的话,会是什么样子? +* 有没有一种叠加波的方法,让他们互相放大? + +从音乐理论上说,每个音符都和一个特定的频率相关联。这些音符和频率的关系遵从一定的模式,也就是我们所说的音阶,一个八度(octave)对应着频率上的加倍或减半。 + +现在,让我们把正弦波放在一边,想想 Perlin 噪声!Perlin 噪声的基本形式看起来和正弦波有点相似。它的振幅和频率有着某种变化,但振幅保持着合理的连续性,而且频率被限制在一个距离中心频率很小的范围内。尽管它不像正弦波那样规则,并且把几个不同缩放比例的 Perlin 噪声相加更容易制造出随机形态。把一些正弦波相加也是有可能制造随机形态的,但那需要很多不同的波叠加才能把他们的天生的周期性和规则性隐藏起来。 + +通过在循环(循环次数为 *octaves*,一次循环为一个八度)中叠加噪声,并以一定的倍数(*lacunarity*,间隙度)连续升高频率,同时以一定的比例(*gain*,增益)降低 **噪声** 的振幅,最终的结果会有更好的细节。这项技术叫“分形布朗运动(fractal Brownian Motion)”(*fBM*),或者“分形噪声(fractal noise)”,最简单的实现如下: + +
+ +* 从 1 到 2,4,8,10 逐渐改变 octaves,看看会发生什么。 +* 当 octaves 大于 4 时,尝试改变 lacunarity 的值。 +* 当 octaves 大于 4 时,改变 gain 的值,看看会发生什么。 + +注意,随着我们一个八度接一个八度地往上叠加,曲线看起来有越来越多的细节,同时,自相似性也越来越明显。如果你放大看看,曲线的局部和整体看起来很相似,并且,任选两段不同的部分看起来也多少有些相似。这是一个数学上的分形的重要性质,我们在上面的循环中模拟了这个性质。我们不是要创造一个*真的*分形,因为我们在几次循环之后就不再往上叠加了,但理论上说,如果我们一直继续这个循环,不断地往上叠加噪声,就会得到一个真正的数学意义上的分形。在计算机图形领域,我们能处理的细节总是有极限的,比如物体比一个像素还小的时候,所以没有必要不断地往上叠加来制造分形的形态。有时候我们确实需要叠加很多次,但不必叠加无限次。 + +下面的示例代码就是 fBm 的二维实现,生成了分形图案: + +
+ +* 在 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.; + amplitud *= .5; +} +``` + + + +这个算法家族中的另一个成员是**山脊**(ridge),就是把凹下去的山谷翻上来,从而制造山脊: + +```glsl + n = abs(n); // create creases + n = offset - n; // invert so creases are at top + n = n * n; // sharpen creases +``` + + + +这个算法的另外一个变种,把噪声分量乘起来(而不是叠加),可以创造一些很有用的东西。另外一个方法是,根据前一次循环中的噪声来缩放后续的噪声。当我们这样做的时候,我们已经走出严格的分形定义了,走入了一个叫“多重分形”的未知领域。多重分形虽不是按数学方式严格定义,但这并不意味着它的用处会更少些。 实际上,用多重分形模拟生成地形在商业软件中非常常见。要了解更多,你可以去读 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) + +下面的代码展示了这项技术的一个不那么极端的例子,用它生成类似云一样的纹理。注意自相似性如何表现在结果中。 + +
+ +用这种方法用噪声扭曲纹理坐标非常有用,非常有趣,也极难掌握。这是个很强大的工具,但要想用好它需要一些经验。一个有用的办法是,用噪声的导数(梯度)替换坐标。[Ken Perlin 和 Fabrice Neyret 的一篇非常著名的“流噪声”](http://evasion.imag.fr/Publications/2001/PN01/)就是以这个想法为基础。一些现代的 Perlin 噪声的实现不但计算噪声,还计算它的解析梯度。如果“真实”梯度对过程化函数来说不便计算,你总是可以计算出数值梯度来逼近它,尽管没那么精确而且要花费更多工夫。 diff --git a/README-ch.md b/README-ch.md index 15640e3..e707af8 100644 --- a/README-ch.md +++ b/README-ch.md @@ -35,7 +35,7 @@ * [随机](10/?lan=ch) * [噪声](11/?lan=ch) * [网格噪声](12/?lan=ch) - * 分形布朗运动 + * [分形布朗运动](13/?lan=ch) * 分形 * 图像处理: