Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

DarkBASIC Professional Discussion / help needed with Perlin noise generator

Author
Message
Green Gandalf
VIP Member
19
Years of Service
User Offline
Joined: 3rd Jan 2005
Playing: Malevolence:Sword of Ahkranox, Skyrim, Civ6.
Posted: 17th Feb 2008 21:09
I've been having one of those really frustrating days.

I've written a Perlin noise generator to replace my earlier cloud image generating program which used the "diamond-square algorithm".

The resulting image was produced quickly and efficiently and looked like a green cloudy black sky (before you ask it was supposed to look like a green cloudy black sky ) - but wasn't quite right (see attached image). At first glance it seems OK, but on closer inspection there are visible lines or seams along the "power of two pixel" boundaries.

After checking for all sorts of rounding errors, misaligned byte boundaries and such, it dawned on me that I was using simple bilinear interpolation instead of "Perlin" interpolation. This explained everything.

I therefore changed the interpolation to Perlin interpolation (so I believe ) and expected a perfect image to pop out. It didn't . And it's much worse - and I just can't see why.

Here's the code I'm using (it's not optimised yet, so I know it can be speeded up in places):


The next post contains the very faulty image produced using what I believe is the correct Perlin interpolation.

The only changes in the code used for the two images are in the following lines where I just comment/uncomment the appropriate sections:


The above gives the bilinear filtering which is almost right.

I'm probably missing something very obvious but just can't see it.

Attachments

Login to view attachments
Green Gandalf
VIP Member
19
Years of Service
User Offline
Joined: 3rd Jan 2005
Playing: Malevolence:Sword of Ahkranox, Skyrim, Civ6.
Posted: 17th Feb 2008 21:10
Here's the second image.

Attachments

Login to view attachments
El Goorf
17
Years of Service
User Offline
Joined: 17th Sep 2006
Location: Uni: Manchester, Home: Dunstable
Posted: 18th Feb 2008 01:52
ah yeh, i noticed this issue with diamond square. it should kinda be expected from the fact you're breaking the image down into squares. i couldnt rly find a lot to do to fix this, though i didnt think it would be an issue with perlin noise, though i guess you just prooved me wrong.

if you find a solution, feel free to eemail it to me ^_^

http://notmybase.com
All my base are not belong to anyone.
Omen
17
Years of Service
User Offline
Joined: 7th Nov 2006
Location: Maple Grove, MN US
Posted: 18th Feb 2008 02:20 Edited at: 18th Feb 2008 02:23
@GG,

Take a look at this page, and especially the "putting it all together" section at the bottom:

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

or you could go straight to the source:

http://mrl.nyu.edu/~perlin/

...hope that helps.

Green Gandalf
VIP Member
19
Years of Service
User Offline
Joined: 3rd Jan 2005
Playing: Malevolence:Sword of Ahkranox, Skyrim, Civ6.
Posted: 18th Feb 2008 12:15
Quote: "though i didnt think it would be an issue with perlin noise"


Neither did I - and it shouldn't be according to my maths. But I've obviously gone wrong somewhere.


Quote: "or you could go straight to the source:"


I've already seen that - but it's quite possible I've slipped up somehow. Just can't see where. Might be worth another look at the source now that I've got the basic idea working. So, thanks for the nudge.

Quote: "if you find a solution, feel free to eemail it to me"


Will do - but don't expect an instant solution. No time for thinking till the weekend now.
Mr Kohlenstoff
17
Years of Service
User Offline
Joined: 7th Jun 2006
Location: Germany
Posted: 18th Feb 2008 12:36
I used cosine interpolation for my perlin noise-algorithms and it works quite well. You could also use this cubic interpolation to make it more natural, but because it's able to grow out of the amplitude you should be careful with converting your noise-values into colors.

By the way, aren't diamont square and perlin noise actually the same.. more or less? Both are Fractal Noise-Algorithms and work in more or less the same way, at least that's what I thought.

However, good luck with your green clouds and black sky.

dark coder
21
Years of Service
User Offline
Joined: 6th Oct 2002
Location: Japan
Posted: 18th Feb 2008 13:38
Didn't really look at your code but I just made my own Perlin noise gen which I find easier to follow, it has both Bicubic and Bilinear filtering(you can comment, uncomment them). Hopefully it will help .



Green Gandalf
VIP Member
19
Years of Service
User Offline
Joined: 3rd Jan 2005
Playing: Malevolence:Sword of Ahkranox, Skyrim, Civ6.
Posted: 18th Feb 2008 22:57 Edited at: 19th Feb 2008 00:23
Quote: "Didn't really look at your code but I just made my own Perlin noise gen which I find easier to follow, it has both Bicubic and Bilinear filtering(you can comment, uncomment them). Hopefully it will help "


Thanks for that. It works nicely.

However, how would you extend it to 3D? The changes to my code are "obvious". How would you change yours?

I'm not yet sure how your cubic interpolation differs from the interpolation I'm trying to use - but whatever it is yours obviously works.

Omen

I've aleady seen that first reference you gave and decided not to use it because it looked unnecessarily complicated. The reference I gave made more sense to me:

http://www.mandelbrot-dazibao.com/Perlin/Perlin1.htm

and seemed closer to Perlin's suggestions to me. However, it's a while since I checked his website so perhaps my memory is playing tricks. I'll check it again and post back.

I notice that your first reference seems to use the same cubic interpolation that Dark Coder is using. It is also obvious from that first reference that the method extends to 3D (I found it hard to unravel what was going on in Dark Coder's memblock arrays - no doubt he thought the same about mine ). At least his works.

Edit

I've just checked Perlin's website from Omen's second link and it confirms that I'm using (or trying to use) Perlin's original interpolators (you need to follow the links to his 2002 paper). Perlin has suggested a revised version which is supposed to be better. However, in my code it's still worse than bilinear filtering, so I must be mis-applying both of them in some way. Still no idea where the problem lies - or why simple bilinear filtering is so good in comparison in my code. There has to be a simple bug staring me in the face somewhere in my code ...
Gamer Making
17
Years of Service
User Offline
Joined: 20th Sep 2006
Location: sitting at the comp programming
Posted: 19th Feb 2008 03:09
What is a perlin Noise generator?

Bach Tran
Benjamin
21
Years of Service
User Offline
Joined: 24th Nov 2002
Location: France
Posted: 19th Feb 2008 03:12
dark coder
21
Years of Service
User Offline
Joined: 6th Oct 2002
Location: Japan
Posted: 19th Feb 2008 11:01
Quote: "However, how would you extend it to 3D? The changes to my code are "obvious". How would you change yours?"


I haven't tried it, but I'd imagine for linear interpolation at least you'd just do the standard 4 samples(2 per axis) on a 2D(X/Z) axis(while reading the current height), then do the same but for the plane above you then linearly interpolate between the two results based on the distance from the lower Y value.

Also if anyone's interested(while not totally related to these boards) I ported my code as best I could to GDK, without any compiler optimizations it's almost exactly 2x the speed on my machine, code's below:



You can pick between the 3 interpolation methods in the defines.

I also made a far more optimized version of this just to see how fast I could get the routine, with compiler optimizations I managed to get it down to 427ms to generate a 512^2px image with 8 octaves, which is far faster than in DBP as I get 6800ms in that, however my DBP code isn't quite as optimized, but there's only so much you can do there.



Green Gandalf
VIP Member
19
Years of Service
User Offline
Joined: 3rd Jan 2005
Playing: Malevolence:Sword of Ahkranox, Skyrim, Civ6.
Posted: 19th Feb 2008 23:02 Edited at: 19th Feb 2008 23:11
Quote: "There has to be a simple bug staring me in the face somewhere in my code ..."


There was - but only when I looked in the right place.

Here's the revised code:



and it works perfectly.

The code allows you to change the method of interpolation by commenting/uncommenting a few lines. The methods are:

1. Simple bilinear interpolation.
2. Ken Perlin's original interpolator.
3. Ken Perlin's revised interpolator (see his 2002 paper).
4. Green Gandalf's sine interpolator ( ).

The slowest is the last (because of the sine function) but is a simple formula and gives results similar to Perlin's revised interpolator.

The only one that shows visible seams is, as expected, simple bilinear interpolation.

And I attach some nice green clouds.

Thanks everyone for your interest and help in this - especially to Dark Coder who has given us another useful tool to play with. I think my code is slower than Dark Coder's but I suspect the images are slightly better - will need to experiment a bit to decide on that one. [Edit: NO! Mine seems to be much faster - and DC's images have some diagonal "criss-crossing" on them. Looks like it pays to stick to K. Perlin's methods.]

My code needs optimising - and Dark Coder's idea of using memblocks might speed things up a bit. Not sure yet. Will post back if I successfully refine mine any further.

Attachments

Login to view attachments
dark coder
21
Years of Service
User Offline
Joined: 6th Oct 2002
Location: Japan
Posted: 20th Feb 2008 06:46 Edited at: 20th Feb 2008 07:57
Quote: "Mine seems to be much faster"


Quite likely as my code isn't very optimized, as I call many functions in my loop and DBP has no concept of inline optimization, however if you want a challenge you shall have one :p.

Quote: "DC's images have some diagonal "criss-crossing""


Maybe it was just that seed, when viewing a single octave I found the opposite actually, here's a sample from one of my ones with filtering:


Yours:


Also with your one I tried two seeds and got the same results, also I made them both use my desktop's resolution so there can be no issues with the window's filtering affecting anything(as there is none then).

[edit] the octave resolutions look slightly different but I can't get one the same as yours, here's my octave 1(highest frequency) http://img171.imageshack.us/img171/4753/dcoctave1ru1.png

[edit 2]
Also, I noticed that your code uses cosine interpolation, whereas the version I posted didn't have this and used Cubic interpolation by default, here's a far more optimized DBP version of my code using bicosine interpolation:



With this code it takes me 1658ms to generate a single 512x8 perlin noise texture, and it takes me 2516ms with yours.

Green Gandalf
VIP Member
19
Years of Service
User Offline
Joined: 3rd Jan 2005
Playing: Malevolence:Sword of Ahkranox, Skyrim, Civ6.
Posted: 20th Feb 2008 12:37
Quote: "however if you want a challenge you shall have one"


Your version performs somewhat faster on my machine too. Looks like I'd better optimize my code. Will have a go later after work.

Quote: "Also with your one I tried two seeds and got the same results, also I made them both use my desktop's resolution so there can be no issues with the window's filtering affecting anything(as there is none then)."


Yes I get those lines with the high resolution octave with my version too. The odd thing is that they don't seem to show in the final image.

Watch this space.
Green Gandalf
VIP Member
19
Years of Service
User Offline
Joined: 3rd Jan 2005
Playing: Malevolence:Sword of Ahkranox, Skyrim, Civ6.
Posted: 20th Feb 2008 23:49 Edited at: 21st Feb 2008 00:16
Dark Coder

Before I bust a gut trying to optimize my code, could you clarify a point about your code? How many octaves are you using? I'm using 9, i.e. my octave 0 uses a 2x2 grid of points (just the four corners of the image), octave 1 uses a 4x4 grid and so on up to octave 8 which uses a 512x512 (which is rather superfluous).

Your high res octave seems to be only 256x256. Could you confirm that?

When I reduce mine to a max resolution of 256x256 (i.e. octaves 0 to 7 in my notation) my image looks the same (the extra high res octave adds nothing) and my code takes about 6089 ms to produce the image, whereas yours takes about 6400 (225+6175) ms. I haven't tried optimising mine yet (I know at least one place where I can). See "health" warning edit at end.

I still have those noticeable lines in the 256x256 octave image - but, again, they don't show in the final cloud image. I vaguely recall Perlin mentioning something about that in his paper - I'll have to look at it again.

Only good can come of this - between us we should end up with fast code for producing this sort of image. But I guess we are some way off producing them in real time either way.

Edit I've pruned a bit of time off my code - but I've just realised that I hadn't checked that I was comparing like with like in my timings above, i.e. which interpolators were being used in the two sets of code. I'll check this again tomorrow, must sign off now. .
dark coder
21
Years of Service
User Offline
Joined: 6th Oct 2002
Location: Japan
Posted: 21st Feb 2008 06:50
Quote: "Your high res octave seems to be only 256x256. Could you confirm that?"


No, it might be slightly hard to read but after my loop I use to add the octaves I add my highest resolution octave(512^2), because it's the same resolution as the canvas I don't need to do any interpolation and can just do a straight lookup, below is some even more optimized code too, also I'm recoding my C++ version as I've thought up several more ways to speed it up .

However you're correct about my 8 octaves being 1-8, but the way mine works is that is I specify a lower number of octaves the end result becomes more detailed at it merely trims off the lower resolution octaves, so for a large canvas you wouldn't want to use octaves all the way down to 2x2 else you'd likely end up with one gigantic hill or mountain, but using a lower number results in many mountains.

Either, way I changed my code to 9 octaves(1-9) so it should be the same as yours and I still get faster speed , I also increaced the weights/strength/opacity between the layers slightly so you can see the result better.



Green Gandalf
VIP Member
19
Years of Service
User Offline
Joined: 3rd Jan 2005
Playing: Malevolence:Sword of Ahkranox, Skyrim, Civ6.
Posted: 21st Feb 2008 14:27
Quote: "I'm using 9, i.e. my octave 0 uses a 2x2 grid of points (just the four corners of the image), octave 1 uses a 4x4 grid and so on up to octave 8 which uses a 512x512 (which is rather superfluous)."


Looks like I can't count - not enough fingers. Octave 0 should use a 2x2 grid, octave 1 a 3x3 grid, and so on up to octave 8 which uses a 257x257 grid of points (i.e. a 256x256 grid of squares or "tiles"). Will have to check my code to see if any similar errors have crept in.

With my code the next octave, i.e. a 513x513 grid, would (should?) literally add nothing as the octaves are defined as zero at the grid points.

Will go over my code more carefully later - need to identify the step(s) which is(are) eating the time.

Quote: "Either, way I changed my code to 9 octaves(1-9) so it should be the same as yours and I still get faster speed"


That doesn't surprise me given the errors that have crept into my code.
dark coder
21
Years of Service
User Offline
Joined: 6th Oct 2002
Location: Japan
Posted: 21st Feb 2008 16:09
Also if you need something else to compete against, I rewrote my C++ version using all the optimizations I could think of and got a low of 75ms for 512^2px texture with 8 octaves(with BiCosine interpolation), the only way you could probably get faster than this would be to use straight asm with major optimized instructions with SSE and/or multiple threads, however both are beyond my current C++ knowledge.

Anyway, this code will go very nicely in my game, plus all these optimizations have taught me some things I didn't know before(i.e. how slow DBP is) .

jason p sage
16
Years of Service
User Offline
Joined: 10th Jun 2007
Location: Ellington, CT USA
Posted: 21st Feb 2008 16:33
Quote: "plus all these optimizations have taught me some things I didn't know before(i.e. how slow DBP is) "


@DarkCoder- with the stuff I've seen you do - I'm surprised at this one!

Green Gandalf
VIP Member
19
Years of Service
User Offline
Joined: 3rd Jan 2005
Playing: Malevolence:Sword of Ahkranox, Skyrim, Civ6.
Posted: 21st Feb 2008 18:18
Quote: "Also if you need something else to compete against, I rewrote my C++ version using all the optimizations I could think of and got a low of 75ms"


Another reason for me to learn C++.


Quote: "plus all these optimizations have taught me some things I didn't know before(i.e. how slow DBP is)"


Yes, I wondered at that statement too - but it is useful to have some benchmark comparisons which DC's posts have provided.

Why is DBP so slow, anyway? The executables must be doing a lot of unnecessary stuff - or is it a consequence of the executables being so large? Anyone know?
Benjamin
21
Years of Service
User Offline
Joined: 24th Nov 2002
Location: France
Posted: 21st Feb 2008 18:32 Edited at: 21st Feb 2008 18:37
Quote: "Why is DBP so slow, anyway?"

After every line of code (I think) is four instructions. The first line sets the global line number variable, and next three lines check the global error variable and jump to the end of the program if it is set. This check also occurs after function calls (which is the only place it should ever happen really). This is quite a bit of overhead if you're doing intensive calculations.

And then there's instructions that are longer than they need to be. Instructions that specify a local variable use a 32-bit offset when in most cases it only needs to be 8-bit.

Then there's also redundant instructions - stores and reads that contradict each other. Some of these probably cause a pipeline stall, losing more processing time. An example of what I mean, in assembly:



Not enough use of registers - results are put straight back into memory rather than storing them in registers until needed.

No constant folding, which means code like this will perform calculations at run-time:



A lot of these things could probably be fixed with a post-optimizer. We'll have to see about that.

Green Gandalf
VIP Member
19
Years of Service
User Offline
Joined: 3rd Jan 2005
Playing: Malevolence:Sword of Ahkranox, Skyrim, Civ6.
Posted: 21st Feb 2008 19:18
Unnecessary "mov" instructions occur at a low level and the programmer can't do much about those - but dbp code like your example



is the sort of thing the programmer can eliminate and is one of the first things I would look for when optimising my code. Another thing to do is to avoid calculating something twice - my code "snippets" on this thread recalculate the same interpolation weights many times over, i.e. that's an obvious candidate for optimisation.

Another thing we can try is to use integer arithmetic rather than float arithmetic where possible.

But obviously, after a certain point, we are stuck with the extra DBP stuff you mention.
Benjamin
21
Years of Service
User Offline
Joined: 24th Nov 2002
Location: France
Posted: 21st Feb 2008 19:27 Edited at: 21st Feb 2008 19:28
Quote: "is the sort of thing the programmer can eliminate and is one of the first things I would look for when optimising my code"

You're right, it's not such a valid point. It's merely useful sometimes to write out an equation rather than a constant.

Quote: "Another thing we can try is to use integer arithmetic rather than float arithmetic where possible."

Agreed, and if using floats, replace divisions by multiplications by the reciprocal where possible.

Another thing I'm not too keen on is how DBP handles float casting, it seems slower than it should be. It takes only a couple of instructions to convert between integer/float, but DBP actually calls a function for this.

Quote: "But obviously, after a certain point, we are stuck with the extra DBP stuff you mention."

Well, if someone were to write an optimiser...

jason p sage
16
Years of Service
User Offline
Joined: 10th Jun 2007
Location: Ellington, CT USA
Posted: 21st Feb 2008 19:41
TinTin
17
Years of Service
User Offline
Joined: 16th May 2006
Location: BORG Drone Ship - Being Assimilated near Roda Beta (28)
Posted: 22nd Feb 2008 00:09 Edited at: 25th Feb 2008 16:06
Haven't read this whole post yet so appologies in advance if someone has already mentioned this...

Perlin Noise (I was using this in another project, GG you know the one ) it's better known as 'Gradient Noise'

Any way here is one of Ken's own implementation in C++ from Texturing & Modeling. This method uses a precalculated table of random values.



His own website has some interesting articles that may be of use but basicaly perin noise is generated from a calculation using the standard noise function in either 1, 2, 3 or 4 dimentions...
yeah! 4-Dimentions. the fourth being time and can be used to animate the noise.

from the top of my head here are the first two on generating noise, I'll look out the code for the others...
I remember something about artifacts occuring at integer intersections, and perins method atempted to interpolate between these to remove or smooth them out.

notice the big value calculations are the same and only the first line realy changes this trend stays the same for the other dimentions.

Here is the DBP equivalent code, unfortunatly there seems to be an issue with the wrong result from the bit shift ^ part. The bit shift works as expected but squaring it always gives the same answer if the input value is over 2. (odd!)



Cyberspace was becoming overcrowded and slummy so I decided to move. These nice chaps gave me a lift.
Green Gandalf
VIP Member
19
Years of Service
User Offline
Joined: 3rd Jan 2005
Playing: Malevolence:Sword of Ahkranox, Skyrim, Civ6.
Posted: 22nd Feb 2008 01:34 Edited at: 22nd Feb 2008 01:35
dark coder

This code seems to run somewhat faster than your code. It uses 9 octaves numbered 0 to 8 starting with a single quad, i.e. a 2x2 grid of four corner values working up to a grid of 256x256 quads using 257x257 grid points.

This code on my machine takes a total of about 4737 ms whereas your latest code takes about 7000 ms.



I haven't investigated the diagonal line issue yet - it could be the rnd(1) calls but I'm not sure.

Your comments about the low order octaves not always being desirable is a good one and hadn't occurred to me. You can obviously get somewhat different effects by modifying the relative weights of the different octaves - and omitting a few entirely is obviously a special case.

There is no way I can compete with your C++ achievements. (Yet?)

TinTin

Quote: "it's better known as 'Gradient Noise'"


I've never heard it called that before - but it's obviously appropriate.
Green Gandalf
VIP Member
19
Years of Service
User Offline
Joined: 3rd Jan 2005
Playing: Malevolence:Sword of Ahkranox, Skyrim, Civ6.
Posted: 22nd Feb 2008 12:21 Edited at: 22nd Apr 2012 13:53
Quote: "I haven't investigated the diagonal line issue yet - it could be the rnd(1) calls but I'm not sure."


Yep. That was the problem. DBP must do something silly when computing "rnd(1)".

Replacing the following code:



with:



fixes the problem with similar timings.

I still like the simplicity of working with a random binary sequence, so I'll see if I can find a fast pseudo-random binary sequence generator. It would need to be significantly faster than DBP's rnd() command to be worthwhile - and even then, looking at the timings using my previous code snippet, not much can be saved anyway.

Edit Fixed spelling typo.
Green Gandalf
VIP Member
19
Years of Service
User Offline
Joined: 3rd Jan 2005
Playing: Malevolence:Sword of Ahkranox, Skyrim, Civ6.
Posted: 22nd Feb 2008 13:19
Of course, my old diamond-square algorithm for producing noisy cloud-like images is even faster - and you get three noise images for the price of one, i.e. one for each colour channel.

Here's a slightly updated version of the diamond-square code:



Of course, I could try to optimise that as well - a casual glance suggests several candidates for optimisation. And I'd like to re-write it using functions rather than gosubs. Enough for now though.

And here's an image created by that code.

Attachments

Login to view attachments
jason p sage
16
Years of Service
User Offline
Joined: 10th Jun 2007
Location: Ellington, CT USA
Posted: 22nd Feb 2008 14:50

Login to post a reply

Server time is: 2024-04-20 09:09:48
Your offset time is: 2024-04-20 09:09:48