Perline Noise
http://www.twinklingstar.cn/2015/2581/classical-perlin-noise/
Perlin噪声
本篇文章谢绝转载,也禁止用于任何商业目的
1. Perlin噪声
Ken Perlin在1983年提出了一种渐变噪声,Perlin在1985年的SIGGRAPH[1]有该算法的描述,称之为经典Perlin噪声(Classical Perlin Noise)。为了简化计算,方便使用硬件实现,并解决经典Perlin噪声中存在的错误,到2001年,Ken Perlin再次对原始的噪声算法进行了改进,称之为Simplex噪声[2](Simplex Noise),这两种算法都可以称为Perlin噪声。但是,我们有时候也把分形噪声也称为Perlin噪声[3],甚至在严肃的学术论文中都有这种称法[4]。为了避免歧义,本文指的Perlin噪声特指经典Perlin噪声和Simplex噪声。
Stefan Gustavson[8]指出:Simplex噪声有更小的算法复杂度,要求更少的乘法,在N维空间上,经典Perlin噪声的算法复杂度为log(2N),但是Simplex噪声的算法复杂度为log(N2)等优点。虽然Stefan Gustavson[8]提供了对Simplex算法的注解,但是我依然不能理解Simplex噪声背后的数学原理,对Simplex噪声不作进一步阐述。
2. 经典Perlin噪声
经典Perlin噪声是Ken Perlin在1983年提出的噪声,Ken Perlin提供了一维、二维、三维算法的C实现[5],我们无法仅从他提供的代码理解其数学原理,Matt Zucker[6]从数学原理的角度,对经典Perlin噪声进行了解读。本节将详细介绍二维、三维的Pernlin噪声的数学原理,算法的C语言实现源码如下所示:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
|
/* coherent noise function over 1, 2 or 3 dimensions *//* (copyright Ken Perlin) */#include <stdlib.h>#include <stdio.h>#include <math.h>#define B 0x100#define BM 0xff#define N 0x1000#define NP 12 /* 2^N */#define NM 0xfffstatic p[B + B + 2];static float g3[B + B + 2][3];static float g2[B + B + 2][2];static float g1[B + B + 2];static start = 1;static void init(void);#define s_curve(t) ( t * t * (3. - 2. * t) )#define lerp(t, a, b) ( a + t * (b - a) )#define setup(i,b0,b1,r0,r1)\ t = vec[i] + N;\ b0 = ((int)t) & BM;\ b1 = (b0+1) & BM;\ r0 = t - (int)t;\ r1 = r0 - 1.;double noise1(double arg){ int bx0, bx1; float rx0, rx1, sx, t, u, v, vec[1]; vec[0] = arg; if (start) { start = 0; init(); } setup(0, bx0,bx1, rx0,rx1); sx = s_curve(rx0); u = rx0 * g1[ p[ bx0 ] ]; v = rx1 * g1[ p[ bx1 ] ]; return lerp(sx, u, v);}float noise2(float vec[2]){ int bx0, bx1, by0, by1, b00, b10, b01, b11; float rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v; register i, j; if (start) { start = 0; init(); } setup(0, bx0,bx1, rx0,rx1); setup(1, by0,by1, ry0,ry1); i = p[ bx0 ]; j = p[ bx1 ]; b00 = p[ i + by0 ]; b10 = p[ j + by0 ]; b01 = p[ i + by1 ]; b11 = p[ j + by1 ]; sx = s_curve(rx0); sy = s_curve(ry0);#define at2(rx,ry) ( rx * q[0] + ry * q[1] ) q = g2[ b00 ] ; u = at2(rx0,ry0); q = g2[ b10 ] ; v = at2(rx1,ry0); a = lerp(sx, u, v); q = g2[ b01 ] ; u = at2(rx0,ry1); q = g2[ b11 ] ; v = at2(rx1,ry1); b = lerp(sx, u, v); return lerp(sy, a, b);}float noise3(float vec[3]){ int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11; float rx0, rx1, ry0, ry1, rz0, rz1, *q, sy, sz, a, b, c, d, t, u, v; register i, j; if (start) { start = 0; init(); } setup(0, bx0,bx1, rx0,rx1); setup(1, by0,by1, ry0,ry1); setup(2, bz0,bz1, rz0,rz1); i = p[ bx0 ]; j = p[ bx1 ]; b00 = p[ i + by0 ]; b10 = p[ j + by0 ]; b01 = p[ i + by1 ]; b11 = p[ j + by1 ]; t = s_curve(rx0); sy = s_curve(ry0); sz = s_curve(rz0);#define at3(rx,ry,rz) ( rx * q[0] + ry * q[1] + rz * q[2] ) q = g3[ b00 + bz0 ] ; u = at3(rx0,ry0,rz0); q = g3[ b10 + bz0 ] ; v = at3(rx1,ry0,rz0); a = lerp(t, u, v); q = g3[ b01 + bz0 ] ; u = at3(rx0,ry1,rz0); q = g3[ b11 + bz0 ] ; v = at3(rx1,ry1,rz0); b = lerp(t, u, v); c = lerp(sy, a, b); q = g3[ b00 + bz1 ] ; u = at3(rx0,ry0,rz1); q = g3[ b10 + bz1 ] ; v = at3(rx1,ry0,rz1); a = lerp(t, u, v); q = g3[ b01 + bz1 ] ; u = at3(rx0,ry1,rz1); q = g3[ b11 + bz1 ] ; v = at3(rx1,ry1,rz1); b = lerp(t, u, v); d = lerp(sy, a, b); return lerp(sz, c, d);}static void normalize2(float v[2]){ float s; s = sqrt(v[0] * v[0] + v[1] * v[1]); v[0] = v[0] / s; v[1] = v[1] / s;}static void normalize3(float v[3]){ float s; s = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); v[0] = v[0] / s; v[1] = v[1] / s; v[2] = v[2] / s;}static void init(void){ int i, j, k; for (i = 0 ; i < B ; i++) { p[i] = i; g1[i] = (float)((random() % (B + B)) - B) / B; for (j = 0 ; j < 2 ; j++) g2[i][j] = (float)((random() % (B + B)) - B) / B; normalize2(g2[i]); for (j = 0 ; j < 3 ; j++) g3[i][j] = (float)((random() % (B + B)) - B) / B; normalize3(g3[i]); } while (--i) { k = p[i]; p[i] = p[j = random() % B]; p[j] = k; } for (i = 0 ; i < B + 2 ; i++) { p[B + i] = p[i]; g1[B + i] = g1[i]; for (j = 0 ; j < 2 ; j++) g2[B + i][j] = g2[i][j]; for (j = 0 ; j < 3 ; j++) g3[B + i][j] = g3[i][j]; }} |
- 给定点输入点p;
- 对于每一个与点p相邻的方格端点:
- 挑选一个伪随机梯度向量;
- 计算点积;
- 每个维度上,均采用缓和曲线,计算出加权平均值,缓和曲线可以选用3t2−2t3。
2.1. 二维空间
1. 找到方格点

