Custom Perlin Noise

53 posts

Flag Post

Alright, BitmapData has a .perlineNoise() function, however

Rather than varying from black to white (or individual channels from 0-255) I’d like to be able to dictate what the high and low colors are, hopefully without doing a double-loop through each pixel to do a color replacement (as that is slow as balls, I’ve already got a double-loop that takes two bitmaps and multiplies their channels together1, and it takes 230 ms on a 550 × 400 pixel image).

1The destination bitmap is all black except for some grayscale areas, the source bitmap is colored, thus the result is the first bitmap with the colors of the second.

 
Flag Post

Plain old noise() won’t work?

And if it takes that long to multiply you must be doing something wrong, I would think.

 
Flag Post

low:uint (default = 0) — The lowest value to generate for each channel (0 to 255).
high:uint (default = 255) — The highest value to generate for each channel (0 to 255).

Note the “for EACH channel” bit. If I want to make a gradient of red to yellow that would require one amount of Red, but a different amount of Green. And notably, I don’t want “high green, low red” but I do want “high green high red” as well as “low green, high red.”

And if it takes that long to multiply you must be doing something wrong, I would think.

I found the draw:blend methods. It quickened things right up. And, no, I wasn’t doing it “wrong” I just had a loop inside a loop and 5 function calls (4 getPixel and 1 setPixel).

 
Flag Post

you could always generate greyscale and colortransform the result

 
Flag Post

Don’t use get/setPixel, use get/setVector. Better yet, you PxB.

 
Flag Post
Originally posted by qwerber:

Don’t use get/setPixel, use get/setVector. Better yet, you PxB.

(800×500 data:) 10ms minimum, plus the time for the loop, which is no less than 25 ms for a Vector. best case: 35 ms for this alone. PxB can do it in a regular case of 45 ms (on all 4 channels. on 3, it may be faster), and it can do it asynchronously; the PxB code:

    input image4 src1;
    input image4 src2;
    output pixel4 dst;
    const float4 top = float4(1.0, 1.0, 1.0, 1.0);
    

    void
    evaluatePixel() {
        dst = (sampleNearest(src1, outCoord()) * sampleNearest(src2, outCoord())) / top;
    }
var a:Shader = new Shader(new ByteCode()); // ByteCode is am embedded class of application/octet-stream
a.data.src1.input = bmpD1;
a.data.src2.input = bmpD2;
(new ShaderJob(a, bmpD3)).start(false); // asynch, you can't reuse these

similarly, you can use your own custom AS3 inline-random int implementation (make sure it’s fast!) to set a vector in ~15ms best-case for this image using bounds (built into the random, or by using bit-wise min/max). no fewer than 3/4 pixels per loop, though for best results

 
Flag Post

Alright Skyboy, what does that code do?

 
Flag Post
Originally posted by Draco18s:

Alright Skyboy, what does that code do?

the top one is the PxB code (all of it) to compile into a shader for a Multiply Filter
the bottom is for creating the shader, setting the input parameters and then running it asynchronously. due to flash being stupid in this regard, you must dispose of each ShaderJob and you can not reuse it. pass true to perform it synchronously

 
Flag Post
Originally posted by jonathanasdf:

you could always generate greyscale and colortransform the result

Not really. Black is stays black, for one. Unless you put in an “add this color” bit, but that would add it regardless of what it was before!
(I.e. If I wanted to make “black” be yellow and “white” be “red” I’d have to make black both high red and high green, while making white high red and no green! But I can’t do that, it’s a flat “add some green” to all pixels)

Originally posted by skyboy:
Originally posted by Draco18s:

Alright Skyboy, what does that code do?

the top one is the PxB code (all of it) to compile into a shader for a Multiply Filter
the bottom is for creating the shader, setting the input parameters and then running it asynchronously. due to flash being stupid in this regard, you must dispose of each ShaderJob and you can not reuse it. pass true to perform it synchronously

Ah. I don’t really need a multiply filter, Flash has one built in, I just didn’t find it until after I made this thread.

 
Flag Post

just colortransform with rm=0 gm=1 bm=0 ra = 255 ga = 0 ba = 0…?

 
Flag Post
Originally posted by jonathanasdf:

just colortransform with rm=0 gm=1 bm=0 ra = 255 ga = 0 ba = 0…?

Ah. But see. That doesn’t give me variations in the red level. ;)

A better example would have been a yellow to blue gradient (and can you do it without going through green?1).

