23
Jun
09

2D Perlin Vertex Noise GLSL Sourcecode

Vertex Shader:

/*
	2D Perlin-Noise in the vertex shader, based originally on
	vBomb.fx HLSL vertex noise shader, from the NVIDIA Shader Library.

http://developer.download.nvidia.com/shaderlibrary/webpages/shader_library.html#vbomb

	Original Perlin function substituted for Stefan Gustavson's
	texture-lookup-based Perlin implementation.
	
	Quartz Composer setup
	toneburst 2009

http://machinesdontcare.wordpress.com

*/

////////////////////////
//  2D Perlin Noise   //
////////////////////////

/*
	2D Perlin-Noise from example by Stefan Gustavson, found at

http://staffwww.itn.liu.se/~stegu/simplexnoise/

*/

uniform sampler2D permTexture;			// Permutation texture
const float permTexUnit = 1.0/256.0;		// Perm texture texel-size
const float permTexUnitHalf = 0.5/256.0;	// Half perm texture texel-size

float fade(in float t) {
	return t*t*t*(t*(t*6.0-15.0)+10.0);
}

float pnoise2D(in vec2 p)
{
	// Integer part, scaled and offset for texture lookup
	vec2 pi = permTexUnit*floor(p) + permTexUnitHalf;
	// Fractional part for interpolation
	vec2 pf = fract(p);
	
	// Noise contribution from lower left corner
	vec2 grad00 = texture2D(permTexture, pi).rg * 4.0 - 1.0;
	float n00 = dot(grad00, pf);
	
	// Noise contribution from lower right corner
	vec2 grad10 = texture2D(permTexture, pi + vec2(permTexUnit, 0.0)).rg * 4.0 - 1.0;
	float n10 = dot(grad10, pf - vec2(1.0, 0.0));
	
	// Noise contribution from upper left corner
	vec2 grad01 = texture2D(permTexture, pi + vec2(0.0, permTexUnit)).rg * 4.0 - 1.0;
	float n01 = dot(grad01, pf - vec2(0.0, 1.0));
	
	// Noise contribution from upper right corner
	vec2 grad11 = texture2D(permTexture, pi + vec2(permTexUnit, permTexUnit)).rg * 4.0 - 1.0;
	float n11 = dot(grad11, pf - vec2(1.0, 1.0));
	
	// Blend contributions along x
	vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fade(pf.x));
	
	// Blend contributions along y
	float n_xy = mix(n_x.x, n_x.y, fade(pf.y));
	
	// We're done, return the final noise value.
	return n_xy;
}

/////////////////////
// Sphere Function //
/////////////////////

const float PI = 3.14159265;
const float TWOPI = 6.28318531;
uniform float BaseRadius;

vec4 sphere(in float u, in float v) {
	u *= PI;
	v *= TWOPI;
	vec4 pSphere;
	pSphere.x = BaseRadius * cos(v) * sin(u);
	pSphere.y = BaseRadius * sin(v) * sin(u);
	pSphere.z = BaseRadius * cos(u);
	pSphere.w = 1.0;
	return pSphere;
}

///////////////////////////
// Apply 2D Perlin Noise //
///////////////////////////

uniform vec3 NoiseScale;	// Noise scale, 0.01 > 8
uniform float Sharpness;	// Displacement 'sharpness', 0.1 > 5
uniform float Displacement;	// Displcement amount, 0 > 2
uniform float Speed;		// Displacement rate, 0.01 > 1
uniform float Timer;		// Feed incrementing value, infinite

vec4 perlinSphere(in float u, in float v) {
	vec4 sPoint = sphere(u, v);
	// The rest of this function is mainly from vBomb shader from NVIDIA Shader Library
	vec4 noisePos = vec4(NoiseScale.xyz,1.0) * (sPoint + (Speed * Timer));
	float noise = (pnoise2D(noisePos.xy) + 1.0) * 0.5;;
	float ni = pow(abs(noise),Sharpness) - 0.25;
	vec4 nn = vec4(normalize(sPoint.xyz),0.0);
	return (sPoint - (nn * (ni-0.5) * Displacement));
}

////////////////////////////////
// Calculate Position, Normal //
////////////////////////////////

