Perlin noise in a fragment shader

Did anyone ever come across a really good implementation of Perlin noise done in a fragment shader anywhere?

Not Perlin noise but “simplex noise”.
Do a seach for it in the OpenGL Shading Forum.

Here’s the “improved” Perlin noise.

http://free-zg.htnet.hr/KishBros/Kresimir/PerlinHW/PerlinHW.htm

I doubt you’ll find it very useful, it pretty much eats up the entire instruction count on SM2 hardware.

In the NVSDK you can look at:
MEDIA/programs/cg_gnoise/fp16noise.cg

For an implementation of Perlin’s traditional
noise function.

It’s part of the cg_gnoise demo.

I’ve been thinking about doing one for simplex noise as well.

Have a look at simplex noise paper and implementation with source code:
simplex noise

and at the bottom of this page:
noise example

kon

I’m not sure if this helps, but I commonly use a lookup table for noise. I generate noise at startup and quantize into a reasonably sized 3D texture. I use 8x8x32 with a 32-bit RGBA (each component is 8 bits) color texture. On some of the more recent cards, a texture less than 8K will fit entirely in the texture cache (or so it seems from some of my perf tests). I address the texture using vec3 texcoord = gl_FragCoord.xyz * vec2(0.125, 0.125, sample / 32) with the addressing mode set to repeat and the filtering mode set to nearest. Hope this helps.

Kevin B

That old GLSL demo of mine actually had regular Perlin noise as well as simplex noise in there, it was just not the default. Look at the shader source and you will see six functions: both regular and simplex noise for 2D, 3D and 4D noise. It’s a pretty hefty shader, but it does run on any Nvidia card from FX5xxx and up, and with good performance on anything from FX6800 and up. On some older ATI cards the 3D and 4D versions exceed the maximum instruction count, but the 2D versions work fine even on pretty outdated hardware.

Hiya,

thanks very much for sharing your code. I’m looking forward to giving it a go!
Do you happen to have example textures for use with the example shaders, to get me started?

Cheers,

a|x
http://machinesdontcare.wordpress.com

There are builtin noise functions in GLSL.

There currently is a discussion on the Mesa mailing list about how to implement noise the best way. The proposal currently discussed seems to work fine even on Intel cards.

Philipp

Maybe I’ve got the wrong end of the stick here… is the 4-channel noise texture kon mentions in the readme in the example zip (I downloaded the MacOS X port package) simply an RGBA texture with random values in all channels? Or, are the values in the alpha channel different to those in the other 3 channels?

Sorry again for asking stupid questions,

a|x
http://machinesdontcare.wordpress.com

Hi,

I’m trying to implement 3D simplex noise from the example found in the GLSL fragment shader in this archive:
http://staffwww.itn.liu.se/~stegu/simplexnoise/MacOSX-port/
and am not sure that I’ve got it right.


The texture I get isn’t quite as random as I’d hope. As you can see, it exhibits very obvious diagonal stripes, and looks nothing like the builtin GLSL noise1(x,y,z) function.

I’m using a 256x256px permutation texture with random values in each of the 4 channels.

Anyone any idea what might be going wrong?

a|x
http://machinesdontcare.wordpress.com

Hi Toneburst.

Indeed, it doesn’t quite look like regular perlin noise or simplex noise. Instead, have you tried using the permutation texture that is generated in the C code? When I ported “GLSL-noise.zip” to Java, I got the expected results with the “permTexture” (I didn’t use the gradTexture because I didn’t need 4D Simplex Noise).

BTW: Interesting. I’ve never seen the GLSL noise function return anything useful on my implementation - infact, the assembly just seems to replace the value from noise with the constant “0.0”. Has it only been implemented recently? What does it look like?

Looks like your octaves are not right, it can be the scale of each level, or the amplitude, or both.

Ive seen that two when I impplemented noise (but such a long time ago I forget what the cause was)

one thing to do is to turn off the filtering on the textures, including the aniostropic filtering

Wow, so many responses!

Nicolai, could you possibly post the permTexture image you used? I’m afraid I don’t know how to generate the texture from the C code. I’m embarrassed to say I’m really a relative programming newbie- I only posted in the Advanced forum because I came across this thread while doing a search for noise implementations.

Here’s an example of GLSL noise1() running on my MacBook Pro. It’s quite possible that it’s running in software, rather than on the GPU (though I haven’t checked).

ZbuffeR, there’s only the one octave there. Maybe that’s the problem…
Actually, though, I seem to get the same diagonal banding whatever scale I use, so adding octaves isn’t going to help, I don’t think.

zed, unfortunately, I don’t have the option of changing filtering modes. I’m using an application that allows GLSL shaders to be used, but doesn’t let filtering options be changed.

Thanks for getting back to me guys! Anything else I could try?

a|x

ToneBurst:

Thanks for the screenshot of the GLSL noise() function.