1The answer is “yes” as there are ways to tween the RGB values such that you transition from Yellow to Blue without passing through Green, due to the fact that Yellow and Blue are complementary: you can get from one to the other by passing through Middle Gray.

 
Flag Post

0xFFFF00 to 0×0000FF huh?

rm = -1, ra = 255, gm = -1, ga = 255, bm = 1, ga = 255

Not actually sure whether this would work though :P Would it clamp the multipliers to [0, 1] before applying it, or will it just clamp the final value to [0, 255]?

And this is just a lerp through the values, which might not produce the best gradients… in fact, I don’t know the math behind gradients at all, I doubt it’s just a simple lerp.

Also, I take back what I said before about just generating a grayscale perlin noise. You probably want to generate 3 grayscale perlin noises, one for each channel, unless it’s possible to generate perlin noise where the 3 channels do not have the same values. But anyways, given any continuous range of color values, I think it’s possible to use ColorTransform to map that to any other range, though I have not tried proving this yet.

 
Flag Post

You can generate different noise functions for each channel, and they will be different. The issue is making it avoid certain colors. Like green. Don’t want green, green bad.

Need to stay within values that could reasonably show up here

 
Flag Post
Originally posted by Draco18s:

Need to stay within values that could reasonably show up here

0xFF0000 with green increasing until 0xFF then blue increasing until 0xFF then red decreasing until 0×00

you could use perlinNoise then getVector and step through each value, ceil(green) to max(red, blue) and keeping the highest one (red, blue) to no less than 0×90

 
Flag Post

I’ll try that tomorrow.

 
Flag Post

Hmm. That didn’t quite work.

I only did a portion of the generated noise so I could take a looksee at what it was doing.

Here’s the code:

var v = vect[i];
var r = (v & 0xFF0000) >> 16;
var g = (v & 0xFF00) >> 8;
var b = (v & 0xFF);
r = .435 * r + 144; //keeps red between 144 and 255
b = .435 * b + 144; //keeps blue between 144 and 255
var m = Math.max(r,b);
g = (g > m) ? m : g;
vect[i] = (r << 16) + (g << 8) + (b)

Too much purple. Maybe I misunderstood what you were saying.

 
Flag Post

something more along these lines:

var v:int = vect[i];
var r:int = (v & 0xFF0000) >> 16;
var g:int = (v & 0xFF00) >> 8;
var b:int = (v & 0xFF);
if (b > r) {
	if (b < 0x90) b = 0x90;
	if (g > b) g = b;
	if (g < r) g = r;
} else {
	if (r < 0x90) r = 0x90;
	if (g > r) g = r;
	if (g < b) g = b;
}
vect[i] = (r << 16) + (g << 8) + (b);

the extra lower bound should keep purple out of the mix (instead trading for a red/blue grey. red may need extra checks). also, do note, red is after blue so that black and other very dark colors become red instead of blue, making super hot stars less common, and cool red ones more common

also, using just

if (r < 0x90) r = 0x90;
if (g > r) g = r;
if (g < b) g = b;

gets a very nice distribution of red, yellow and grey/white stars. i’m finding getting blue in there to be not nice

 
Flag Post

So I sat down and worked out the volumes that I want to avoid:

If the color pallet is a cube 256 units on a side, with coordinates expressed as (R,G,B), then:

Any color that is below the plane defined by the triangle:
(0,0,0),(230,255,0),(0,225,225)
//this avoids “greens”

Any color that is above the plane defined by the triangle:
(0,0,255),(255,0,50),(255,245,255)
//this avoids “magentas”/purples

Any color that is inside the circle defined as:
Sqrt(R*R+G*G) = 215

This is roughly speaking, of course. I haven’t tested all the values inside and outside those volumes, but they look pretty close.

 
Flag Post

Yeah, first one didn’t work well.

Originally posted by skyboy:

also, using just

if (r &lt; 0x90) r = 0x90;
if (g &gt; r) g = r;
if (g &lt; b) g = b;

gets a very nice distribution of red, yellow and grey/white stars. i’m finding getting blue in there to be not nice

This one….is….okish. Oddly, red ends up at a flat 0×90 across the whole image, and isn’t randomized at all.

Oh, that’s because Perlin noise generates a maximum value of…174. And rarely, at that. So 99% of the time, the red channel won’t be above 144.

Here’s the single highest value (550 × 400 image using perlinNoise(Width/4,Height/4,1,Math.random()*int.MAX_VALUE,false,false,7,true,null);) from a bunch of runs:
151
152
132
167
140
130
123
127
123
145
127
133
157
127
156

 
Flag Post
Originally posted by Draco18s:

