PDA

View Full Version : PerlinNoise



kryton9
06-05-2017, 02:47
This is a translation of Ken Perlin's Improved Noise Function written in Java.

https://github.com/kryton9/thinBasic-PerlinNoise.git

There are two functions for the programmer:

1. PerlinNoiseSeed( seedValue )
seedValue is an integer number, it will affect the noise returned from:

2. PerlinNoise( x, y, z )
x, y, z are double values

You generally use PerlinNoiseSeed( seedValue ) once in the beginning of your code.
It gives you the same controlled noise results
each time your program runs.

As more examples are made it will be more apparent the value of these two functions.

9684

You don't need Git to use these files. Just follow the link and click on the following to download a zip file of all the needed code:
9686

primo
06-05-2017, 16:11
Thank you kryton9 for the noise function, i haven't focused on this subject before, now i find it interesting. but what confuse me is that for any 3 numbers the result is the same as before ie it is not like the RND function. which gives different results , i will read the explanation here about perlin noise http://web.archive.org/web/20160227164836/http://freespace.virgin.net/hugo.elias/models/m_perlin.htm
since he said "If you pass it the same parameter twice, it produces the same number twice. It is very important that it behaves in this way, otherwise the Prelim function will simply produce nonsense"

i have tried your function noise to plot x, y, 0 by plotting the noise output as if it is y. the result is like a chaotic heart pulses
9682
if we change yy = yy + 0.03 to yy = yy + 1 it will be more chaotic
using a Canvas_SineCurve.tbasuc as a template:
with your include file "PerlinNoise.tbasici"

Uses "UI"
#INCLUDE "PerlinNoise.tbasici"
'-----------------------------------------------------------
'---Global variables
'-----------------------------------------------------------
Function TBMain () As Long

Dim ScreenWidth As Long = 800
Dim ScreenHeight As Long = 600
Dim hWin As DWord '---Handle of the canvas window
Dim x,y,yy As Double
Dim thick As Double
Long f

'-----------------------------------------------------------
'---Main program
'-----------------------------------------------------------
hWin = Canvas_Window("Perlin Noise: press ESC to exit", 1, 1, ScreenWidth, ScreenHeight)

Canvas_Attach(hWin, 0, %TRUE) ' <- double buffer

'---Init canvas
Canvas_Clear %BLACK
Canvas_Font "Arial" , 8, %CANVAS_FONTSTYLE_NORMAL

'---Set personalized canvas scale
Canvas_Scale(-20, -5, 20, 20)
yy = -15
'---Loop For All steps
For x = -15 To 15 Step 0.02

yy = yy + 0.03
y = PerlinNoise( x, yy, 0 )

thick = 3
Canvas_Width(thick)
Canvas_Line( (x, y*3), (x, y*3) , Rgb(25 * thick, 255, 0) )

f+1
If f=6 Then 'to make the animation faster
Canvas_Redraw
f=0
End If
If GetAsyncKeyState(%VK_ESCAPE) Then Exit For

Next

Do

Loop While All( Asc(Canvas_WaitKey) <> 27, IsWindow(hWin) )
Canvas_Window End

End Function

kryton9
06-05-2017, 19:03
Hi Primo,

Thanks for your example, that is really neat! It is fun to watch!!

Yes the noise function from Ken Perlin will give the same results with smooth randomness where the differences are realistic to Nature. As you see in your fine example what looks like a heartbeat can become in 3D terrain, clouds and almost any type of natural material when used to create procedural textures.

I need to make some more useful code in include files to start tapping into even more complex uses for Perlin Noise. Hope to have more in the coming days.

Petr Schreiber
06-05-2017, 23:34
Kent,

I prepared proposal for change in your repository, you can accept it or not, please review here:
https://github.com/kryton9/thinBasic-PerlinNoise/pull/1

The point is - by default, you have not guarantee, that Git will preserve Windows line endings. This will force it.


Petr

Petr Schreiber
06-05-2017, 23:49
Kent,

I also found one minor imprecision in your port of the code. I prepared second pull request to correct the data types:
https://github.com/kryton9/thinBasic-PerlinNoise/pull/2

