BBC BASIC for Windows
« Perlin Noise Function »

Welcome Guest. Please Login or Register.
Apr 5th, 2018, 10:29pm



ATTENTION MEMBERS: Conforums will be closing it doors and discontinuing its service on April 15, 2018.
Ad-Free has been deactivated. Outstanding Ad-Free credits will be reimbursed to respective payment methods.

If you require a dump of the post on your message board, please come to the support board and request it.


Thank you Conforums members.

BBC BASIC for Windows Resources
Online BBC BASIC for Windows documentation
BBC BASIC for Windows Beginners' Tutorial
BBC BASIC Home Page
BBC BASIC on Rosetta Code
BBC BASIC discussion group
BBC BASIC for Windows Programmers' Reference

« Previous Topic | Next Topic »
Pages: 1  Notify Send Topic Print
 thread  Author  Topic: Perlin Noise Function  (Read 1403 times)
Michael Hutton
Developer

member is offline

Avatar




PM

Gender: Male
Posts: 248
xx Perlin Noise Function
« Thread started on: Aug 29th, 2009, 09:51am »

I am trying to create a noise function to create Perlin Noise. I have a C(C++) function:

Code:
function IntNoise(32-bit integer: x)

x = (x<<13) ^ x
return (1.0 - ( (x * x * 15731 + 789221) + 1376312589) & 7fffffff) / 1073741824.0);

end IntNoise function
 


It should return a number between -1.0 and 1.0 and be the same number for every x passed.

I have translated this as:

Code:
DEF FN_Noise(X%)
LOCAL p%, t%, p#
X% = (X%<<13) EOR X% 
p% = FN32(X%*X%)
t% = FN32((p%*15731+789221)+1376312589) AND &7FFFFFFF
p# = 1.0 - (t%/1073741824.0)
=p#