This one….is….okish. Oddly, red ends up at a flat 0×90 across the whole image, and isn’t randomized at all.

i’d noticed the first gives bad results and was working on it, gave up, went with:

if (r < 0x90) r = 0x90;
if (g > r) g = r;
if (g < b) g = b;
if (b > r) b = Math.min(b + 0x12, 255);

not perfect, but it looks pretty good to me
.perlinNoise(320, 240, 8, 837484, false, false, 7, false, null);
.perlinNoise(320, 240, 8, 977769, false, false, 7, false, null);
.perlinNoise(320, 240, 8, 1569849, false, false, 7, false, null);
on 320×240 images

and Adobe’s algorithm, with the seeds i’ve tried, seems to avoid high red values. not sure what’s up with that. are they using Math.random() * 0xFFFFFF ?

edit: ah, i see. i was under the impression any value would be valid as a result. perhaps adding a random amount of 79 to each channel will improve it? on 837484 this is causing an overflow of green into red making a surprisingly vibrant purple. 40 does better, but it seems that this one is getting values higher than 174

 
Flag Post

I think the solution is a hand-written perlin noise function. Say this gem (excepting the fact that it only outputs in grayscale erf, oh, and it’s 9 times slower than BitmapData.perlinNoise()).

Originally posted by UnknownGuardian:

Plain old noise() won’t work?

Oh, and I forgot to mention. noise() is not smooth. It’s simply randomizing each pixel independently of every other pixel. I.e. useless.

 
Flag Post
Originally posted by Draco18s:

(oh, and it’s 9 times slower than BitmapData.perlinNoise()).

it was 32x slower on my machine (3 seconds, vs 100 ms) but i managed to properly optimize it (some of the things quasi did slowed it down: verbose int on int variables, using ?: instead of if() (bad compile output for former)) to the point it’s a mere 3x slower on my machine.

these lines removed a lot of work:

xf = x - (x % 1);//Math.floor(x);
yf = y - (y % 1);//Math.floor(y);
zf = z - (z % 1);//Math.floor(z);

the rest of it was stripping conditionals and replacing with pure math.
also, in my optimizations, i rotated the output 180 degrees, if that’s a problem

after optimizations, upping it from 4 to 8 octaves, same as perlinNoise, it’s 7.9x slower (109ms vs 859ms). on your machine, that difference outta be lower

creating a new object (one of the optimizations. also allows for multiple instances with different properties/seeds) and calling its fill method instead of the static method results in 6.6x slower operation.

 
Flag Post

Oh man, this perlin noise generator is SO MUCH BETTER.

I mean, look at these results!

Not bright enough, but that is solvable!

Originally posted by skyboy:
Originally posted by Draco18s:

(oh, and it’s 9 times slower than BitmapData.perlinNoise()).

it was 32x slower on my machine (3 seconds, vs 100 ms) but i managed to properly optimize it (some of the things quasi did slowed it down: verbose int on int variables, using ?: instead of if() (bad compile output for former)) to the point it’s a mere 3x slower on my machine.

these lines removed a lot of work:

xf = x - (x % 1);//Math.floor(x);
yf = y - (y % 1);//Math.floor(y);
zf = z - (z % 1);//Math.floor(z);

My work machine is afucking beast. It chews through unoptomized code like candy (8x slower on home machine).
Changing those lines actually did….nothing to my home machine’s speed. 1909-1986 versus 1924-1981ms (for 3 calls). ~236 for BitmapData.perlinNoise() (also 3 calls).

Although, this is what the same code using the bitmap data function outputs:

Like, WTH?

 
Flag Post

if you’re interested in the other optimizations i made

you seem to be using a low number of octaves… you might try 8 or so, more colors. 320, 240, 8, 10446200, false, false makes for some vibrant blues, greens and a small patch of red/purple that will probably turn white

 
Flag Post
Originally posted by skyboy:

if you’re interested in the other optimizations i made

you seem to be using a low number of octaves… you might try 8 or so, more colors

I’m using 1. 2 octaves doesn’t give me better results (see this ) and takes almost twice as long (3732ms) because I have to call the .fill() function three times, as each call generates grayscale maps.

And it doesn’t like this line (after I converted the vectors back to arrays because Flash IDE doesn’t like new <int>[...]:

internal const oNoise:OptimizedPerlin = new OptimizedPerlin;
1180: Call to a possibly undefined method OptimizedPerlin.