As usually, feel free to ignore :)

As with all pull requests - to review changes, click the File changes tab, where you see it line by line, side by side. Git rules.


Petr

kryton9
07-05-2017, 02:03
Thanks Petr, you have to bare with me as I learn GitHub. Thanks for your help!

I downloaded the github desktop program. I tell it to sync, I tell it to upload commits
and it seems random it works, and it doesn't work. It then merged 2 different copies
together into one big mess. It does a lot and I understand and appreciate what all it can do. So will
keep trying to learn it. But I spent most of the day syncing by hand all the things I messed up
using git's tools :)

Petr Schreiber
07-05-2017, 09:20
You are doing great, Kent :)


Petr

kryton9
11-05-2017, 20:55
I made this demo, but it is not giving me that clouds like look that I expected. I know the PerlinNoise function is returning correctly, but I did notice that Processing must use one of the older noise functions as it gives different results than the Improved Perlin Noise function.

This takes sometime at first. I was hoping to do animations with the PerlinNoise function, but it is not fast enough with my implementation.

Unfortunately this demo is nothing to look at. Just something to give some code for others perhaps to come up with something.


Uses "TBGL"
#INCLUDE "%APP_INCLUDEPATH%thinbasic_gl.inc" ' to use glColor4d function
#INCLUDE "https://raw.githubusercontent.com/kryton9/thinBasic-PerlinNoise/master/PerlinNoise.tbasicu"
DWord hWnd = 0
Long x = 0
Long y = 0
Double frameRate = 0.0
Long interval = 0
Long width = 250 'larger the longer it takes to calculate
Long height = 100 'larger the longer it takes to calculate
Long numPoints = width * height
String winTitle = "Calculating " + numPoints + " Noise Values, This can take Some time."
Double noise ( width, height )
Double yOff = 0.0
Double xOff = 0.0
Double noiseZoom = 0.1 'good ranges : 0.1 to 0.001
hWnd = TBGL_CreateWindowEx ( "PerlinNoise 2D GL Demo", width, height, 32, %TBGL_WS_WINDOWED )
TBGL_ShowWindow
TBGL_SetWindowTitle ( hWnd, winTitle)
For y = 1 To height
yOff += noiseZoom
winTitle = "Calculating " + Str$ (x * y) + " of " + numPoints + " Noise Values, working away."
TBGL_SetWindowTitle ( hWnd, winTitle)
For x = 1 To width
xOff += noiseZoom
noise(x, y) = PerlinNoise xOff, yOff, 7
Next
Next
TBGL_BlendFunc %GL_SRC_ALPHA , %GL_ONE
TBGL_UseBlend %TRUE
TBGL_RenderMatrix2D ( 1, height, width, 1 )
While TBGL_IsWindow ( hWnd )
FrameRate = TBGL_GetFrameRate
Incr interval
If interval > 5 Then
TBGL_SetWindowTitle ( hWnd, "PerlinNoise 2D GL Demo FPS: " + Str$ ( FrameRate, 4 ) )
interval = 0
EndIf
TBGL_ClearFrame
TBGL_PushMatrix
TBGL_BeginPoly %GL_POINTS
For y = 1 To height
For x = 1 To width
glColor4d 255, 255, 255, noise ( x, y )
TBGL_Vertex x, y
Next
Next
TBGL_EndPoly
TBGL_PopMatrix
TBGL_DrawFrame
If TBGL_GetWindowKeyState( hWnd, %VK_ESCAPE ) Then Exit While
Wend
TBGL_DestroyWindow
9693

Petr Schreiber
12-05-2017, 08:52
Nothing to look at? You serious, Kent? It looks super high tech! Like some alien scratches!

I will check this one... do you have link to the original code by hand somewhere?


Petr

kryton9
12-05-2017, 14:08
The full code is posted in the message Petr. One thing it proved to me is that my PerlinNoise is too slow unless you work in dimensions 5,5 :D
I am excited by this because now I will work on his older noise functions. And then will work on making them faster.

