Chapter 4 Perlin Noise
Posted tooyoungtsukasa
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Chapter 4 Perlin Noise相关的知识,希望对你有一定的参考价值。
为了获得炫酷的坚固纹理效果,大部分人使用某种形式的Perlin噪声, 这些都是以他们的发明家 Ken Perlin的名字命名的。 Perlin纹理不会像这样返回白色噪点:
相反,它会返回类似于模糊白噪声的东西:
Perlin噪声的一个关键部分是它是可重复的:它将3D点作为输入并始终返回相同的随机数。附近的点返回相似的数字。Perlin噪声的另一个重要部分是它简单快速,所以它通常以黑客身份进行。根据Andrew Kensler的描述,我将逐步构建这种攻击。
我们可以用一个随机数字的3D数组拼接所有的空间,并将它们用于块中。在重复明确的地方你会得到一些块状物:
我们只是使用某种散列来搅乱这个,而不是拼贴。这有一些支持代码来完成这一切:
from math import floor from random import random class perlin: def __init__(self): self.ranfloat=perlin_generate() self.perm_x=perlin_generate_perm() self.perm_y=perlin_generate_perm() self.perm_z=perlin_generate_perm() def noise(self,p): u=p.x()-floor(p.x()) v=p.y()-floor(p.y()) w=p.z()-floor(p.z()) i=int(4*p.x())&255 j=int(4*p.y())&255 k=int(4*p.z())&255 return self.ranfloat[self.perm_x[i]^self.perm_y[j]^self.perm_z[k]] def perlin_generate(): p=[0.0 for j in range(256)] for i in range(0,256,1): p[i]=random() return p def permute(p,n): for i in range(n-1,0,-1): target=int(random()*(i+1)) tmp=p[i] p[i]=p[target] p[target]=tmp return ; def perlin_generate_perm(): p=p=[0 for j in range(256)] for i in range(0,256,1): p[i]=i permute(p,256) return p
现在,如果我们创建一个实际的纹理,它使这些浮点数在0和1之间并创建灰色颜色:
from texture import texture from rayMath import vec3 from perlin import perlin class noise_texture(texture): def __init__(self): self.noise=perlin() def value(self,u,v,p): return vec3(1,1,1)*self.noise.noise(p)
加上散列之后,的确如我们所料的那样混乱:
为了使之更平滑,我们使用插值进行修改。
class perlin: def noise(self,p): u=p.x()-floor(p.x()) v=p.y()-floor(p.y()) w=p.z()-floor(p.z()) i=floor(p.x()) j=floor(p.y()) k=floor(p.z()) c=[[[0.0 for a in range(2)] for b in range(2)] for c in range(2)] for di in range(2): for dj in range(2): for dk in range(2): temp=self.ranfloat[self.perm_x[i+di&255]^self.perm_y[j+dj&255]^self.perm_z[k+dk&255]] c[di][dj][dk]=temp return trilinear_interp(c,u,v,w) def trilinear_interp(c,u,v,w): accum=0 for i in range(2): for j in range(2): for k in range(2): accum=accum+( i*u+(1-i)*(1-u))*( j*v+(1-j)*(1-v))*( k*w+(1-k)*(1-w))*(c[i][j][k]) return accum
更好了,但有明显的网格效果。 其中一些是马赫带,一种颜色的线性内插的知名人造物。 一个标准的技巧是使用hermite立方来舍去插值:
def noise(self,p): u=p.x()-floor(p.x()) v=p.y()-floor(p.y()) w=p.z()-floor(p.z()) u=u*u*(3-2*u) v=v*v*(3-2*v) w=w*w*(3-2*w) i=floor(p.x()) j=floor(p.y()) k=floor(p.z())
得到了看起来更平滑的效果:
还是有点低频。 我们可以缩放输入点以使其变化更快:
class noise_texture(texture): def __init__(self,sc): self.noise=perlin() self.scale=sc def value(self,u,v,p): return vec3(1,1,1)*self.noise.noise(self.scale*p)
def turb(self,p, depth=7): accum = 0 temp_p = p weight = 1.0 for i in range(depth): accum = accum + weight *self.noise(temp_p) weight=weight*0.5 temp_p=temp_p*2 return fabs(accum) def noise(self,p): u=p.x()-floor(p.x()) v=p.y()-floor(p.y()) w=p.z()-floor(p.z()) u=u*u*(3-2*u) v=v*v*(3-2*v) w=w*w*(3-2*w) i=floor(p.x()) j=floor(p.y()) k=floor(p.z()) c=[[[0.0 for a in range(2)] for b in range(2)] for c in range(2)] for di in range(2): for dj in range(2): for dk in range(2): temp=self.ranvec[self.perm_x[i+di&255]^self.perm_y[j+dj&255]^self.perm_z[k+dk&255]] c[di][dj][dk]=temp return perlin_interp(c,u,v,w) def perlin_interp(c,u,v,w): uu = u * u * (3 - 2 * u) vv = v * v * (3 - 2 * v) ww = w * w * (3 - 2 * w) accum=0 for i in range(2): for j in range(2): for k in range(2): weight_v=vec3(u-i,v-j,w-k) accum=accum+( i*uu+(1-i)*(1-uu))*( j*vv+(1-j)*(1-vv))*( k*ww+(1-k)*(1-ww))*(c[i][j][k].dot(weight_v)) return accum
class noise_texture(texture): def __init__(self,sc): self.noise=perlin() self.scale=sc def value(self,u,v,p): return vec3(1,1,1)*0.5*(1+sin(self.scale*p.z()+10*self.noise.turb(p)))
以上是关于Chapter 4 Perlin Noise的主要内容,如果未能解决你的问题,请参考以下文章
除了显而易见的之外,Perlin Noise 还有其他用途吗?
Unity 动态网格地图的生成:基于Perlin Noise创建地形