3D数学之柏林噪声(Perlin Noise)
经过四天的努力,终于自己实现了3D柏林噪声,当第一次用它成功渲染出茶壶的时候,感觉自己跟《当幸福来敲门》的男主角chris一样,当时不由自主为自己鼓起了掌.
4天时间啊,这4天时间基本上没有背单词,白天一个人去教室面对冰冷的代码.晚上回寝室熬夜到2,3点,到处在网上找资料,找源码,但是当自己把茶壶渲染出来的时候,突然觉得这一切都是值得的,因为它那斑驳的颜色温暖了我的心!!!
好了,转入正题.
其实它的原理并不是很难,但是由于网上实现的版本太多太杂,真要实现起来竟然不知从何处下手,而且自己写的时候会遇到各种各样的问题.最终写出来了,所以很欣然.
先看下,我在网上找的一些资料吧
另外我还下载了libnoise源码,可以说这个是我能成功渲染出茶壶的关键.后面我会提到.
好了,这里我只讲1到3D的柏林噪声,4D的我没有实现,实在没有时间,不能在这个环节浪费太多了时间,因为项目里面只用到了2D的柏林噪声来渲染海面.
由于网上理论资料也很详细,我这里主要讲解实现过程中需要注意的地方.
先简单提一下理论,
Perlin噪声由多个coherent noise组成,每一个coherent noise称为一个octave.
octave越多,Perlin噪声就越细致,但计算时间也越长。
具体理论知识见
http://blog.sina.com.cn/s/blog_68f6e8a901013t7d.html
http://www.cnblogs.com/babyrender/archive/2008/10/27/BabyRender.html
coherent 噪声函数
输入为对应维数的向量(对于一维来说就是一个float,对于二维来说就是一个float[2],以此类推)
输出为[-1,1]的float
现在拿一维来举例,首选我们需要在值为整数的点为其生成随机的梯度(1维下是斜率),然后找出输入的float数f左边和右边的两个整数x0,x1,然后找到x0,x1到的梯度g0,g1,算出点f到点x0和x1的向量r0 = f - x0, r1 = f - x1 = r0 - 1,最后分别将两个整数点的梯度与f到这两个整数点的向量相乘,得到u,v两个值,最后需要通过r0算出混合权值h,公式为
h = s_curve_new(r0),最后得到范围在[-1,1]的随机数 u + h * (v - u)
#define s_curve(t) ( t * t * (3. - 2. * t) ) //即3t^2 - 2t^3 ,旧版本
#define s_curve_new(t) ( t * t * t * (t * (t * 6 - 15) + 10) ) //即 6t^5 - 15t^4 + 10t^3,新版本
好了,下面就一个版本一个版本的讲吧
首先是两个官方版本:
http://mrl.nyu.edu/~perlin/noise
第一个版本,只实现了3D的coherent noise
下面是我自己的代码
这个结果表面看上去能接受,但是要拿来真正渲染的时候就会出问题,所以我们需要对输入的数据进行处理,后面我会讲
第二个版本比较详细,实现了1到3D的noise
http://mrl.nyu.edu/~perlin/doc/oscar.html
下面我的代码
看了一下输出结果,不是太理想,绝对值超过0.5的数几乎没有.哎,官方版本也坑啊!!!!!!尽管如此,我的茶壶例子还是基本这个版本的3D noise写的,谁叫它是官方版本呢,
但是我看了下libnoise的输出,0.6,0.7都很常见,以后估计我会用libnoise这个库
另外,我根据<<三维游戏引擎设计技术及其应用>>这本书的提示生成了一个2D Perlin noise渲染了上面Displacement mapping讲过的海洋,只不过这里的移位贴图是我们通过2D Perlin noise生成的,不是预先设定的,虽然版本有点山寨,但是效果还是可以接受
它的倍频的采样方法有点特别,最低倍频,波长最长,振幅最大,如果我们需要生成129*129的2D图(它的方法最好采用2^n + 1的大小的图),最低倍频,我们设波长为32,那么我们就分别
在0,32,64,96,128处进行采样(也就是生成0到振幅的随机数),然后中间的进行以这些点进行插值,当为2倍频时,周期减少一倍为16,则在,0,16,32,48,64,80,96,112,128处进行采样,然后进行插值,注意采样时这里振幅也会减少一倍.后面的依次类推,最后将它们全部累加得到的就是2D的perlin noise
用这个版本渲染的海洋
用这个版本生成的海洋以及对应的位移贴图,颜色越白,海面越高
用这个版本生成 的2D Perlin noise(即移位贴图,只不过是两张Perlin noise按照某一权值比重进行累加后的结果)
下面给出这个DEMO的源代码
最后,说下4D perlin noise虽然我没有去实现,但是它还是有它的作用的,因为多出来的一维可以作成时间的维数,这样,你的3D纹理就可以动态变化了,
同时,3D perlin noise也可以当作动态的2D perlin noise纹理.呵呵,大功造成,perlin noise就告一段落吧,下一阶段目标是projected grid,好了今天晚上就不找资料,太累了,睡觉了.明天继续我的伟大航程