图1. 包围点(x,y)的方格
2. 挑选伪随机梯度向量
- 生成一个随机排列表,P[n];
- 生成一个随机梯度表,G[n];
- G(i,j)=G[(i+P[j])modn];
在实际应用中,我们不需要求模运算,设n=256,通过位运算,留下低8位的值就行,即G(i,j)=G[(i+P[j])&0xff]。由方格的四个端点,我们可以得出对应的伪随机梯度向量,如图2所示。

图2. 伪随机梯度向量
3. 计算点积

图3. 由点(x,y)到方格四个端点的向量
4. 计算加权平均值

图4. f(x)=3x2−2x3曲线
Noise(x,y)=a+(b−a)⋅sy
2.2. 三维空间

图5. 三维空间上的方格
Noise(x,y,z)=z0+(z1−z0)⋅sz
2.3. 实现代码
最后,提供一个经典Perlin噪声的C++实现代码,如下所示:
SrClassicalPerlinNoise.h
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
/************************************************************************ \link www.twinklingstar.cn\author twinklingstar\file SrClassicalPerlinNoise.h\date 2015/07/15****************************************************************************/#ifndef SR_CLASSIC_PERLIN_NOISE_H_#define SR_CLASSIC_PERLIN_NOISE_H_#define SR_PERLIN_N 0x100#define SR_PERLIN_MASK 0xff#define SR_PERLIN_PI 3.141592653589793typedef float real;typedef unsigned char ubyte;class SrPerlinNoise{public: SrPerlinNoise(){}protected: real ease(real x); int permutate(int x); real lerp(real t, real value0, real value1);protected: static ubyte mP[SR_PERLIN_N];};class SrClassicalPerlinNoise1D:public SrPerlinNoise{public: static SrClassicalPerlinNoise1D* create(); real noise(real x);private: int index(int ix); real lattice(int ix, real fx); static void init(); SrClassicalPerlinNoise1D(){} SrClassicalPerlinNoise1D(const SrClassicalPerlinNoise1D&); SrClassicalPerlinNoise1D& operator = (const SrClassicalPerlinNoise1D&);private: static real mG[SR_PERLIN_N]; static bool mIsInit;};class SrClassicalPerlinNoise2D:public SrPerlinNoise{public: static SrClassicalPerlinNoise2D* create(); real noise(real x, real y);private: int index(int ix, int iy); real lattice(int ix, int iy, real fx, real fy); static void init(); SrClassicalPerlinNoise2D(){} SrClassicalPerlinNoise2D(const SrClassicalPerlinNoise2D&); SrClassicalPerlinNoise2D& operator = (const SrClassicalPerlinNoise2D&);private: static real mG[SR_PERLIN_N][2]; static bool mIsInit;};class SrClassicalPerlinNoise3D:public SrPerlinNoise{public: static SrClassicalPerlinNoise3D* create(); real noise(real x, real y, real z);;private: int index(int ix, int iy, int iz); real lattice(int ix, int iy, int iz, float fx, float fy, float fz); static void init(); SrClassicalPerlinNoise3D(){} SrClassicalPerlinNoise3D(const SrClassicalPerlinNoise3D&); SrClassicalPerlinNoise3D& operator = (const SrClassicalPerlinNoise3D&);private: static real mG[SR_PERLIN_N][3]; static bool mIsInit;};#endif |
SrClassicalPerlinNoise.cpp
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
|
/************************************************************************ \link www.twinklingstar.cn\author twinklingstar\file SrClassicalPerlinNoise.cpp\date 2015/07/15****************************************************************************/#include <stdio.h>#include <math.h>#include <stdlib.h>#include "SrClassicalPerlinNoise.h"/************************************************************************//* class SrPerlinNoise *//************************************************************************/real SrPerlinNoise::ease(real x){ return (real)(x * x * (3 - 2 * x));}int SrPerlinNoise::permutate(int x){ return mP[x & SR_PERLIN_MASK];}real SrPerlinNoise::lerp(real t, real value0, real value1){ return (real)(value0 + t * (value1 - value0));}ubyte SrPerlinNoise::mP[SR_PERLIN_N] = {151,160,137,91,90,15, 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180};/************************************************************************//* class SrClassicalPerlinNoise1D *//************************************************************************/SrClassicalPerlinNoise1D* SrClassicalPerlinNoise1D::create(){ if(!mIsInit) { mIsInit = true; init(); } return (new SrClassicalPerlinNoise1D());}real SrClassicalPerlinNoise1D::noise(real x){ int x0 = (int)floor(x); real fx0 = x - x0; real fx1 = fx0 - 1; real sx = ease(fx0); real u = lattice(x0 , fx0); real v = lattice(x0 + 1, fx1); return lerp(sx, u, v);}int SrClassicalPerlinNoise1D::index(int ix){ return permutate(ix);}real SrClassicalPerlinNoise1D::lattice(int ix, real fx){ int indx = index(ix); return (real)(mG[indx] * fx );}void SrClassicalPerlinNoise1D::init(){ int i; for (i = 0 ; i < SR_PERLIN_N ; i++) { mG[i] = (float)((rand() % (SR_PERLIN_N + SR_PERLIN_N)) - SR_PERLIN_N) / SR_PERLIN_N; }}bool SrClassicalPerlinNoise1D::mIsInit = false;real SrClassicalPerlinNoise1D::mG[SR_PERLIN_N] = {};/************************************************************************//* class SrClassicalPerlinNoise2D *//************************************************************************/SrClassicalPerlinNoise2D* SrClassicalPerlinNoise2D::create(){ if(!mIsInit) { mIsInit = true; init(); } return (new SrClassicalPerlinNoise2D());}real SrClassicalPerlinNoise2D::noise(real x, real y){ int x0 = (int)floor(x); real fx0 = x - x0; real fx1 = fx0 - 1; real sx = ease(fx0); int y0 = (int)floor(y); real fy0 = y - y0; real fy1 = fy0 - 1; real sy = ease(fy0); real s = lattice(x0 ,y0 , fx0, fy0); real t = lattice(x0 + 1,y0 , fx1, fy0); real u = lattice(x0 ,y0 + 1, fx0, fy1); real v = lattice(x0 + 1,y0 + 1, fx1, fy1); real a = lerp(sx, s, t); real b = lerp(sx, u, v); return lerp(sy, a, b);}int SrClassicalPerlinNoise2D::index(int ix, int iy){ return permutate(ix + permutate(iy));}real SrClassicalPerlinNoise2D::lattice(int ix, int iy, real fx, real fy){ int indx = index(ix, iy); return (real)(mG[indx][0] * fx + mG[indx][1] * fy);}void SrClassicalPerlinNoise2D::init(){ int i, j; real s; for (i = 0 ; i < SR_PERLIN_N ; i++) { for (j = 0 ; j < 2 ; j++) mG[i][j] = (float)((rand() % (SR_PERLIN_N + SR_PERLIN_N)) - SR_PERLIN_N) / SR_PERLIN_N; s = sqrt((real)(mG[i][0] * mG[i][0] + mG[i][1] * mG[i][1])); mG[i][0] = mG[i][0] / s; mG[i][1] = mG[i][1] / s; }}bool SrClassicalPerlinNoise2D::mIsInit = false;real SrClassicalPerlinNoise2D::mG[SR_PERLIN_N][2] = {};/************************************************************************//* class SrClassicalPerlinNoise3D *//************************************************************************/SrClassicalPerlinNoise3D* SrClassicalPerlinNoise3D::create(){ if(!mIsInit) { mIsInit = true; init(); } return (new SrClassicalPerlinNoise3D());}real SrClassicalPerlinNoise3D::noise(real x, real y, real z){ int x0 = (int)floor(x); real fx0 = x - x0; real fx1 = fx0 - 1; real wx = ease(fx0); int y0 = (int)floor(y); real fy0 = y - y0; real fy1 = fy0 - 1; real wy = ease(fy0); int z0 = (int)floor(z); real fz0 = z - z0; real fz1 = fz0 - 1; real wz = ease(fz0); real vx0 = lattice(x0 , y0, z0, fx0, fy0, fz0); real vx1 = lattice(x0 + 1, y0, z0, fx1, fy0, fz0); real vy0 = lerp(wx, vx0, vx1); vx0 = lattice(x0 , y0 + 1, z0, fx0, fy1, fz0); vx1 = lattice(x0 + 1, y0 + 1, z0, fx1, fy1, fz0); real vy1 = lerp(wx, vx0, vx1); real vz0 = lerp(wy, vy0, vy1); vx0 = lattice(x0 , y0, z0 + 1, fx0, fy0, fz1); vx1 = lattice(x0 + 1, y0, z0 + 1, fx1, fy0, fz1); vy0 = lerp(wx, vx0, vx1); vx0 = lattice(x0 , y0 + 1, z0 + 1, fx0, fy1, fz1); vx1 = lattice(x0 + 1, y0 + 1, z0 + 1, fx1, fy1, fz1); vy1 = lerp(wx, vx0, vx1); real vz1 = lerp(wy, vy0, vy1); return lerp(wz, vz0, vz1);}int SrClassicalPerlinNoise3D::index(int ix, int iy, int iz){ return permutate(ix + permutate(iy + permutate(iz)));}real SrClassicalPerlinNoise3D::lattice(int ix, int iy, int iz, float fx, float fy, float fz){ int indx = index(ix, iy, iz); return (real)(mG[indx][0] * fx + mG[indx][1] * fy + mG[indx][2] * fz);}void SrClassicalPerlinNoise3D::init(){ int i; for (i = 0 ; i < SR_PERLIN_N ; i++) { real z = 1.0f - 2.0f * ((real)::rand()/((real)RAND_MAX+1)); real r = (real)sqrt(1.0f - z * z); real theta = 2 * (real)SR_PERLIN_PI * ((real)::rand()/((real)RAND_MAX+1)); mG[i][0] = r * (real)cos(theta); mG[i][1] = r * (real)sin(theta); mG[i][2] = z; }}bool SrClassicalPerlinNoise3D::mIsInit = false;real SrClassicalPerlinNoise3D::mG[SR_PERLIN_N][3] = {}; |
3. 地形的应用
float3x3[9]描述了一种基于经典Perlin噪声的地形高度生成算法,具体参见链接对该算法的描述,这里提供DirectX的地形实现类的代码,整个工程代码参见github链接(https://github.com/twinklingstar20/twinklingstar_cn_PerlinNoise)。
实现的效果图6所示,滚动鼠标或者按键“A”, “W”, “S”, “D” , “U” , “J”或者按住鼠标左键并移动鼠标,可以控制视角。

图6. 基于经典Perlin噪声的地形
SrTerrain.h
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
/************************************************************************ \link www.twinklingstar.cn\author twinklingstar\file SrTerrain.h\date 2015/05/20****************************************************************************/#ifndef SR_TERRAIN_H_#define SR_TERRAIN_H_#include <math.h>#include <stdlib.h>#include "SrClassicalPerlinNoise.h"#include "SrModel.h"//Terrain parameter.struct SrTerrainParam{ float mXSize; float mYSize; float mZSize; D3DXVECTOR3 mCenter; int mNumRows; int mNumCols; float mF; float mD; float mErode;};/**\brief Generate terrain based on Perlin noise. */class SrTerrain:public SrModel{private: struct TERRAINVERTEX { D3DXVECTOR3 mPos; D3DXVECTOR3 mNormal; D3DXVECTOR2 mTexture; }; #define D3DFVF_TERRAIN_VERTEX (D3DFVF_XYZ | D3DFVF_NORMAL| D3DFVF_TEX1)public: SrTerrain(const TCHAR* file, const SrTerrainParam& para); virtual bool init(IDirect3DDevice9* pd3dDevice); virtual void render(IDirect3DDevice9* pd3dDevice) const; ~SrTerrain();protected: void calHeightMap(float *h, D3DXVECTOR3* m); /** \brief Calculate the normal of each vertex. */ void calNormal(D3DXVECTOR3* m, D3DXVECTOR3* norm); /** \brief Calculate the texture coordinates of each vertex. */ void calTextureCoord(D3DXVECTOR2* coords); void calVertes(TERRAINVERTEX* v, D3DXVECTOR3 * m, D3DXVECTOR3* n, D3DXVECTOR2* coords); void buildMesh(IDirect3DDevice9* pd3dDevice); /** \brief Calculate the height. */ void calHeight(float* height); void addPerlinNoise(float* height, float f, float base = 10.0f); void perturb(float* height, float f, float d); void erode(float * height, float smoothness); void smoothen(float * height);private: SrClassicalPerlinNoise3D* mPerlin; LPDIRECT3DVERTEXBUFFER9 mVB; LPDIRECT3DTEXTURE9 mTexture; SrTerrainParam mParam; TCHAR mTextureFile[MAX_PATH];};#endif |
SrTerrain.cpp
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
|
/************************************************************************ \link www.twinklingstar.cn\author twinklingstar\file SrTerrain.cpp\date 2015/05/20****************************************************************************/#include "SrTerrain.h"SrTerrain::SrTerrain(const TCHAR* file,const SrTerrainParam& para):SrModel(){ mPerlin = SrClassicalPerlinNoise3D::create(); mTexture = NULL; mParam = para; float x = mParam.mCenter.x - mParam.mXSize * 0.5f; float z = mParam.mCenter.z - mParam.mZSize * 0.5f; float y = mParam.mCenter.y - mParam.mYSize * 0.5f; D3DXMatrixTranslation(&mMatrix, x, y, z); lstrcpy(mTextureFile, file);}bool SrTerrain::init(IDirect3DDevice9* pd3dDevice){ buildMesh(pd3dDevice); D3DXCreateTextureFromFile( pd3dDevice,mTextureFile, &mTexture); return true;}void SrTerrain::calHeightMap(float *h, D3DXVECTOR3* m){ int r, c, indx = 0; float cstep = (float)mParam.mXSize / (float)mParam.mNumCols; float rstep = (float)mParam.mZSize / (float)mParam.mNumRows; for(r = 0; r < mParam.mNumRows ; r += 1) { for(c = 0 ; c < mParam.mNumCols ; c += 1) { m[indx].x = c * cstep; m[indx].y = h[indx]; m[indx].z = r * rstep; indx += 1; } }}void SrTerrain::calNormal(D3DXVECTOR3* m, D3DXVECTOR3* norm){ int r, c, count = 0; float len; D3DXVECTOR3 v1, v2, v3, s1, s2, n, sum; D3DXVECTOR3* normals = new D3DXVECTOR3[(mParam.mNumRows - 1) * (mParam.mNumCols - 1)]; for(r = 0; r < mParam.mNumRows - 1 ; r += 1) { for(c = 0 ; c < mParam.mNumCols - 1 ; c += 1) { v1 = m[(r * mParam.mNumCols) + c]; v2 = m[(r * mParam.mNumCols) + c + 1]; v3 = m[((r + 1) * mParam.mNumCols) + c]; s1 = v3 - v1; s2 = v2 - v1; n.x = (s1.y * s2.z) - (s1.z * s2.y); n.y = (s1.z * s2.x) - (s1.x * s2.z); n.z = (s1.x * s2.y) - (s1.y * s2.x); normals[r * (mParam.mNumCols - 1) + c] = n; } } for(r = 0; r < mParam.mNumRows ; r += 1) { for(c = 0 ; c < mParam.mNumCols ; c += 1) { sum = D3DXVECTOR3(0,0,0); count = 0; if(((r - 1) >= 0) && (c - 1) >= 0) { sum += normals[(r - 1) * (mParam.mNumCols - 1) + c - 1]; count += 1; } if((r < (mParam.mNumRows - 1)) && ((c - 1) >= 0)) { sum += normals[r * (mParam.mNumCols - 1) + c - 1]; count += 1; } if(((r - 1) >= 0) && (c < (mParam.mNumCols - 1))) { sum += normals[(r - 1) * (mParam.mNumCols - 1) + c]; count += 1; } if((r < (mParam.mNumRows - 1)) && (c < (mParam.mNumCols - 1))) { sum += normals[r * (mParam.mNumCols - 1) + c]; count += 1; } sum /= (float) count; len = sqrt(sum.x * sum.x + sum.y * sum.y + sum.z * sum.z); norm[r * mParam.mNumCols + c]= (sum / len); } } delete []normals;}void SrTerrain::calTextureCoord(D3DXVECTOR2* coords){ int r, c, indx = 0; for(r = 0; r < mParam.mNumRows ; r += 1) { for(c = 0 ; c < mParam.mNumCols ; c += 1) { coords[indx] = D3DXVECTOR2((float)c/(float)mParam.mNumCols * 10.0f, (1 - (float)r/(float)mParam.mNumRows) * 10.0f); indx += 1; } }}void SrTerrain::calVertes(TERRAINVERTEX* v, D3DXVECTOR3 * m, D3DXVECTOR3* n, D3DXVECTOR2* coords){ int r, c, indx1, indx2, indx3, indx4; int indx = 0; for( r = 0; r < mParam.mNumRows - 1; r += 1) { for(c = 0 ; c < mParam.mNumCols - 1 ; c += 1) { indx1 = r * mParam.mNumCols + c; indx2 = (r + 1) * mParam.mNumCols + c; indx3 = r * mParam.mNumCols + (c + 1); indx4 = (r + 1) * mParam.mNumCols + (c + 1); //Triangle 1. v[indx].mPos = m[indx1]; v[indx].mNormal = n[indx1]; v[indx].mTexture = coords[indx1]; indx += 1; v[indx].mPos = m[indx2]; v[indx].mNormal = n[indx2]; v[indx].mTexture = coords[indx2]; indx += 1; v[indx].mPos = m[indx4]; v[indx].mNormal = n[indx4]; v[indx].mTexture = coords[indx4]; indx += 1; //Triangle 2. v[indx].mPos = m[indx1]; v[indx].mNormal = n[indx1]; v[indx].mTexture = coords[indx1]; indx += 1; v[indx].mPos = m[indx4]; v[indx].mNormal = n[indx4]; v[indx].mTexture = coords[indx4]; indx += 1; v[indx].mPos = m[indx3]; v[indx].mNormal = n[indx3]; v[indx].mTexture = coords[indx3]; indx += 1; } }}void SrTerrain::buildMesh(IDirect3DDevice9* pd3dDevice) { int count = mParam.mNumCols * mParam.mNumRows; float *h = new float[count]; D3DXVECTOR3* n = new D3DXVECTOR3[count]; D3DXVECTOR3* m = new D3DXVECTOR3[count]; D3DXVECTOR2* coords = new D3DXVECTOR2[count]; memset(h, 0, sizeof(float) * count); calHeight(h); calHeightMap(h, m); calNormal(m, n); calTextureCoord(coords); TERRAINVERTEX* v; int numVerts = (mParam.mNumRows - 1) * (mParam.mNumCols - 1); pd3dDevice->CreateVertexBuffer(6 * numVerts * sizeof(TERRAINVERTEX), 0, D3DFVF_TERRAIN_VERTEX, D3DPOOL_MANAGED, &mVB, NULL ); mVB->Lock( 0, 6 * numVerts * sizeof(TERRAINVERTEX), (void**)&v, 0 ); calVertes(v, m, n, coords); mVB->Unlock(); delete []h; delete []n; delete []m; delete []coords;}void SrTerrain::render(IDirect3DDevice9* pd3dDevice)const{ pd3dDevice->SetTransform( D3DTS_WORLD, &mMatrix ); pd3dDevice->SetTexture(0, mTexture); pd3dDevice->SetStreamSource( 0, mVB, 0, sizeof(TERRAINVERTEX) ); pd3dDevice->SetFVF( D3DFVF_TERRAIN_VERTEX ); pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 2 * (mParam.mNumCols - 1) * (mParam.mNumRows - 1));}SrTerrain::~SrTerrain(){ SAFE_RELEASE(mTexture); SAFE_RELEASE(mVB); delete mPerlin;}void SrTerrain::calHeight(float* height){ memset(height, 0, sizeof(float) * mParam.mNumCols * mParam.mNumRows); addPerlinNoise(height, mParam.mYSize); perturb(height, mParam.mF, mParam.mD); for (int i = 0; i < 10; i++ ) erode(height, mParam.mErode); smoothen(height);}void SrTerrain::addPerlinNoise(float* height, float f, float base){ int r, c; for (r = 0; r < mParam.mNumRows; r += 1) { for (c = 0; c < mParam.mNumCols; c += 1) { height[r * mParam.mNumCols + c] += base * mPerlin->noise(f * r / (float)mParam.mNumRows, f * c / (float)mParam.mNumCols, 0); } }}void SrTerrain::perturb(float* height, float f, float d){ int u, v, i, j; float* tmp = new float[mParam.mNumCols * mParam.mNumRows]; for (i = 0; i < mParam.mNumRows; i += 1) { for (j = 0; j < mParam.mNumCols; j += 1) { u = i + (int)(mPerlin->noise(f * i / (float)mParam.mNumRows, f * j / (float)mParam.mNumCols, 0) * d); v = j + (int)(mPerlin->noise(f * i / (float)mParam.mNumRows, f * j / (float)mParam.mNumCols, 1) * d); if (u < 0) u = 0; if (u >= mParam.mNumRows) u = mParam.mNumRows - 1; if (v < 0) v = 0; if (v >= mParam.mNumCols) v = mParam.mNumCols - 1; tmp[i * mParam.mNumCols + j] = height[u * mParam.mNumCols + v]; } } memcpy(height, tmp, sizeof(float) * mParam.mNumCols * mParam.mNumRows); delete []tmp;}void SrTerrain::erode(float * height, float smoothness){ int i, j; for (i = 1; i < mParam.mNumRows - 1; i++) { for (j = 1; j < mParam.mNumCols - 1; j++) { float d_max = 0.0f; int match[] = {0, 0}; for (int u = -1; u <= 1; u++) { for (int v = -1; v <= 1; v++) { if(abs(u) +abs(v) > 0) { float d_i = height[i * mParam.mNumCols + j] - height[(i + u) * mParam.mNumCols + (j + v)]; if (d_i > d_max) { d_max = d_i; match[0] = u; match[1] = v; } } } } if(0 < d_max && d_max <= smoothness) { float d_h = 0.5f * d_max; height[i * mParam.mNumCols + j] -= d_h; height[(i + match[0]) * mParam.mNumCols + (j + match[1])] += d_h; } } }}void SrTerrain::smoothen(float * height){ for (int i = 1; i < mParam.mNumRows - 1; ++i) { for (int j = 1; j < mParam.mNumCols - 1; ++j) { float total = 0.0f; for (int u = -1; u <= 1; u++) { for (int v = -1; v <= 1; v++) { total += height[(i + u) * mParam.mNumCols + (j + v)]; } } height[i * mParam.mNumCols + j] = total / 9.0f; } }} |
参考
- Ken Perlin. "An image synthesizer." ACM Siggraph Computer Graphics, vol.19, no.3, pp.287-296, 1985.
- Ken Perlin. "Improving noise." ACM Transactions on Graphics (TOG), vol.21, no.3, 2002.
- Hugo Elias. “Perlin Noise”. website <http://freespace.virgin.net/hugo.elias/models/m_perlin.htm>.
- “Perlin 噪声.” website <https://zh.wikipedia.org/wiki/Perlin%E5%99%AA%E5%A3%B0>.
- Ken Perlin. “Noise and Turbulence.” website <http://www.mrl.nyu.edu/~perlin/doc/oscar.html>.
- Matt Zucker. “The Perlin noise math FAQ.” website <http://webstaff.itn.liu.se/~stegu/TNM022-2005/perlinnoiselinks/perlin-noise-math-faq.html>.
- Ken Perlin. “Making Noise.” website <http://www.noisemachine.com/talk1/>.
- Stefan Gustavson. "Simplex noise demystified." Linköping University, Linköping, Sweden, Research Report, 2005.
- “Generating realistic and playable terrain height”. website <http://www.float4x4.net/index.php/2010/06/generating-realistic-and-playable-terrain-height-maps/>.

More Articles
posted on 2018-04-23 12:52 fanbird2008 阅读(445) 评论(0) 收藏 举报
浙公网安备 33010602011771号
One comment on “Perlin噪声”
感谢博主的演算过程!想问一个问题:为什么在二维加权的时候,插值计算的结果是 a=s+(t−s)⋅sx; b=u+(v−u)⋅sx呢?为什么是t-s, v-u,而不是s-u, t-v之类呢?是有方向或者其他的限制么?