I wrote out the permTexture with DevIL. The texels are not computed from Stefan Gustavson’s reference code in C but from my conversion. I ran it through ImageMagick “identify -verbose” and uploaded the output here. If someone thinks this looks wrong, please post a message. Looking at the histogram it certainly looks noisy and when used with the GLSL shaders from Gustavson, it produces what I expected simplex noise example. Rendered in ortho with normalized texture coordinates with the shaders:


 /*
 * Author: Stefan Gustavson ITN-LiTH (stegu at itn.liu.se) 2004-12-05
 * You may use, modify and redistribute this code free of charge,
 * provided that my name and this notice appears intact.
 *
 * Modified: Nicolai de Haan Brøgger (ndhb78 at gmail.com)
 */
 uniform vec2 noiseFrequency;
 uniform vec2 noiseOffset;
 
 void main(void) {
	gl_TexCoord[0].st = gl_MultiTexCoord0.st;
	gl_TexCoord[0].st += noiseOffset;
	gl_TexCoord[0].st *= noiseFrequency;
	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
} // function


/*
 * Author: Stefan Gustavson ITN-LiTH (stegu at itn.liu.se) 2004-12-05
 * You may use, modify and redistribute this code free of charge,
 * provided that my name and this notice appears intact.
 *
 * Modified: Nicolai de Haan Brøgger (ndhb78 at gmail.com)
 */
uniform sampler2D noisePermTexture;

/*
 * To create offsets of one texel and one half texel in the
 * texture lookup, we need to know the texture image size.
 */
#define ONE 0.00390625 // change accordingly if you change the code to use another texture size (1 / 256) 
#define ONEHALF 0.001953125  // change accordingly if you change the code to use another texture size (0.5 / 256)

/*
 * The interpolation function. This could be a 1D texture lookup
 * to get some more speed, but it's not the main part of the algorithm.
 */
float fade(float t) {
  return t*t*t*(t*(t*6.0-15.0)+10.0); // Improved fade, yields C2-continuous noise
} // function

/*
 * 2D simplex noise. Somewhat slower but much better looking than classic noise.
 */
float snoise(vec2 P) {
	// Skew and unskew factors are a bit hairy for 2D, so define them as constants
	#define F2 0.366025403784 // This is (sqrt(3.0) - 1.0) / 2.0
	#define G2 0.211324865405 // This is (3.0 - sqrt(3.0)) / 6.0

	// Skew the (x,y) space to determine which cell of 2 simplices we're in
 	float s = (P.x + P.y) * F2;   // Hairy factor for 2D skewing
  	vec2 Pi = floor(P + s);
  	float t = (Pi.x + Pi.y) * G2; // Hairy factor for unskewing
  	vec2 P0 = Pi - t; // Unskew the cell origin back to (x,y) space
  	Pi = Pi * ONE + ONEHALF; // Integer part, scaled and offset for texture lookup

  	vec2 Pf0 = P - P0;  // The x,y distances from the cell origin

  	// For the 2D case, the simplex shape is an equilateral triangle.
  	// Find out whether we are above or below the x=y diagonal to
  	// determine which of the two triangles we're in.
  	vec2 o1;
  	if(Pf0.x > Pf0.y)
  		o1 = vec2(1.0, 0.0); // +x, +y traversal order
  	else
  		o1 = vec2(0.0, 1.0); // +y, +x traversal order

  	// Noise contribution from simplex origin
  	vec2 grad0 = texture2D(noisePermTexture, Pi).rg * 4.0 - 1.0;
  	float t0 = 0.5 - dot(Pf0, Pf0);
  	float n0;
  	if (t0 < 0.0) {
  		n0 = 0.0;
  	} else {
    	t0 *= t0;
    	n0 = t0 * t0 * dot(grad0, Pf0);
  	} // if else

  	// Noise contribution from middle corner
  	vec2 Pf1 = Pf0 - o1 + G2;
  	vec2 grad1 = texture2D(noisePermTexture, Pi + o1*ONE).rg * 4.0 - 1.0;
  	float t1 = 0.5 - dot(Pf1, Pf1);
  	float n1;
  	if (t1 < 0.0) {
  		n1 = 0.0;
  	} else {
	    t1 *= t1;
		n1 = t1 * t1 * dot(grad1, Pf1);
  	} // if else
  
	// Noise contribution from last corner
  	vec2 Pf2 = Pf0 - vec2(1.0-2.0*G2);
  	vec2 grad2 = texture2D(noisePermTexture, Pi + vec2(ONE, ONE)).rg * 4.0 - 1.0;
  	float t2 = 0.5 - dot(Pf2, Pf2);
  	float n2;
  	if(t2 < 0.0) {
  		n2 = 0.0;
	} else {
		t2 *= t2;
		n2 = t2 * t2 * dot(grad2, Pf2);
	} // if else

	// Sum up and scale the result to cover the range [-1,1]
  	return 70.0 * (n0 + n1 + n2);
} // function

void main(void) {
	float n = snoise(gl_TexCoord[0].st);
	gl_FragColor = vec4(0.5 + 0.5 * vec3(n, n, n), 1.0); // Bias to range [0.0 : 1.0] and set fragment to fully opaque. 
} // function

Try my implementation. Unfortunately it slows down at high magnifications, but it supports anisotropic filtering…

http://lumina.sourceforge.net/Tutorials/Noise.html

Thanks for the extra info oc2k1 and Nicolai de Haan Brøgger!
I hadn’t checked this thread for a while, but I’ve come back to this problem now, so I’ll check out both your leads.

Cheers again,

a|x