const float grid = 0.01;	// Grid offset for normal-estimation
varying vec3 norm;			// Normal

vec4 posNorm(in float u, in float v) {
	// Vertex position
	vec4 vPosition = perlinSphere(u, v);
	// Estimate normal by 'neighbour' technique
	// with thanks to tonfilm
	vec3 tangent = (perlinSphere(u + grid, v) - vPosition).xyz;
	vec3 bitangent = (perlinSphere(u, v + grid) - vPosition).xyz;
	norm = gl_NormalMatrix * normalize(cross(tangent, bitangent));
	// Return vertex position
	return vPosition;
}

//////////////////////////
// Phong Directional VS //
//////////////////////////

// -- Lighting varyings (to Fragment Shader)
varying vec3 lightDir0, halfVector0;
varying vec4 diffuse0, ambient;

void phongDir_VS() {
	// Extract values from gl light parameters
	// and set varyings for Fragment Shader
	lightDir0 = normalize(vec3(gl_LightSource[0].position));
	halfVector0 = normalize(gl_LightSource[0].halfVector.xyz);
	diffuse0 = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
	ambient =  gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
	ambient += gl_LightModel.ambient * gl_FrontMaterial.ambient;
}

///////////////
// Main Loop //
///////////////

uniform vec2 PreScale, PreTranslate;	// Mesh pre-transform

void main()
{
	vec2 uv = gl_Vertex.xy;
	// Offset XY mesh coords to 0 > 1 range
	uv += 0.5;
	
	// Pre-scale and transform mesh
	uv *= PreScale;
	uv += PreTranslate;
	
	// Calculate new vertex position and normal
	vec4 spherePos = posNorm(uv[0], uv[1]);
	
	// Calculate lighting varyings to be passed to fragment shader
	phongDir_VS();
	
	// Transform new vertex position by modelview and projection matrices
	gl_Position = gl_ModelViewProjectionMatrix * spherePos;
	
	// Forward current texture coordinates after applying texture matrix
	gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
}

And the Fragment Shader, which just implements a generic Phong Directional lighting model:

/*
	Generic Fragment Shader
	with Phong Directional lighting
*/

//////////////////////////
// Phong Directional FS //
//////////////////////////

// -- Lighting varyings (from Vertex Shader)
varying vec3 norm, lightDir0, halfVector0;
varying vec4 diffuse0, ambient;

vec4 phongDir_FS()
{
	vec3 halfV;
	float NdotL, NdotHV;
	
	// The ambient term will always be present
	vec4 color = ambient;
	
	// compute the dot product between normal and ldir
	NdotL = max(dot(norm, lightDir0),0.0);
	
	if (NdotL > 0.0) {
		color += diffuse0 * NdotL;
		halfV = normalize(halfVector0);
		NdotHV = max(dot(norm, halfV), 0.0);
		color +=	gl_FrontMaterial.specular * 
				gl_LightSource[0].specular * 
				pow(NdotHV, gl_FrontMaterial.shininess);
	}	
	return color;
}

///////////////
// Main Loop //
///////////////

void main()
{
	// Call lighting function and return result
	gl_FragColor = phongDir_FS();
}

Setup:

You’ll need to put a GLSL Grid patch inside the GLSL Shader macro. I’d set the Vertical and Horizontal Resolution of the Grid to a higher value than the default 50. Try to balance resolution (higher the better) against frame-rate. Setting it to 256×256 will give an ultra-smooth mesh, but will potentially slow down the rendering, depending on your system. You could put the GLSL Grid inside a Trackball if you wanted, so you can spin the whole thing around.

You’ll also need to put the whole thing inside a Lighting patch.

The complete setup would be:
Lighting : GLSL Shader : (optional) Trackball : GLSL Grid

I’ve tried to organise the code into blocks to make it easier to understand. I’m sure it could be made more efficient and/or elegant by combining some of the functions, but my aim was to make it easier to copy-paste discrete functions into other code, in nice self-contained chunks. It’s probably terrible coding practice, but I’ve placed all the variables for each function with the function definition itself, rather than declaring them all at the top. I found this helped keep the code ‘modular’, and it seems to work, so I guess the compiler can work its way through it OK.

I’ve suggested ranges for the various parameters in the shader code.