kryton9
13-05-2017, 21:55
I finished the noise function. Files are here on GitHub (https://github.com/kryton9/thinBasic-Noise)

I finally settled on making the noise function based on the information located here at Paul Bourke's website:
http://paulbourke.net/texture_colour/perlin/

Noise.tbasici is the include file needed to use the dll and functions.

NoiseDemoCanvasUsingFillNoiseArray.tbasic demo code using the FillNoiseArray function. This is probably what you will want to use most often. Very fast.

NoiseDemoCanvasManualNoise.tbasic demo where you manually fill the necessary arrays with noise. More control, but slower.
9712

primo
14-05-2017, 12:33
Hi kryton9
using a C dll makes the calculations speedier
but there is a slow rendering with:

For y = 1 To height
For x = 1 To width
glColor4d 255, 255, 255, noise ( x, y ) * alphaScale
TBGL_Vertex x, y
Next
Next
the fps for me is 5
i have tried the Gbuffer using a template from some examples ,the graphics will be speedy , it is for me about 60. but i have a problem, there is something not correct
i replace your

For y = 1 To height
For x = 1 To width
glColor4d 255, 255, 255, noise ( x, y ) * alphaScale
TBGL_Vertex x, y
Next
Next
with positional array vertex(x,y) which are filled from 1-500, 1-500
the color and alpha for every vertex in another array : noise(x,y)

noiseNum = PerlinNoise xOff, yOff, 0
noise(x,y).r = 255
noise(x,y).g = 255
noise(x,y).b = 255
noise(x,y).A = noiseNum * alphaScale
but i don't see the alpha working here

Uses "TBGL"
#INCLUDE "%APP_INCLUDEPATH%thinbasic_gl.inc" ' to use glColor4d function
#INCLUDE "PerlinNoise.tbasici"

Double yOff = 0.0
Double xOff = 0.0
Long interval = 0
Long width = 500 'larger the longer it takes to calculate
Long height = 500 'larger the longer it takes to calculate
Long x = 0
Long y = 0

Function TBMain()
Local hWnd As DWord
Local FrameRate As Double
Long numPoints = width * height
String winTitle = "Calculating " + numPoints + " Noise Values, This can take Some time."

' -- Create and show window
hWnd = TBGL_CreateWindowEx ( "PerlinNoise 2D GL Demo", width, height, 32, %TBGL_WS_WINDOWED )
TBGL_ShowWindow
TBGL_SetWindowTitle ( hWnd, winTitle)

TBGL_ShowWindow

Dim gbPoints As DWord = TBGL_GBufferCreate(%TBGL_POINTS, %TBGL_2D)

' -- Define data for it
Global vertex(width,height) As TBGL_TVECTOR2F
Global noise(width,height) As TBGL_TRGBA 'TBGL_TRGB

Dim x,y As Integer
Double noiseStep = 0.001 'good ranges : 0.1 to 0.00000000000000001
Double alphaScale = 5 ' ranges: 1 to 1000000000000
Double noiseNum

For y=1 To width
yOff += noiseStep
For x=1 To height
xOff += noiseStep

vertex(x,y).x = x
vertex(x,y).y = y

noiseNum = PerlinNoise xOff, yOff, 0

noise(x,y).r = 255
noise(x,y).g = 255
noise(x,y).b = 255
noise(x,y).A = noiseNum * alphaScale

Next

Next


Dim NumOfarrayElements As Long = CountOf(vertex(1))* CountOf(vertex(2))
' -- Create buffer dynamically linked to the arrays above
TBGL_GBufferDefineFromArray(gbPoints, %TBGL_STATIC, NumOfarrayElements, vertex(1,1), noise(1,1))

' -- Resets status of all keys
TBGL_ResetKeyState()

TBGL_BlendFunc %GL_SRC_ALPHA , %GL_ONE
TBGL_UseBlend %TRUE
TBGL_RenderMatrix2D ( 1, height, width, 1 )
' -- Main loop
While TBGL_IsWindow(hWnd)

FrameRate = TBGL_GetFrameRate
Incr interval
If interval > 5 Then
TBGL_SetWindowTitle ( hWnd, "PerlinNoise 2D GL Demo FPS: " + Str$ ( FrameRate, 4 ) )
interval = 0
EndIf
TBGL_ClearFrame

' -- Render it
TBGL_RenderMatrix2D ( 1, height, width, 1 )
TBGL_GBufferRender(gbPoints)

TBGL_DrawFrame

' -- ESCAPE key to exit application
If TBGL_GetWindowKeyState(hWnd, %VK_ESCAPE) Then Exit While

Wend
' -- Destroying the buffer is not necessary,
' -- the garbage collector will take care of it

' -- Destroy window
TBGL_DestroyWindow
End Function

kryton9
14-05-2017, 18:42
Thanks for testing it Primo and for the suggestions. In my demo, I just wanted to show the speed increase from using the dll version. Actually if I were using this in a real program, I would just do a single render to a texture and then just show that in the main loop. Drawing every frame pixel by pixel is not efficient.

Your program though opens up animating in real time, so I will study your code. I am not an opengl guru like Petr. But I will happily look at it now and see if I can figure it out. Hopefully in the meantime, Petr sees your post and probably knows the answer right away, and will reply quicker.

kryton9
14-05-2017, 20:09
Primo, TBGL_tRGBA uses byte and not double
' -- This type is used by TBGL_GBufferCreate, TBGL_GBufferDefineFromArray
TYPE TBGL_tRGBA
R AS BYTE
G AS BYTE
B AS BYTE
A AS BYTE
END TYPE

Also TBGL_tVector3F uses singles (floats in C) and not doubles
' -- This type is used by TBGL_GBufferCreate, TBGL_GBufferDefineFromArray
TYPE TBGL_tVector3F
x AS SINGLE
y AS SINGLE
z AS SINGLE
END TYPE

I will need to talk about these with Petr to make sure everything is uniform and compatible.

Thanks again for testing and bringing out these inconsistencies.

primo
14-05-2017, 21:07
thanks kryton9 for testing the code and ideas.
if the gbuffer code logic in general is right then it should produce the same output as your first code. yes the reason may be the byte choice for alpha channel. tomorrow i will put your perlNoise formula in one of the general opengl codes to see if it will produce the same shape as your first code. but opengl is too complex and i even don't remember my opengl codes, while Gbuffer is easy to use and it is speedy

kryton9
14-05-2017, 21:10
Primo, don't waste your time at this stage. I am working on major changes and I will post it when finished for testing.

I hope to iron out differences with tbgl to make it as compatible as possible.

primo
15-05-2017, 10:13
kryton9, like Petr said the double in color and alpha have no meaning with real screens.
but to continue the testing your dll with alpha as single here is the opengl code adapted from old post http://www.thinbasic.com/community/showthread.php?12614-glDrawArrays-example-GBuffers-example
as you can see it provide the same shape (circle of light) as in your first example
look the :

Type Point3D
x As Single
y As Single
z As Single
red As Single
green As Single
blue As Single
alpha As Single 'Double
End Type

using Double make the shape a complete random
you may use this demo as a template for other testing
(not optimised so there is too much flicker)
note: there must me a special care to gluPerspective and the gluLookAt, also the glTranslatef better to be before glDrawArrays which draw the graphics.
EDIT: note the glColorPointer(4 , the 4 refer to the presence of alpha. if 3 there is no alpha in Type end type
EDIT2: the TBGL_RenderMatrix2D is great, it replaces many functions. i have added TBGL_RenderMatrix2D( 1, 500, 500, 1 ) before glDrawArrays(%GL_POINTS, 1, 500*500 ) removing the z , and it fixes the flicker, and the shape is 2D . attached the new version, save it to the kryton9 DLL in the first post.

Uses "tbgl", "MATH"

#INCLUDE "%app_includepath%\thinbasic_gl.inc"
#INCLUDE "%app_includepath%\thinbasic_glu.inc"
#INCLUDE "PerlinNoise.tbasici"

Type Point3D
x As Single
y As Single
z As Single
red As Single
green As Single
blue As Single
alpha As Single 'Double
End Type

Dim hwnd As DWord
Global Nb As DWord = 500*500

Global Vertex(Nb) As Point3D
Double yOff = 0.0
Double xOff = 0.0
'msgbox 0, SizeOf(Point3D))

hwnd = TBGL_CreateWindowEx("glDrawArrays example - esc to exit", 500, 500, 32, 0)
TBGL_ShowWindow
TBGL_ResetKeyState()
'TBGL_SetDrawDistance 550
'TBGL_BackColor 0,0,0
'glPointSize( 2.0 ) ' just to show the curve in bold

FillArray ' fill Vertex(...) with position data and color data

While TBGL_IsWindow(hwnd)
TBGL_ClearFrame

display
TBGL_DrawFrame
If TBGL_GetWindowKeyState( hwnd, %VK_ESCAPE) Then Exit While
Wend

TBGL_DestroyWindow

'=================================================================================

'--------------------------------------------------------
'--------------------------------------------------------
Sub display()
TBGL_BlendFunc %GL_SRC_ALPHA , %GL_ONE
TBGL_UseBlend %TRUE
glMatrixMode(%GL_PROJECTION)
glLoadIdentity()
gluPerspective(120.0, 500/500, 1.0, 500.0)
glMatrixMode(%GL_MODELVIEW)

glShadeModel(%GL_SMOOTH)
glEnable(%GL_DEPTH_TEST)
gluLookAt( 0, 0, 100,
0, 1, 0,
0, 1, 0 )
glclear(%gl_color_buffer_bit)

TBGL_Rotate GetTickCount/30,0,0,1

glClear(%GL_COLOR_BUFFER_BIT Or %GL_DEPTH_BUFFER_BIT)
'glClearColor(1, 1, 1, 1)

glEnableClientState(%GL_VERTEX_ARRAY )
glEnableClientState(%GL_COLOR_ARRAY)
glVertexPointer(3, %GL_FLOAT,SizeOf(Point3D),VarPtr(Vertex(1).x))
glColorPointer(4, %GL_FLOAT, SizeOf(Point3D), VarPtr(Vertex(1).red))

glTranslatef(-240, -250, -60)
glDrawArrays(%GL_POINTS, 1, 500*500 )

glDisableClientState(%GL_COLOR_ARRAY)
glDisableClientState(%GL_VERTEX_ARRAY)
'**************************************************


End Sub

Sub FillArray()
DWord indx = 0
Dim x, y, z As Long

Double noiseStep = 0.001 'good ranges : 0.1 to 0.00000000000000001
Double alphaScale = 5 ' ranges: 1 to 1000000000000
Double noiseNum
For y=1 To 500
yOff += noiseStep
For x=1 To 500
xOff += noiseStep
indx+1
Vertex(indx).x = x
Vertex(indx).y = y
Vertex(indx).z = 0
Vertex(indx).red = 1.0 :Vertex(indx).green = 1.0 :Vertex(indx).blue = 1
noiseNum = PerlinNoise xOff, yOff, 0
Vertex(indx).alpha = noiseNum * alphaScale


Next x

Next y


End Sub

kryton9
15-05-2017, 18:50
Thanks a lot for your help Primo. This will be very helpful to test the new library when finished. Once I get the code to a testable stage I will put it on GitHub, so we can all work on polishing it up together then. That was interesting to see the difference from using single and double in Point3D.alpha

kryton9
29-05-2017, 01:01
I finished the noise code. It is linked to in the first post here:
http://www.thinbasic.com/community/showthread.php?12760-PerlinNoise&p=93572&viewfull=1#post93572

primo
29-05-2017, 09:43
Thanks Kryton9, it is speedy and looks good, and can be used to provide images to build a terrain for the 3d grpahics. i like your idea SeedNoise(2017).
while searching i find http://www.bugman123.com/Fractals/index.html the site have all colors on the world and he refers also to perl noise and to the paul bourke site

kryton9
29-05-2017, 21:02
Thanks for your link Primo. I added it to my bookmarks. Between that site and Paul's I will have lots of things to experiment with.

While working on this noise code, I realized that all of this should probably be done with shaders. But it was a good learning experience.