Codesnippets > Math

Simplex Noise

<< < (2/3) > >>

erico:
Really pretty stuff! :good:

fuzzy70:
Had a play around with the 2D noise today & added some fBM (Fractional Brownian Motion) to roughen things up a bit.

The code is far from optimal (esp the isometric part) as it was merely knocked up in less than an hour for playing purposes & there might be variables which have no use. Also the colour is mapped as a single range (black to red) as I haven't got round to finishing my gradient routine as yet. Uncomment the
--- Code: (glbasic) ---map#[x][y] = simplex.noise2D#(x2,y2)
--- End code ---
part & comment out the fBM line directly below to see the difference & just generally play around with it.

Hopefully the variables are pretty self explanatory but I will more than likely tidy it all up at some stage. It's not really something that should be done in real time but rather pre calculated if you want a large landscape etc.

Enjoy  =D

Lee

--- Code: (glbasic) ---GLOBAL simplex AS TSimplex
GLOBAL map#[]
GLOBAL counter%
GLOBAL mapsize%=48
GLOBAL tilewidth%=16
GLOBAL tileheight%=tilewidth%/2

LOCAL x%,y%,x2#=0,y2#=0
LOCAL timer_start%,timer_end%
LOCAL incamount#=0.04,scroll#=0.0
LOCAL low#=0,high#=0,loop%

DIM map%[mapsize%][mapsize%]

SETSCREEN 640,480,FALSE

simplex.initialise()

FOR loop=0 TO 1000

timer_start=GETTIMERALL()

FOR x=0 TO mapsize%-1

FOR y=0 TO mapsize%-1

//map#[x][y] = simplex.noise2D#(x2,y2)
fBM(x,y,x2,y2,6,2,2.0,0.65)
IF map[x][y]<low# THEN low#=map[x][y]
IF map[x][y]>high# THEN high#=map[x][y]
INC x2,incamount#

NEXT

x2=0+scroll#
INC y2,incamount#

NEXT

y2=0

INC scroll#,0.01

drawmap(30)

timer_end=GETTIMERALL()-timer_start

PRINT timer_end,0,0
PRINT "Low= "+low,0,10
PRINT "High="+high,0,20

SHOWSCREEN

NEXT

KEYWAIT

FUNCTION drawmap: scale#

LOCAL x%,y%,colour%
LOCAL w2 = tilewidth / 2
LOCAL h2 = tileheight /2
LOCAL xoff%=(640/2)-(tilewidth/2)
LOCAL yoff%=(480/2)-((mapsize%*tileheight)/2)

STARTPOLY -1,2

FOR x=0 TO mapsize%-2

FOR y=0 TO mapsize%-2