You’ll also need this picture, as the permutation texture, connected to the ‘permTex’ input on the shader.

Permutation Texture

And here’s a couple of examples of the code in action, courtesy of Marcos Prack.

Very smooth, and looks like it’s running faster than it does on my MacBook Pro.
Cheers Marcos!

About these ads

13 Responses to “2D Perlin Vertex Noise GLSL Sourcecode”


  1. 1 Rob
    June 24, 2009 at 1:56 pm

    Perhaps a very stupid question, but what do I do with the GLSL Shader once I’ve loaded all of your code in? I’ve tried placing a sphere or similar inside… outside… upsidedownside… also with a clear patch, but I’m getting no output… I think I’ve missed something!

  2. 2 Rob
    June 24, 2009 at 2:02 pm

    Oh, no – I got it. Just lots of playing with numbers. (Turns out it WAS a stupid question!)

  3. 3 toneburst
    June 24, 2009 at 2:07 pm

    Hi Rob,

    you need a GLSL Grid patch inside the GLSL Shader macro. You could also try sticking a Trackball patch inside the GLSL Shader patch, then the GLSL Grid inside that.

    You will need to play with the parameters a bit. I’ve put suggested value ranges for most of them in the code itself.

    Hope this helps,

    a|x

    PS
    I’ve update the post add more detail on the setup.

  4. 4 Rob
    June 24, 2009 at 2:14 pm

    Ahhhh kay. I pretty much got it working through trial and error. Perhaps I’ll start again with the GLSL Grid instead…

    Cheers!

    • 5 toneburst
      June 24, 2009 at 2:19 pm

      You’ll also need to put the whole thing inside a Lighting patch. Forgot to mention that, sorry…
      I guess I never expected anyone actually to use the code…. ;)

      a|x

  5. 6 toneburst
    June 24, 2009 at 2:31 pm

    Yeah.. it’s not going to work with anything else, because part of the code (the sphere() function) takes a flat mesh, and bends it around into a sphere, before the noise can do it’s work. Use anything other than a flat grid as the base geometry, and it’s going to come out weird.

    a|x

  6. June 25, 2009 at 12:57 pm

    hi A|x! thanks again for the code. I like it really to much. I have a one year old macbook pro, 2.5 Ghz, 17′ and 4 gigas RAM. The composition runs at 60 fps, sometimes a little less, but really fine.
    :-)-
    m

    • 8 toneburst
      June 25, 2009 at 1:13 pm

      Cool, glad it works well for you!
      I’ve just posted the code for the 3D version, thought you might be interested. This one might run a bit slower, but should still be reasonably fast on your machine.

      a|x

  7. June 25, 2009 at 1:38 pm

    hi again! works fine, a little slower, between 30 fps and 45 fps, with “somes” 60 or more fps. very useful stuff for vdmx i think. i’m very happy. ;-)-

  8. 10 tigerhoods
    July 16, 2009 at 6:25 am

    Sorry for my off-topic question. I am just wondering what’s the difference between fractal noise and perlin noise. I searched the web and could not find much information. Any suggestion is welcome. Thank you.

    Your blog is very cool :)

    • 11 toneburst
      July 16, 2009 at 8:42 am

      Thanks!

      I honestly don’t know what the difference between Fractal and Perlin noise is, I’m afraid. They may be just different words for the same thing, or Perlin Noise may be a specific implementation of Fractal Noise. The basic idea of Perlin Noise is that it looks ‘noisy’, but the same input will always produce the same output, so it’s controllable. It can also be easily scaled and moved, simply by scaling or adding an offset to the input value.

      Hope this helps,

      a|x

  9. 12 tigerhoods
    July 17, 2009 at 6:26 pm

    Thanks for your reply :)

  10. October 30, 2009 at 7:55 pm

    i just cant get anything from this.
    am i doing something wrong?
    i followed the steps and my viewer is still empty


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Twitter

Error: Twitter did not respond. Please wait a few minutes and refresh this page.

June 2009
M T W T F S S
« May   Jul »
1234567
891011121314
15161718192021
22232425262728
2930  

Links

Blog Stats

  • 398,282 hits

Follow

Get every new post delivered to your Inbox.

Join 35 other followers

%d bloggers like this: