GLBasic implementation of Simplex noise. 2D and 3D noise are supported. I'll update with 4D when I get time.
Also provided is very basic demo to show how Simplex noise can be used to affect the velocities of particles.
Left mouse button to create particles, right to clear the screen. Spacebar to toggle the screen hold effect on/off.
I'm sure you guys can put this to better use (generating funky heightmap data etc) and I can't wait to see the results :)
Cheers,
Andy
Thanks! I always wanted some noise-functions, now I can finally try out some things :-)
Very interesting. I had to look up Simplex Noise and it looks that it could be very handy indeed! Thank you Andy! :booze:
Hi very intersting and nice... this can be very ineresting for make pencils por some paint app, in fact seems somthing like accrilict pinture...
Very cool the effect.
Thanks. Very much. =D
Quote from: fivesprites on 2013-Mar-11
I'm sure you guys can put this to better use (generating funky heightmap data etc) and I can't wait to see the results :)
Nor me. :)
Really pretty stuff! :good:
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 map#[x][y] = simplex.noise2D#(x2,y2)
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
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#
// Taken & adapted from the following http://code.google.com/p/fractalterraingeneration/wiki/Fractional_Brownian_Motion
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
grad3[] AS TSxGrad
grad4[] AS TSxGrad
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
LOCAL g30 AS TSxGrad
LOCAL g31 AS TSxGrad
LOCAL g32 AS TSxGrad
LOCAL g33 AS TSxGrad
LOCAL g34 AS TSxGrad
LOCAL g35 AS TSxGrad
LOCAL g36 AS TSxGrad
LOCAL g37 AS TSxGrad
LOCAL g38 AS TSxGrad
LOCAL g39 AS TSxGrad
LOCAL g310 AS TSxGrad
LOCAL g311 AS TSxGrad
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)
REDIM self.grad3[12]
self.grad3[ 0] = g30
self.grad3[ 1] = g31
self.grad3[ 2] = g32
self.grad3[ 3] = g33
self.grad3[ 4] = g34
self.grad3[ 5] = g35
self.grad3[ 6] = g36
self.grad3[ 7] = g37
self.grad3[ 8] = g38
self.grad3[ 9] = g39
self.grad3[10] = g310
self.grad3[11] = g311
LOCAL g40 AS TSxGrad
LOCAL g41 AS TSxGrad
LOCAL g42 AS TSxGrad
LOCAL g43 AS TSxGrad
LOCAL g44 AS TSxGrad
LOCAL g45 AS TSxGrad
LOCAL g46 AS TSxGrad
LOCAL g47 AS TSxGrad
LOCAL g48 AS TSxGrad
LOCAL g49 AS TSxGrad
LOCAL g410 AS TSxGrad
LOCAL g411 AS TSxGrad
LOCAL g412 AS TSxGrad
LOCAL g413 AS TSxGrad
LOCAL g414 AS TSxGrad
LOCAL g415 AS TSxGrad
LOCAL g416 AS TSxGrad
LOCAL g417 AS TSxGrad
LOCAL g418 AS TSxGrad
LOCAL g419 AS TSxGrad
LOCAL g420 AS TSxGrad
LOCAL g421 AS TSxGrad
LOCAL g422 AS TSxGrad
LOCAL g423 AS TSxGrad
LOCAL g424 AS TSxGrad
LOCAL g425 AS TSxGrad
LOCAL g426 AS TSxGrad
LOCAL g427 AS TSxGrad
LOCAL g428 AS TSxGrad
LOCAL g429 AS TSxGrad
LOCAL g430 AS TSxGrad
LOCAL g431 AS TSxGrad
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)
REDIM self.grad4[32]
self.grad4[ 0] = g40
self.grad4[ 1] = g41
self.grad4[ 2] = g42
self.grad4[ 3] = g43
self.grad4[ 4] = g44
self.grad4[ 5] = g45
self.grad4[ 6] = g46
self.grad4[ 7] = g47
self.grad4[ 8] = g48
self.grad4[ 9] = g49
self.grad4[10] = g410
self.grad4[11] = g411
self.grad4[12] = g412
self.grad4[13] = g413
self.grad4[14] = g414
self.grad4[15] = g415
self.grad4[16] = g416
self.grad4[17] = g417
self.grad4[18] = g418
self.grad4[19] = g419
self.grad4[20] = g420
self.grad4[21] = g421
self.grad4[22] = g422
self.grad4[23] = g423
self.grad4[24] = g424
self.grad4[25] = g425
self.grad4[26] = g426
self.grad4[27] = g427
self.grad4[28] = g428
self.grad4[29] = g429
self.grad4[30] = g430
self.grad4[31] = g431
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[512]
REDIM self.permMod12[512]
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
TYPE TSxGrad
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
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:
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
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.
QuoteI 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.
You have the .DSK ?¿ =D =D, I love to see this... I love the 3D in CPC...
I wrote quite a few isometric programs back in the 8bit day's, both knight lore style & wireframe style. I used faultline type generation for landscapes which was pretty quick tbh, not realtime quick but a few seconds for an array of 64x64 which was a few screens worth.
My isometric skills are really rusty but writing that has made me think about brushing up on it again.
Lee
Sent from my C6603 using Tapatalk
Quote from: mentalthink on 2013-Dec-23
QuoteI 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.
You have the .DSK ?¿ =D =D, I love to see this... I love the 3D in CPC...
No. I've lost pretty much everything I ever did on CPC, Amiga and early pc. It was all either give away, sold, swapped or thrown out. Cimputer deaths killed much of my DOS days development. It's scattered around the internet, but I no longer have it. :(