DEF FN32(V#)
WHILE V#>&7FFFFFFF : V#-=2^32 : ENDWHILE
WHILE V#<&80000000 : V#+=2^32 :ENDWHILE
= V#
 


1. Is this a valid way of translating this? - or should I break it up even more?

2. Am I introducing errors when using FN32(X%*X%)?

3. Could I use V instead of V# when calculating X%*X% as in the original FN32() in the Wiki?

4. Is there a similar way to achieve this with the RND function ie can I create a function to return me the nth random number (from the same seed each time) but *without* iterating through the RND generator? I suspect not, but any interesting thoughts....?

5. I suspect that I should create a ASM function to do this, it would be straight forward and a lot quicker.

I hope this makes sense.

Michael

« Last Edit: Aug 29th, 2009, 10:15am by Michael Hutton » User IP Logged

Michael Hutton
Developer

member is offline

Avatar




PM

Gender: Male
Posts: 248
xx Re: Perlin Noise Function
« Reply #1 on: Aug 29th, 2009, 10:10am »

eek!

I am getting different results for FN32(X%*X%)

Code:
      PRINT FN_Noise(50)
      PRINT FN_Noise1(50)
      
      END
      
      DEF FN_Noise(X%)
      X% = (X%<<13)EORX%
      p% = FN32(X%*X%)
      =p%
      
      DEF FN32(V#)
      WHILE V#>&7FFFFFFF : V#-=2^32 : ENDWHILE
      WHILE V#<&80000000 : V#+=2^32 : ENDWHILE
      = V#
      
      
      DEF FN_Noise1(A%)
      PRIVATE code%
      IF code%=0 THEN
        LOCAL L%, P%, pass%, loop
        DIM code% 200, L%-1
        FOR pass% = 8 TO 10 STEP 2
          P% = code%
          [OPT pass%
          mov ebx,eax
          shl ebx,13
          xor eax,ebx
          mov ecx,eax
          mov ebx,eax
          xor eax,eax
          .loop
          add eax,ebx
          loop loop
          ret
          ]
        NEXT
      ENDIF
      =USR(code%)
 


Michael

(modified..) Yes, and I should be using imul ....
« Last Edit: Aug 29th, 2009, 10:47am by Michael Hutton » User IP Logged

Michael Hutton
Developer

member is offline

Avatar




PM

Gender: Male
Posts: 248
xx Re: Perlin Noise Function
« Reply #2 on: Aug 29th, 2009, 10:52am »

I know, I should have started this topic when I had done this.

Code:
      DEF FN_Noise(A%)
      PRIVATE code%
      LOCAL r#, D%
      r# = 1073741824.0
      IF code%=0 THEN
        LOCAL L%, P%
        DIM code% 62, L%-1
        P% = code%
        [OPT 8
        mov ebx,eax
        shl ebx,13
        xor eax,ebx
        mov ebx,eax
        imul eax,ebx
        imul eax,15731
        add eax,789221
        add eax,1376312589
        and eax,&7FFFFFFF
        mov [^A%],eax
        fild dword [^A%]
        fld qword [^r#]
        fdiv
        fld1
        fsub
        fstp qword [^r#]
        ret
        ]
      ENDIF
      D%=USR(code%)
      =r#
 


Does this make sense?

Michael
« Last Edit: Aug 29th, 2009, 10:57am by Michael Hutton » User IP Logged

admin
Administrator
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1145
xx Re: Perlin Noise Function
« Reply #3 on: Aug 29th, 2009, 12:03pm »

Quote:
p% = FN32(X%*X%)
Is this a valid way of translating this?

Definitely not! FN32() works only after addition or subtraction. You are trying to use it after multiplication!

If you multiply two 32-bit signed integers (X%*X%) you potentially get a 63-bit result, of which you are interested in the low 32-bits. In your code the first thing you do is to transfer the 63-bit result into a variable capable of holding only a 53-bit number (V#). Obviously at that point the number may be truncated and the result you get will be incorrect.

I don't understand how you thought it could work in the first place, since BBC BASIC has no variable type capable of holding a 63-bit integer value.

Richard.
User IP Logged

admin
Administrator
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1145
xx Re: Perlin Noise Function
« Reply #4 on: Aug 29th, 2009, 12:13pm »

Quote:
I have a C(C++) function

It is unlike any C code I've ever seen. These lines:

Code:
function IntNoise(32-bit integer: x)
end IntNoise function 

don't look to me like C (or C++) at all. Importantly, there seems to be no declaration of either the data type of x (is it signed or unsigned?) or the data type returned by the function (float? double?).

If you're asking for help in reproducing the functionality of a C function please list the C function in its entirety!! It's impossible to tell whether your assembler code is right or wrong without knowing the data types.

Incidentally, one relatively simple solution is to get the C compiler to output the assembler code it has generated for the function. Modern C compilers do a very good job, so that's a perfectly reasonable way to 'convert' C to assembler code.

Richard.
User IP Logged

Michael Hutton
Developer

member is offline

Avatar




PM

Gender: Male
Posts: 248
xx Re: Perlin Noise Function
« Reply #5 on: Aug 29th, 2009, 12:35pm »

http://freespace.virgin.net/hugo.elias/models/m_perlin.htm

This is the page. I had assumed C/C++ although it seems it isn't. I am not that familiar with C/C++ to really know.

re: a compiler outputting the source asm sounds very interesting. Would Visual C++ do this? I haven't downloaded anything but would be interested in pointers before I start searching.

Michael
User IP Logged

admin
Administrator
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1145
xx Re: Perlin Noise Function
« Reply #6 on: Aug 29th, 2009, 2:19pm »

Quote:
This is the page. I had assumed C/C++ although it seems it isn't.

The IntNoise function listed there is simply being used as a Random Number Generator; the Perlin Noise comes from the subsequent processing. Therefore there's no particular reason to use that RNG over another, so long as the replacement is equally good.

Rather than worrying about whether your translation of the IntNoise function is correct you might as well use the tried-and-tested (I assume!) code here:

http://bb4w.wikispaces.com/Alternative+pseudo-random+numbers

Another approach would be to use one of the 'ready made' Perlin Noise generators, such as Ken Perlin's own:

http://mrl.nyu.edu/~perlin/doc/oscar.html

What I'd probably do is to compile that C code as a DLL and call the DLL from BBC BASIC. That would be quick, easy and reliable.

The simplest solution of all would be to download PerlinDLL.dll from here:

http://www.shedletsky.com/perlin/

Richard.
User IP Logged

admin
Administrator
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1145
xx Re: Perlin Noise Function
« Reply #7 on: Aug 29th, 2009, 3:03pm »

Instant 2D Perlin texture in BBC BASIC:

Code:
      DIB_RGB_COLORS = 0
      
      SYS "LoadLibrary", @lib$+"PerlinDLL.dll" TO perlin%
      SYS "GetProcAddress", perlin%, "GeneratePerlinTexture" TO `GeneratePerlinTexture`
      
      DIM bmi{biSize%, biWidth%, biHeight%, biPlanes{l&,h&}, biBitCount{l&,h&}, \
      \       biCompression%, biSizeImage%, biXPelsPerMeter%, biYPelsPerMeter%, \
      \       biClrUsed%, biClrImportant%}
      
      bmi.biSize% = DIM(bmi{})
      bmi.biWidth% = 400
      bmi.biHeight% = 400
      bmi.biPlanes.l& = 1
      bmi.biBitCount.l& = 32
      
      SYS "CreateDIBSection", @memhdc%, bmi{}, DIB_RGB_COLORS, ^bits%, 0, 0 TO hdib%
      
      SYS `GeneratePerlinTexture`, bits%, bmi.biWidth%, bmi.biHeight%
      
      SYS "SelectObject", @memhdc%, hdib%
      SYS "InvalidateRect", @hwnd%, 0, 0 


Richard.
User IP Logged

Michael Hutton
Developer

member is offline

Avatar




PM

Gender: Male
Posts: 248
xx Re: Perlin Noise Function
« Reply #8 on: Aug 29th, 2009, 3:17pm »

Your Googleing is obviously a lot better than mine! Thanks for the links.
User IP Logged

Michael Hutton
Developer

member is offline

Avatar




PM

Gender: Male
Posts: 248
xx Re: Perlin Noise Function
« Reply #9 on: Sep 1st, 2009, 02:53am »

Hi Richard,

I suspect you knew I would have trouble with this. I am trying to use

extern "C" PERLINDLL_API void SetPerlinVars(float init_freq, float init_amp, float persistance, int octaves, int random_seed);

and pass the floats to the function. I have read the updated wiki article but so far have failed to get it to work.

Code:
      REM Using the PerlinDLL to create Perlin Textures
      
      REM Set up the output window
      MODE 8
      SYS "GetClientRect",@hwnd%,^V%
      window_X% = X%
      window_Y% = Y%
      
      
      REM!WC
      DIB_RGB_COLORS = 0
      
      REM Load the Library and get the address of the functions
      SYS "LoadLibrary", @lib$+"PerlinDLL.dll" TO perlin%
      SYS "GetProcAddress", perlin%, "AddColor" TO `AddColor`
      SYS "GetProcAddress", perlin%, "GeneratePerlinTexture" TO `GeneratePerlinTexture`
      SYS "GetProcAddress", perlin%, "GetColor" TO `GetColor`
      SYS "GetProcAddress", perlin%, "InitializePalette" TO `InitializePalette`
      SYS "GetProcAddress", perlin%, "SetClampAbs" TO `SetClampAbs`
      SYS "GetProcAddress", perlin%, "SetClampNormalize" TO `SetClampNormalize`
      SYS "GetProcAddress", perlin%, "SetClampTruncate" TO `SetClampTruncate`
      SYS "GetProcAddress", perlin%, "SetCosineInterpolation" TO `SetCosineInterpolation`
      SYS "GetProcAddress", perlin%, "SetCubicInterpolation" TO `SetCubicInterpolation`
      SYS "GetProcAddress", perlin%, "SetLinearInterpolation" TO `SetLinearInterpolation`
      SYS "GetProcAddress", perlin%, "SetOutputFlat" TO `SetOutputFlat`
      SYS "GetProcAddress", perlin%, "SetOutputSphereMapped" TO `SetOutputSphereMapped`
      SYS "GetProcAddress", perlin%, "SetOutputTileable" TO `SetOutputTileable`
      SYS "GetProcAddress", perlin%, "SetPerlinVars" TO `SetPerlinVars`
      SYS "GetProcAddress", perlin%, "TestBMP" TO `TestBMP`
      SYS "GetProcAddress", perlin%, "fnTestDLL" TO `fnTestDLL`
      SYS "GetProcAddress", perlin%, "nTestDLL" TO `nTestDLL`
      
      REM Create a bitmap to store the Texture
      DIM bmi{biSize%, biWidth%, biHeight%, biPlanes{l&,h&}, biBitCount{l&,h&}, \
      \       biCompression%, biSizeImage%, biXPelsPerMeter%, biYPelsPerMeter%, \
      \       biClrUsed%, biClrImportant%}
      
      bmi.biSize% = DIM(bmi{})
      bmi.biWidth% = window_X%
      bmi.biHeight% = window_Y%
      bmi.biPlanes.l& = 1
      bmi.biBitCount.l& = 32
      
      
      
      SYS "CreateDIBSection", @memhdc%, bmi{}, DIB_RGB_COLORS, ^bits%, 0, 0 TO hdib%
      
      
      REM Call `SetPerlinVars`
      init_freq#   = 0.0625 * 1.0#
      init_amp#     = 1.0 * 1.0#
      persistance# = 0.25 * 1.0#
      octaves% = 5
      random_seed% = 500
      
      REM SYS `SetPerlinVars`, !^init_freq#, !(^init_freq#+4), !^init_amp#, !(^init_amp+4), !^persistance#, !(^persistance#+4), octaves%, random_seed%
      
      REM SYS `SetPerlinVars`, ^init_freq#, ^init_amp#, ^persistance#, octaves%, random_seed%
      
      REM Now generate the Texture
      REM SYS `SetOutputSphereMapped`
      SYS `GeneratePerlinTexture`, bits%, bmi.biWidth%, bmi.biHeight%
      
      
      REM Show the bitmap/texture in the main window
      SYS "SelectObject", @memhdc%, hdib%
      SYS "InvalidateRect", @hwnd%, 0, 0
      
      REM Free the Library
      SYS "FreeLibrary", perlin%
 


It 'works' but gives me a blue bitmap with now texture with both ways of calling `SetPerlinVars`

What type of float am I expecting to pass? Should this be stated in the PerlinDLL?

Sorry for the irritation, I know this may seem simple to you.

Thank you in advance.

Michael
« Last Edit: Sep 1st, 2009, 02:54am by Michael Hutton » User IP Logged

admin
Administrator
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1145
xx Re: Perlin Noise Function
« Reply #10 on: Sep 1st, 2009, 08:36am »

Quote:
I have read the updated wiki article

Not terribly well it seems! It says there "You must first ascertain from the documentation of the function whether the value is to be passed by reference or by value, and whether the value needs to be a float or a double; this is vitally important!".

You have ignored that warning, and have attempted to send doubles to a function that requires floats, and (in one of your examples) send them by reference when they are required to be sent by value. :(

Since you have the C function definition:

Code:
SetPerlinVars(float init_freq, float init_amp, float persistance, 
              int octaves, int random_seed); 

you know that you must send the parameters by value as floats, so the correct section of the Wiki article is entitled '32-bit floats passed by value'. Revised code below:

Code:
      REM Using the PerlinDLL to create Perlin Textures
      
      REM Set up the output window
      MODE 8
      DIM rc{l%,t%,r%,b%}
      SYS "GetClientRect", @hwnd%, rc{}
      window_X% = rc.r% : REM could have used window_X% = %vdu%!208
      window_Y% = rc.b% : REM could have used window_Y% = %vdu%!212
      
      REM!WC
      DIB_RGB_COLORS = 0
      
      REM Load the Library and get the address of the functions
      SYS "LoadLibrary", @lib$+"PerlinDLL.dll" TO perlin%
      SYS "GetProcAddress", perlin%, "AddColor" TO `AddColor`
      SYS "GetProcAddress", perlin%, "GeneratePerlinTexture" TO `GeneratePerlinTexture`
      SYS "GetProcAddress", perlin%, "GetColor" TO `GetColor`
      SYS "GetProcAddress", perlin%, "InitializePalette" TO `InitializePalette`
      SYS "GetProcAddress", perlin%, "SetClampAbs" TO `SetClampAbs`
      SYS "GetProcAddress", perlin%, "SetClampNormalize" TO `SetClampNormalize`
      SYS "GetProcAddress", perlin%, "SetClampTruncate" TO `SetClampTruncate`
      SYS "GetProcAddress", perlin%, "SetCosineInterpolation" TO `SetCosineInterpolation`
      SYS "GetProcAddress", perlin%, "SetCubicInterpolation" TO `SetCubicInterpolation`
      SYS "GetProcAddress", perlin%, "SetLinearInterpolation" TO `SetLinearInterpolation`
      SYS "GetProcAddress", perlin%, "SetOutputFlat" TO `SetOutputFlat`
      SYS "GetProcAddress", perlin%, "SetOutputSphereMapped" TO `SetOutputSphereMapped`
      SYS "GetProcAddress", perlin%, "SetOutputTileable" TO `SetOutputTileable`
      SYS "GetProcAddress", perlin%, "SetPerlinVars" TO `SetPerlinVars`
      SYS "GetProcAddress", perlin%, "TestBMP" TO `TestBMP`
      SYS "GetProcAddress", perlin%, "fnTestDLL" TO `fnTestDLL`
      SYS "GetProcAddress", perlin%, "nTestDLL" TO `nTestDLL`
      
      REM Create a bitmap to store the Texture
      DIM bmi{biSize%, biWidth%, biHeight%, biPlanes{l&,h&}, biBitCount{l&,h&}, \
      \       biCompression%, biSizeImage%, biXPelsPerMeter%, biYPelsPerMeter%, \
      \       biClrUsed%, biClrImportant%}
      
      bmi.biSize% = DIM(bmi{})
      bmi.biWidth% = window_X%
      bmi.biHeight% = window_Y%
      bmi.biPlanes.l& = 1
      bmi.biBitCount.l& = 32
      
      SYS "CreateDIBSection", @memhdc%, bmi{}, DIB_RGB_COLORS, ^bits%, 0, 0 TO hdib%
      
      REM Call `SetPerlinVars`
      init_freq   = 0.0625
      init_amp    = 1.0
      persistence = 0.25
      octaves% = 5
      random_seed% = 500
      
      SYS `SetPerlinVars`, FN_f4(init_freq), FN_f4(init_amp), FN_f4(persistence), \
      \                    octaves%, random_seed%
      
      REM Now generate the Texture
      REM SYS `SetOutputSphereMapped`
      SYS `GeneratePerlinTexture`, bits%, bmi.biWidth%, bmi.biHeight%
      
      REM Show the bitmap/texture in the main window
      SYS "SelectObject", @memhdc%, hdib% TO hbmold%
      SYS "DeleteObject", hbmold%
      SYS "InvalidateRect", @hwnd%, 0, 0
      
      REM Free the Library
      SYS "FreeLibrary", perlin%
      
      END
      
      REM Convert to 32-bit float
      DEF FN_f4(A#)
      LOCAL A%,P%
      PRIVATE F%
      IF F%=0 THEN
        DIM P%10
        [OPT 2
        .F%
        mov esi,[ebp+2]:mov edi,[ebp+7]
        fld qword [esi]:fstp dword [edi]
        ret
        ]
      ENDIF
      A# *= 1.0#
      CALL F%,A#,A%
      =A% 

Incidentally I'm somewhat surprised it works at all, because the web page describing the DLL states that it only supports square bitmaps! Has the DLL been revised, or are you somehow 'getting away with it'?

Richard.
User IP Logged

admin
Administrator
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1145
xx Re: Perlin Noise Function
« Reply #11 on: Sep 1st, 2009, 09:10am »

Off the main topic, but this code of yours is rather strange:

Code:
      init_freq#   = 0.0625 * 1.0#
      init_amp#    = 1.0 * 1.0#
      persistance# = 0.25 * 1.0# 

I'd love to know why you thought you had to do that rather than the far more straightforward (and faster):

Code:
      init_freq#   = 0.0625#
      init_amp#    = 1.0#
      persistance# = 0.25# 

I worry because it suggests a fundamental misunderstanding of how BBC BASIC works!

Richard.
User IP Logged

Michael Hutton
Developer

member is offline

Avatar




PM

Gender: Male
Posts: 248
xx Re: Perlin Noise Function
« Reply #12 on: Sep 3rd, 2009, 01:00am »

Quote:
"You must first ascertain from the documentation of the function whether the value is to be passed by reference or by value, and whether the value needs to be a float or a double; this is vitally important!".


This stems from my misunderstanding/lack of understanding of the C variable types. Although I am aware of 32-bit floats and have used them extensively in DirectX, I was unaware that 'float' meant a 32-bit float. I had dangerously assumed it would be a double. (Hence the C name double, obviously..!). I had not really come accross passing floats to DLL's before that and I had thought they would be 64-bit. (Doubles). Anyhow, the issue is now clearer.

Re: My odd looking assignment of the 64-bit float. This stemmed from my confusion to as exactly what I was passing and how BB4W assigns 40 and 64 bit floats, and when. I have lately been wishing for some 'type casting' in BB4W. I hope this is the right way of saying it. Although as an absolute beginner I appreciated being able to put an integers or floats in a 40 or 64-bit variable and have BB4W know what the type was intuitevely was I now sometimes get myself confused as to knowing exactly what is in a variable. Hence my rather confusing assignment statements. It was a belt and braces for me at the time of misunderstanding 'float' type.

For me the issue is:
Does
Code:
float1# = 1.0
 

load the 40 or 64 bit representation? Does it depend on the *FLOAT mode? (I know it's in the manual. I just forget..)
Code:
*FLOAT 64
float1# = 1.0
float2 = 1.0#
 

Are the same.

I know it is easy to compare the floats byte-wise. I realise the problem lies on this side of my keyboard...

Michael
« Last Edit: Sep 3rd, 2009, 01:01am by Michael Hutton » User IP Logged

admin
Administrator
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1145
xx Re: Perlin Noise Function
« Reply #13 on: Sep 3rd, 2009, 08:30am »

Quote:
Does float1# = 1.0 load the 40 or 64 bit representation? Does it depend on the *FLOAT mode?

It depends on the *FLOAT mode. float1# is a variant type, so it can contain a 32-bit integer, a 40-bit float or a 64-bit float (double). To force a 64-bit representation (irrespective of the mode) you must do:

Code:
float1# = 1.0# 

Both #'s are essential, the first to tell BB4W that it must reserve 8-bytes for the variable (so that it is capable of holding a 64-bit double) and the second to force a 64-bit representation of the value 1.0.

Quote:
float1# = 1.0
float2 = 1.0#

They have the same effect in *FLOAT64 mode, but (importantly) not in *FLOAT40 mode. In fact the second of the two is never sensible in *FLOAT40 mode, because it forces an internal conversion from a 64-bit float (1.0#) to a 40-bit float (float2) which you definitely don't want.

Richard.
User IP Logged

Michael Hutton
Developer

member is offline

Avatar




PM

Gender: Male
Posts: 248
xx Re: Perlin Noise Function
« Reply #14 on: Sep 3rd, 2009, 11:48am »

The float issue was a point of confusion for me but I think it is better now.

http://tech.groups.yahoo.com/group/bb4w/files/%22Temp%20Folder%22/MDCH/Perlin%20Noise_2D_hsv_1.0_simple.exe

Anyway, I have uploaded a BASIC 2D Perlin Noise Demo which is limited but gives the essence of what can be achieved. The actual drawing of the noise function is rather slow compared to the PerlinDLL but at the moment I think the Perlindll uses a colour palette which rather reduces the colour range posible. I haven't figured out how to change that yet. I hope people like the colour cycling. I think that can be used with any 32 bpp bitmap, I am just hexperimenting at the moment.

You can get some pretty pictures using octaves of 8 to 10 but they do take a while to draw...

Next up - a multithreaded ASM version!! - just joking.

Michael
User IP Logged

Pages: 1  Notify Send Topic Print
« Previous Topic | Next Topic »

| |

This forum powered for FREE by Conforums ©
Terms of Service | Privacy Policy | Conforums Support | Parental Controls