colour% = ((map#[x][y])+1)*128
IF colour% <=0 THEN colour%=0
IF colour% >=255 THEN colour%=255

POLYVECTOR xoff+((x-y)*w2)  ,yoff%+((x+y)*h2)    - (map#[x][y]*scale)    ,0,0,RGB(colour%,0,0)
POLYVECTOR xoff+((x-1-y)*w2),yoff%+((x+y+1)*h2)  - (map#[x][y+1]*scale)  ,0,0,RGB(colour%,0,0)
POLYVECTOR xoff+((x+1-y)*w2),yoff%+((x+1+y)*h2)  - (map#[x+1][y]*scale)  ,0,0,RGB(colour%,0,0)
POLYVECTOR xoff+((x-y)*w2)  ,yoff%+((x+1+y+1)*h2)- (map#[x+1][y+1]*scale),0,0,RGB(colour%,0,0)

POLYNEWSTRIP

NEXT

NEXT

ENDPOLY

ENDFUNCTION

FUNCTION fBM: x%,y%,nx#,ny#,octaves%,hgrid%,lacunarity#,gain#

LOCAL total# = 0.0
LOCAL frequency# = 1.0/hgrid
LOCAL amplitude# = gain#

FOR i = 0 TO octaves

total = total + simplex.noise2D#(nx * frequency, ny * frequency) * amplitude
frequency = frequency * lacunarity
amplitude = amplitude * gain

NEXT

map[x][y]=total  //now that we have the value, put it in

//Octaves are how many layers you are putting together.
//IF you start with big features, the number of octaves determines how detailed the map will look.

//The frequency of a layer is how many points fit into the space you've created.
//So FOR the mountain scale, you only need a few points, but at the rock scale you may need hundreds of points.
//IN the code above, I start with a small frequency (which equates TO large features) AND move TO
//higher frequencies which produce smaller details.

//The amplitude is how tall the features should be.
//Frequency determines the width of features, amplitude determines the height.
//Each octave the amplitude shrinks, meaning small features are also short.
//this doesn't have TO be the CASE, but FOR this CASE it makes pleasing maps.

//Lacunarity is what makes the frequency grow.
//Each octave the frequency is multiplied by the lacunarity.
//I use a lacunarity of 2.0, however values of 1.8715 OR 2.1042 can help TO reduce artifacts IN some algorithms.
//A lacunarity of 2.0 means that the frequency doubles each octave,
//so IF the first octave had 3 points the second would have 6, THEN 12, THEN 24, etc.
//this is used almost exclusively, partly because octaves IN music double IN frequency.
//Other values are perfectly acceptable, but the results will vary.

//Gain, also called persistence, is what makes the amplitude shrink (OR NOT shrink).
//Each octave the amplitude is multiplied by the gain. I use a gain of 0.65.
//IF it is higher THEN the amplitude will barely shrink, AND maps get crazy.
//Too low AND the details become miniscule, AND the map looks washed out.
//However, most use 1/lacunarity. Since the standard FOR lacunarity is 2.0,
//the standard FOR the gain is 0.5.
//Noise that has a gain of 0.5 AND a lacunarity of 2.0 is referred TO AS 1/f noise, AND is the industry standard.
ENDFUNCTION

// --------------------------------------------------------
//
// Project: Simplex
//
// Version: 1.0
//
// Desc.:   Implementation of Simplex noise for GLBasic
//          Based on C++ implementation by Stefan Gustavson Simplex noise
//
// Author:  Andy White
//          http://www.fivesprites.com
//
// Date:    Friday, June 18, 2012
//
// NOTE: Make sure you call initialise() :)
//
// --------------------------------------------------------

TYPE TSimplex

p[] AS short
perm[] AS short
permMod12[] AS short

F2#
G2#
F3#
G3#
F4#
G4#

FUNCTION noise2D#: xin#, yin#
LOCAL n0#, n1#, n2#
LOCAL s# = (xin + yin) * self.F2
LOCAL i% = FastFloor(xin+s)
LOCAL j% = FastFloor(yin+s)
LOCAL t# = (i + j) * self.G2
LOCAL X0# = i - t
LOCAL Y0# = j - t
LOCAL xx0# = xin - X0
LOCAL yy0# = yin - Y0

LOCAL i1%, j1%

IF (xx0 > yy0)
i1 = 1
j1 = 0
ELSE
i1 = 0
j1 = 1
ENDIF

LOCAL x1# = xx0 - i1 + self.G2
LOCAL y1# = yy0 - j1 + self.G2
LOCAL x2# = xx0 - 1.0 + 2.0 * self.G2
LOCAL y2# = yy0 - 1.0 + 2.0 * self.G2

LOCAL ii% = bAND(i, 255)
LOCAL jj% = bAND(j, 255)
LOCAL gi0% = self.permMod12[ii + self.perm[jj]]
LOCAL gi1% = self.permMod12[ii + i1 + self.perm[jj+j1]]
LOCAL gi2% = self.permMod12[ii + 1 + self.perm[jj + 1]]

LOCAL t0# = 0.5 - xx0 * xx0 - yy0 * yy0
IF (t0 < 0)
n0 = 0.0
ELSE
t0 = t0 * t0
n0 = t0 * t0 * Dot2(self.grad3[gi0], xx0, yy0)
ENDIF

LOCAL t1# = 0.5 - x1 * x1 - y1 * y1
IF (t1 < 0)
n1 = 0.0
ELSE
t1 = t1 * t1
n1 = t1 * t1 * Dot2(self.grad3[gi1], x1, y1)
ENDIF

LOCAL t2# = 0.5 - x2 * x2 - y2 * y2
IF (t2 < 0)
n2 = 0.0
ELSE
t2 = t2 * t2
n2 = t2 * t2 * Dot2(self.grad3[gi2], x2, y2)
ENDIF

RETURN 70.0 * (n0 + n1 + n2)

ENDFUNCTION

FUNCTION noise3D#: xin#, yin#, zin#
LOCAL n0#, n1#, n2#, n3#
LOCAL s# = (xin + yin + zin) * self.F3
LOCAL i% = FastFloor(xin + s)
LOCAL j% = FastFloor(yin + s)
LOCAL k% = FastFloor(zin + s)
LOCAL t# = (i + j + k) * self.G3
LOCAL X0# = i - t
LOCAL Y0# = j - t
LOCAL Z0# = k - t
LOCAL xx0# = xin - X0
LOCAL yy0# = yin - Y0
LOCAL zz0# = zin - Z0
LOCAL i1%, j1%, k1%
LOCAL i2%, j2%, k2%

IF (xx0 >= yy0)
IF (yy0 >= zz0)
i1 = 1
j1 = 0
k1 = 0
i2 = 1
j2 = 1
k2 = 0
ELSEIF (xx0 >= zz0)
i1 = 1
j1 = 0
k1 = 0
i2 = 1
j2 = 0
k2 = 1
ELSE
i1 = 0
j1 = 0
k1 = 1
i2 = 1
j2 = 0
k2 = 1
ENDIF
ELSE
IF (yy0 < zz0)
i1 = 0
j1 = 0
k1 = 1
i2 = 0
j2 = 1
k2 = 1
ELSEIF (xx0 < zz0)
i1 = 0
j1 = 1
k1 = 0
i2 = 0
j2 = 1
k2 = 1
ELSE
i1 = 0
j1 = 1
k1 = 0
i2 = 1
j2 = 1
k2 = 0
ENDIF
ENDIF

LOCAL x1# = xx0 - i1 + self.G3
LOCAL y1# = yy0 - j1 + self.G3
LOCAL z1# = zz0 - k1 + self.G3

LOCAL x2# = xx0 - i2 + 2.0 * self.G3
LOCAL y2# = yy0 - j2 + 2.0 * self.G3
LOCAL z2# = zz0 - k2 + 2.0 * self.G3

LOCAL x3# = xx0 - 1.0 + 3.0 * self.G3
LOCAL y3# = yy0 - 1.0 + 3.0 * self.G3
LOCAL z3# = zz0 - 1.0 + 3.0 * self.G3

LOCAL ii% = bAND(i, 255)
LOCAL jj% = bAND(j, 255)
LOCAL kk% = bAND(k, 255)

LOCAL gi0% = self.permMod12[ii+self.perm[jj+self.perm[kk]]]
LOCAL gi1% = self.permMod12[ii+i1+self.perm[jj+j1+self.perm[kk+k1]]]
LOCAL gi2% = self.permMod12[ii+i2+self.perm[jj+j2+self.perm[kk+k2]]]
LOCAL gi3% = self.permMod12[ii+1+self.perm[jj+1+self.perm[kk+1]]]

LOCAL t0# = 0.6 - xx0 * xx0 - yy0 * yy0 - zz0 * zz0
IF (t0 < 0)
n0 = 0.0
ELSE
t0 = t0 * t0
n0 = t0 * t0 * Dot3(self.grad3[gi0], xx0, yy0, zz0)
ENDIF

LOCAL t1# = 0.6 - x1 * x1 - y1*y1 - z1*z1
IF (t1 < 0)
n1 = 0.0
ELSE
t1 = t1 * t1
n1 = t1 * t1 * Dot3(self.grad3[gi1], x1, y1, z1)
ENDIF

LOCAL t2# = 0.6 - x2 * x2 - y2*y2 - z2*z2
IF (t2 < 0)
n2 = 0.0
ELSE
t2 = t2 * t2
n2 = t2 * t2 * Dot3(self.grad3[gi2], x2, y2, z2)
ENDIF

LOCAL t3# = 0.6 - x3 * x3 - y3 * y3 - z3 * z3
IF (t3 < 0)
n3 = 0.0
ELSE
t3 = t3 * t3
n3 = t3 * t3 * Dot3(self.grad3[gi3], x3, y3, z3)
ENDIF

RETURN 32.0 * (n0 + n1 + n2 + n3)
ENDFUNCTION

FUNCTION SumOctave#: numIterations%, x#, y#, persistence#, scale#, low#, high#
LOCAL maxAmp# = 0.0
LOCAL amp# = 0.0
LOCAL freq# = scale
LOCAL noise# = 0.0

FOR i%=0 TO numIterations-1
noise = noise + noise2D(x * freq, y * freq) * amp
maxAmp = maxAmp + amp
amp = amp * persistence
freq = freq * 2
NEXT

noise = noise / maxAmp
noise = noise * (high-low) / 2.0 + (high + low) / 2.0

RETURN noise
ENDFUNCTION

FUNCTION Dot2#: g AS TSxGrad, x#, y#
RETURN g.x * x + g.y * y
ENDFUNCTION

FUNCTION Dot3#: g AS TSxGrad, x#, y#, z#
RETURN (g.x * x) + (g.y * y) + (g.z * z)
ENDFUNCTION

FUNCTION Dot4#: g AS TSxGrad, x#, y#, z#, w#
RETURN g.x * x + g.y * y + g.z * z + g.w * w
ENDFUNCTION

FUNCTION initialise:

DEBUG "Initialising Simplex noise...  "

self.F2 = 0.5 * (SQR(3.0) - 1.0)
self.G2 = (3.0 - SQR(3.0)) / 6.0
self.F3 = 1.0 / 3.0
self.G3 = 1.0 / 6.0
self.F4 = (SQR(5.0) - 1.0) / 4.0
self.G4 = (5.0 - SQR(5.0)) / 20.0

g30.init ( 1,  1,  0)
g31.init (-1,  1,  0)
g32.init ( 1, -1,  0)
g33.init (-1, -1,  0)

g34.init ( 1,  0,  1)
g35.init (-1,  0,  1)
g36.init ( 1,  0, -1)
g37.init (-1,  0, -1)

g38.init ( 0,  1,  1)
g39.init ( 0, -1,  1)
g310.init( 0,  1, -1)
g311.init( 0, -1, -1)

g40.init ( 0,  1,  1,  1)
g41.init ( 0,  1,  1, -1)
g42.init ( 0,  1, -1,  1)
g43.init ( 0,  1, -1, -1)

g44.init ( 0, -1,  1,  1)
g45.init ( 0, -1,  1, -1)
g46.init ( 0, -1, -1,  1)
g47.init ( 0, -1, -1, -1)

g48.init ( 1,  0,  1,  1)
g49.init ( 1,  0,  1, -1)
g410.init( 1,  0, -1,  1)
g411.init( 1,  0, -1, -1)

g412.init(-1,  0,  1,  1)
g413.init(-1,  0,  1, -1)
g414.init(-1,  0, -1,  1)
g415.init(-1,  0, -1, -1)

g416.init( 1,  1,  0,  1)
g417.init( 1,  1,  0, -1)
g418.init( 1, -1,  0,  1)
g419.init( 1, -1,  0, -1)

g420.init(-1,  1,  0,  1)
g421.init(-1,  1,  0, -1)
g422.init(-1, -1,  0,  1)
g423.init(-1, -1,  0, -1)

g424.init( 1,  1,  1,  0)
g425.init( 1,  1, -1,  0)
g426.init( 1, -1,  1,  0)
g427.init( 1, -1, -1,  0)

g428.init(-1,  1,  1,  0)
g429.init(-1,  1, -1,  0)
g430.init(-1, -1,  1,  0)
g431.init(-1, -1, -1,  0)

DIMDATA self.p[], _
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

REDIM self.perm
REDIM self.permMod12
FOR i%=0 TO 511
self.perm[i] = self.p[bAND(i, 255)]
self.permMod12[i] = MOD(self.perm[i], 12)
NEXT

DEBUG "[DONE]\n"

ENDFUNCTION

ENDTYPE

x#
y#
z#
w#

FUNCTION init: x#, y#, z#, w#=0.0
self.x = x
self.y = y
self.z = z
self.w = w
ENDFUNCTION

ENDTYPE

FUNCTION FastFloor%: x#
LOCAL i_x% = x
IF (x < i_x)
RETURN i_x - 1
ELSE
RETURN i_x
ENDIF
ENDFUNCTION

--- End code ---

mentalthink:
Ey Fuzzy this code it's real nice... I test with a lot of subdivisions and makeing the cell more little to 16x16 and runs well---

Really for implement like a bump , gepmetry or displacement... I think use a image usign this technique can be possible, looking white and black parts of the image...

Very interesting Sniipet, thanks a lot  :booze:

fuzzy70:
Your welcome. Creating a flat 2D greyscale image is very easy for heightmaps/displacement maps etc & as the code stands creating a terrain in GLB is easily done as the data is in the map array. I only done a quick isometric view as I have not played around at all with GLB's 3D commands so quickly knocked out a routine to visualise the output in iso.

After I get my gradient routine finished which will work pretty much the same as most graphic programs version in that you can add various points of colour & it interpolates the colours in-between then I am going to play with the 3D noise I think along with the 3D parts of GLB hopefully.

Lee

Ian Price:
I could have done with this nearly 30 years ago!

I wrote an isometric "game" on my Amstrad CPC, where a tank traversed a 3D landscape, like the one shown in the bottom picture, all created in hand-inputted data. It worked well, but took forever to implement (and was pretty slow). Think Populous but with a tank, 10 years before Populous. Once I had the tank moving on the landscape my mission was completed and it was soon abandoned. I often think of it.