XNA Shader Programming – Tutorial 26, Bump Mapping Perlin Noise

image

This tutorial will continue from Tutorial 25, where we implemented a noise function. Now that you have the Perlin Noise-function in your shader, it is very simple to implement Bump Mapping.

The object being rendered contains normals on each point of the surface. By calculating the gradient vector for each normal, you can modify the normal to point in a slightly different direction, based on the “bumps” the noise function creates, to create the illusion of a bumpy surface. The Normal must be modified so it point in the right direction based on the bumps, are we going “up” on the side of the bump? Or are we going “down”?

The algorithm

Say you have a function F(x,y,z) that produce a noise based pattern, as inoise() from tutorial 25. By slightly modifying the input of this function, you can calculate the gradient vector. We create a new variable E (EPSILON) that contains a small value like 0.001f. We then use E with the point P as input to the function F for each X, Y and Z:

image

Fx, Fy and Fz can now be used to calculate the gradient vector:

image

Now you got the gradient vector, and can calculate the new vector by subtracting this from the original Normal.

After that, you can implement the lighting model you want the normal way. In this tutorial, we simply use the diffuse lighting model from Tutorial 2.

image

Where N.L is the dotprocut between the Normal vector N and the light vector L.

image

The implementation

You can implement the whole bump mapping algorithm in the shader. We start by implementing the light algorithm in the vertex shader, shouldn’t be new to you:

struct VertexShaderInput
{
    float4 Position : POSITION0;
    float2 texCoord : TEXCOORD0;
    float3 Normal    : NORMAL;
};

struct VertexShaderOutput
{
    float4 Position : POSITION0;
    float2 texCoord : TEXCOORD0;
    float4 wPosition: TEXCOORD1;
    float3 Light    : TEXCOORD2;
    float3 Normal    : NORMAL;

};

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
    VertexShaderOutput output;

    float4 worldPosition = mul(input.Position, World);
    float4 viewPosition = mul(worldPosition, View);
    output.Position = mul(viewPosition, Projection);
    output.wPosition = mul(input.Position, World);
    output.texCoord = input.texCoord;
    output.Light = normalize(float3(0,0,1));
    output.Normal = normalize(mul(input.Normal, World));

    return output;
}

Next, we store the output from our noise function and use this as the color map:

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
    float3 p = input.wPosition;
    float inz = inoise(p)*0.5+0.5;
    float4 color = float4(inz,inz,inz,1);

Next, we create the variable E that will be used to calculate the gradient vector:

    float E = 0.001f;

Then we take the input point P and add E to each component, storing them in different variables:

float3 pX = input.wPosition;
pX.x += E;
float3 pY = input.wPosition;
pY.y += E;
float3 pZ = input.wPosition;
pZ.z += E;

Next, we create Fx,Fy and Fz by using the exact same function F used to produce the color, but instead of using point P, we use the E-modified point Px, Py and Pz:

float3 bump = float3( inoise(pX)*0.5+0.5,inoise(pY)*0.5+0.5,inoise(pZ)*0.5+0.5);
float3 modNormal = float3( (bump.x-inz) / E, (bump.y-inz) / E, (bump.z-inz) / E);

We then subtract the gradient vector from the input Normal, and normalize this:
float3 Normal = normalize(input.Normal – modNormal);

All that is left is to implement the lighting model we want (in this case, diffuse light), using the new modified Normal:

    float Ai = 0.3f;
    float4 Ac = float4(0.3, 0.0, 0.3, 1.0);
    float Di = 1.0f;
    float4 Dc = float4(1.0, 1.0, 1.0, 1.0);
    return Ai*Ac + color*saturate(dot(input.Light, Normal));
}

That is all there is to bump mapping your noise function. Smilefjes

 

Download: Source (XNA 4.0)

Reference: GPU Gems 1 – Improved Perlin Noise

This entry was posted in Graphics, Tutorial, XNA Shader Tutorial. Bookmark the permalink.

2 Responses to XNA Shader Programming – Tutorial 26, Bump Mapping Perlin Noise

  1. Pingback: Windows Client Developer Roundup 070 for 5/23/2011 - Pete Brown's 10rem.net

  2. Gouda says:

    Thanks for this tutorial series on XNA shaders. Yours are the best I’ve found on the web! These are fantastic, thanks for sharing.

    I have a question for you: Any chance you’d be willing to take a stab at a CRT emulation pixel shader? These guys have done some amazing work on CRT shaders, but they’re all in GLSL: http://filthypants.blogspot.com/2011/05/more-emulator-pixel-shaders-crt-updated.html

    It seems like the “CRT.OpenGL” version is the best one, but they all look pretty amazing. The shader source is freely available, but I’m having trouble porting it to XNA/HLSL. I found a guy who ported it to Direct3D/HLSL, but it doesn’t work in XNA: http://www.si-gamer.net/gulikoza/

    I would love to see a tutorial on making an effect like that in XNA if you’re up to it! Anyway, keep up the great work, love your articles.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.