2
// This unit is part of the GLScene Engine https://github.com/glscene
8
23/08/10 - Yar - Added OpenGLTokens to uses, replaced OpenGL1x functions to OpenGLAdapter
9
22/04/10 - Yar - Fixes after GLState revision
10
22/01/10 - Yar - Added bmp32.Blank:=false for memory allocation,
11
Depth dimension, NativeTextureTarget becomes property
12
16/03/07 - DaStr - Added explicit pointer dereferencing
13
(thanks Burkhard Carstens) (Bugtracker ID = 1678644)
14
01/10/04 - ilh - Added SetPermFromData and SetPermToDefault
15
moved PERM array to protected from inside Noise procedure
16
so it can be changed by SetPermFromData and SetPermToDefault
17
Added FNoiseRandSeed property so a Seed can be set
18
Starts with a random Generated Seed...
19
One must be set to BE Seeded
20
11/12/02 - ??? - Initial, procedural perlin noise texture
21
code by Tobias Peirick
24
I used the following references for my implementation:
26
http://freespace.virgin.net/hugo.elias/models/m_perlin.htm
27
http://freespace.virgin.net/hugo.elias/models/m_clouds.htm
28
http://www.delphi3d.net
36
uses Classes, GLTexture, GLGraphics, OpenGLTokens, GLCrossPlatform, SysUtils,
40
GRADIENT_TABLE_SIZE = 256;
44
TGLProcTextureNoise = class(TGLTextureImage)
46
FNoiseMap: TGLBitmap32;
47
FWidth, FHeight: Integer;
50
FNoiseSharpness: Single;
51
FNoiseAnimate: Single;
53
FNoiseRandSeed: Longint;
55
FGradients: array[0..GRADIENT_TABLE_SIZE * 3 - 1] of Single;
56
PERM: array[0..GRADIENT_TABLE_SIZE - 1] of Byte;
57
function GetWidth: Integer; override;
58
function GetHeight: Integer; override;
59
function GetDepth: Integer; override;
60
function GetTextureTarget: TGLTextureTarget; override;
61
function Noise(x, y: Single): Single;
62
procedure SetMinCut(const val: Byte);
63
procedure SetSeamless(const val: Boolean);
64
procedure SetWidth(const val: Integer);
65
procedure SetHeight(const val: Integer);
66
procedure SetNoiseSharpness(const val: Single);
67
procedure SetNoiseRandSeed(const val: Longint);
68
procedure UpdateNoise;
70
constructor Create(AOwner: TPersistent); override;
71
destructor Destroy; override;
72
class function FriendlyName: string; override;
73
class function FriendlyDescription: string; override;
74
procedure Assign(Source: TPersistent); override;
75
function GetBitmap32: TGLBitmap32; override;
76
procedure ReleaseBitmap32; override;
77
procedure SaveToFile(const fileName: string); override;
78
procedure LoadFromFile(const fileName: string); override;
79
procedure NoiseAnimate(speed: Single);
80
procedure SetPermFromData(inPERM: array of Byte);
81
procedure SetPermToDefault;
83
property Width: Integer read GetWidth write SetWidth default 128;
84
property Height: Integer read GetHeight write SetHeight default 128;
85
property Depth: Integer read GetDepth;
86
property MinCut: Byte read FMinCut write SetMinCut;
87
property NoiseSharpness: Single read FNoiseSharpness write
89
property Seamless: Boolean read FSeamless write SetSeamless;
90
property NoiseRandSeed: Longint read FNoiseRandSeed write SetNoiseRandSeed;
93
// ------------------------------------------------------------------
94
// ------------------------------------------------------------------
95
// ------------------------------------------------------------------
97
// ------------------------------------------------------------------
98
// ------------------------------------------------------------------
99
// ------------------------------------------------------------------
101
uses GLVectorGeometry;
103
constructor TGLProcTextureNoise.Create(AOwner: TPersistent);
104
{ PERM array Borrowed from Darwyn Peachey.
105
The gradient table is indexed with an XYZ triplet, which is first turned
106
into a single random index using a lookup in PERM array. The PERM array simply
107
contains all numbers in [0..255] in random order. }
108
//Can now be set to a different set of Random arrangement
118
FNoiseSharpness := 0.99;
120
seedBackup := RandSeed;
122
FNoiseRandSeed := Random(2147483647); //Random(10000);
123
RandSeed := FNoiseRandSeed;
125
// Generate random gradient vectors.
126
for i := 0 to GRADIENT_TABLE_SIZE - 1 do
129
r := sqrt(1 - z * z);
130
theta := 2 * PI * Random;
131
FGradients[i * 3] := r * cos(theta);
132
FGradients[i * 3 + 1] := r * sin(theta);
133
FGradients[i * 3 + 2] := z;
135
RandSeed := seedBackup;
138
destructor TGLProcTextureNoise.Destroy;
144
procedure TGLProcTextureNoise.UpdateNoise;
147
Line: PGLPixel32Array;
151
function NoiseSeamless(Scale: Single): Single;
153
Result := (Noise(x / Scale, y / Scale) * (Width - x) * (Height - y)
154
+ Noise((x - width) / Scale, y / Scale) * x * (Height - y)
155
+ Noise((x - width) / Scale, (y - Height) / Scale) * x * y
156
+ Noise(x / Scale, (y - Height) / Scale) * (Width - x) * y)
161
// Update the noise texture.
162
for y := 0 to FNoiseMap.Height - 1 do
164
Line := FNoiseMap.ScanLine[y];
165
for x := 0 to FNoiseMap.Width - 1 do
170
// Take 4 octaves of noise and add them weighted for seamless.
174
nf := NoiseSeamless(16)
175
+ NoiseSeamless(8) / 2
176
+ NoiseSeamless(4) / 4
177
+ NoiseSeamless(2) / 8;
179
// Take 4 octaves of noise and add them.
182
nf := Noise(x / 16, y / 16)
183
+ Noise(x / 8, y / 8) / 2
184
+ Noise(x / 4, y / 4) / 4
185
+ Noise(x / 2, y / 2) / 8;
189
// Range between 0 and 255
190
n := Round(255 * (nf + 1) / 2);
199
n := 255 - Round(IntPower(FNoiseSharpness, C) * 255);
201
//if n < 13 then n:=13;
202
// Write the result to the texture image.
211
function TGLProcTextureNoise.GetBitmap32: TGLBitmap32;
213
if not Assigned(FNoiseMap) then
215
FNoiseMap := TGLBitmap32.Create;
216
FNoiseMap.Width := FWidth;
217
FNoiseMap.Height := FHeight;
218
FNoiseMap.Blank := false;
227
class function TGLProcTextureNoise.FriendlyName: string;
229
Result := 'Procedural Noise';
232
// FriendlyDescription
235
class function TGLProcTextureNoise.FriendlyDescription: string;
237
Result := 'Procedural Noise (Animated)';
240
procedure TGLProcTextureNoise.SetSeamless(const val: Boolean);
242
if val <> FSeamless then
249
procedure TGLProcTextureNoise.LoadFromFile(const fileName: string);
251
Assert(False, 'TGLProcTextureNoise.LoadFromFile not implemented');
254
procedure TGLProcTextureNoise.ReleaseBitmap32;
256
if Assigned(FNoiseMap) then
263
procedure TGLProcTextureNoise.SaveToFile(const fileName: string);
268
function TGLProcTextureNoise.GetHeight: Integer;
273
function TGLProcTextureNoise.GetWidth: Integer;
278
function TGLProcTextureNoise.GetDepth: Integer;
286
function TGLProcTextureNoise.GetTextureTarget: TGLTextureTarget;
288
Result := ttTexture2D;
291
procedure TGLProcTextureNoise.SetHeight(const val: Integer);
293
if val <> FHeight then
302
procedure TGLProcTextureNoise.SetWidth(const val: Integer);
304
if val <> FWidth then
313
procedure TGLProcTextureNoise.SetMinCut(const val: Byte);
315
if val <> FMinCut then
322
procedure TGLProcTextureNoise.SetNoiseSharpness(const val: Single);
324
if val <> FNoiseSharpness then
326
FNoiseSharpness := val;
327
if FNoiseSharpness > 1 then
328
FNoiseSharpness := 1;
333
procedure TGLProcTextureNoise.SetNoiseRandSeed(const val: Longint);
339
if val <> FNoiseRandSeed then
341
seedBackup := RandSeed;
342
FNoiseRandSeed := val;
343
//Dunno, might be ok to be negative
344
if FNoiseRandSeed < 1 then
346
RandSeed := FNoiseRandSeed;
347
//didnt change so added/copied FGradients here... to get Seed to work
348
// Generate random gradient vectors.
349
for i := 0 to GRADIENT_TABLE_SIZE - 1 do
352
r := sqrt(1 - z * z);
353
theta := 2 * PI * Random;
354
FGradients[i * 3] := r * cos(theta);
355
FGradients[i * 3 + 1] := r * sin(theta);
356
FGradients[i * 3 + 2] := z;
358
RandSeed := seedBackup;
363
procedure TGLProcTextureNoise.Assign(Source: TPersistent);
365
if Assigned(Source) and (Source is TGLProcTextureNoise) then
367
FWidth := TGLProcTextureNoise(Source).FWidth;
368
FHeight := TGLProcTextureNoise(Source).FHeight;
369
FMinCut := TGLProcTextureNoise(Source).FMinCut;
370
FNoiseSharpness := TGLProcTextureNoise(Source).FNoiseSharpness;
371
FNoiseRandSeed := TGLProcTextureNoise(Source).FNoiseRandSeed;
378
procedure TGLProcTextureNoise.NoiseAnimate(speed: Single);
380
FNoiseAnimate := FNoiseAnimate + speed;
384
function TGLProcTextureNoise.Noise(x, y: Single): Single;
387
fx0, fx1, fy0, fy1, fz0, fz1: Single;
389
vx0, vx1, vy0, vy1, vz0, vz1: Single;
391
function Smooth(x: Single): Single;
393
{ Smoothing curve. This is used to calculate interpolants so that the noise
394
doesn't look blocky when the frequency is low. }
395
Result := x * x * (3 - 2 * x);
398
function Permutate(x: Integer): Integer;
400
MASK = GRADIENT_TABLE_SIZE - 1;
402
// Do a lookup in the permutation table.
403
Result := PERM[x and MASK];
406
function Index(ix, iy, iz: Integer): Integer;
408
// Turn an XYZ triplet into a single gradient table index.
409
Result := Permutate(ix + Permutate(iy + Permutate(iz)));
412
function Lattice(lx, ly, lz: Integer; fx, fy, fz: Single): Single;
416
// Look up a random gradient at [ix,iy,iz] and dot it with the [fx,fy,fz] vector.
417
g := Index(lx, ly, lz) * 3;
418
Result := FGradients[g] * fx + FGradients[g + 1] * fy + FGradients[g + 2] *
422
function Lerp(t, x0, x1: Single): Single;
424
// Simple linear interpolation.
425
Result := x0 + t * (x1 - x0);
429
{ The main noise function. Looks up the pseudorandom gradients at the nearest
430
lattice points, dots them with the input vector, and interpolates the
431
results to produce a single output value in [0, 1] range. }
442
iz := Floor(FNoiseAnimate);
443
fz0 := FNoiseAnimate - iz;
447
vx0 := Lattice(ix, iy, iz, fx0, fy0, fz0);
448
vx1 := Lattice(ix + 1, iy, iz, fx1, fy0, fz0);
449
vy0 := Lerp(wx, vx0, vx1);
451
vx0 := Lattice(ix, iy + 1, iz, fx0, fy1, fz0);
452
vx1 := Lattice(ix + 1, iy + 1, iz, fx1, fy1, fz0);
453
vy1 := Lerp(wx, vx0, vx1);
455
vz0 := Lerp(wy, vy0, vy1);
457
vx0 := Lattice(ix, iy, iz + 1, fx0, fy0, fz1);
458
vx1 := Lattice(ix + 1, iy, iz + 1, fx1, fy0, fz1);
459
vy0 := Lerp(wx, vx0, vx1);
461
vx0 := Lattice(ix, iy + 1, iz + 1, fx0, fy1, fz1);
462
vx1 := Lattice(ix + 1, iy + 1, iz + 1, fx1, fy1, fz1);
463
vy1 := Lerp(wx, vx0, vx1);
465
vz1 := Lerp(wy, vy0, vy1);
467
Result := Lerp(wz, vz0, vz1);
470
procedure TGLProcTextureNoise.SetPermFromData(inPERM: array of Byte);
475
PERM[I] := inPERM[I];
479
procedure TGLProcTextureNoise.SetPermToDefault;
481
//225,155,210,108,175,199,221,144,203,116, 70,213, 69,158, 33,252,
498
//5, 82,173,133,222,139,174, 27, 9, 71, 90,246, 75,130, 91,191,
515
//169,138, 2,151,194,235, 81, 7, 25,113,228,159,205,253,134,142,
532
//248, 65,224,217, 22,121,229, 63, 89,103, 96,104,156, 17,201,129,
549
//36, 8,165,110,237,117,231, 56,132,211,152, 20,181,111,239,218,
566
// 170,163, 51,172,157, 47, 80,212,176,250, 87, 49, 99,242,136,189,
583
//162,115, 44, 43,124, 94,150, 16,141,247, 32, 10,198,223,255, 72,
600
//53,131, 84, 57,220,197, 58, 50,208, 11,241, 28, 3,192, 62,202,
617
//18,215,153, 24, 76, 41, 15,179, 39, 46, 55, 6,128,167, 23,188,
634
// 106, 34,187,140,164, 73,112,182,244,195,227, 13, 35, 77,196,185,
651
//26,200,226,119, 31,123,168,125,249, 68,183,230,177,135,160,180,
668
// 12, 1,243,148,102,166, 38,238,251, 37,240,126, 64, 74,161, 40,
685
// 184,149,171,178,101, 66, 29, 59,146, 61,254,107, 42, 86,154, 4,
702
// 236,232,120, 21,233,209, 45, 98,193,114, 78, 19,206, 14,118,127,
719
// 48, 79,147, 85, 30,207,219, 54, 88,234,190,122, 95, 67,143,109,
736
// 137,214,145, 93, 92,100,245, 0,216,186, 60, 83,105, 97,204, 52
755
// ------------------------------------------------------------------
756
// ------------------------------------------------------------------
757
// ------------------------------------------------------------------
759
// ------------------------------------------------------------------
760
// ------------------------------------------------------------------
761
// ------------------------------------------------------------------
763
RegisterGLTextureImageClass(TGLProcTextureNoise);