好了,转入正题.
其实它的原理并不是很难,但是由于网上实现的版本太多太杂,真要实现起来竟然不知从何处下手,而且自己写的时候会遇到各种各样的问题.最终写出来了,所以很欣然.
先看下,我在网上找的一些资料吧
另外我还下载了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的源代码
好了,重头戏了,也是在研究perlin noise过程中耗时最久的一个demo,着重讲一下
这个DEMO是关于perlin 3d noise的,它的实现是基于第2个官方版本中的3d noise
这个DEMO需要一个3维的纹理供像素着色器来采样.所以我们需要建立自己的3维纹理,比如我们需要建立128*128*128的三维纹理
注意DEMO将noise的4个倍频分存储到RGBA四个通道,到时候再到像素着色器里面通过输入顶点的位置采样三维纹理得到一个float4,再将每个通道从这个float4提取出来进行累加,得到一个[0,1]范围的随机值,用这个随机值在两个颜色之间进行插值,得到最终的颜色
这里最关键的步骤就是3D纹理的建立,最初我设想通过 i/128,j/128,k/128作为noise3d的输入值,i,j,k从0到127,但是这样得到的纹理太有规律了,并且波动很小,就在这个地方卡了我很长一段时间,最后研究Libnoise的时候才按照它的方法把输入值进行了一下处理.它指定了x,y,z的上,下界共6个值,最后将输入的值变换到这个6个值之间,具体如何变换,看我的源码,有注释的.好了给出源码
这样绚丽的茶壶就出来了,呵呵,4天的努力终于没有白费
最后,说下4D perlin noise虽然我没有去实现,但是它还是有它的作用的,因为多出来的一维可以作成时间的维数,这样,你的3D纹理就可以动态变化了,
同时,3D perlin noise也可以当作动态的2D perlin noise纹理.呵呵,大功造成,perlin noise就告一段落吧,下一阶段目标是projected grid,好了今天晚上就不找资料,太累了,睡觉了.明天继续我的伟大航程
2D柏林噪声海洋源代码