Simple GLSL Bump-Mapping / Normal-Mapping

I had an hour to kill earlier on so thought I’d take a shot at some bump-mapping/normal-mapping (whatever you want to call it) – and it’s actually not too hard. Well, it’s probably kinda hard to do properly, but it’s pretty easy to get something that works without going into TBN (Tangent/Binormal/Normal) space, like this…

[nggallery id=5]

For this simple way of doing things there are two important steps:

  • Generate normal-map versions of your texture(s)
  • Perturb your per-pixel geometry normals by a value derived from sampling your normal map.

You can use lots of different tools to generate normal maps, I wanted something quick and free, so I used the GIMP, specifically the GIMP Normal-Map Plugin. Just load the texture image, then select Filters | Map | Normalmap… from the menu and and save that bad boy for later use. I upped the scale of the normal-map generation to 10.000 from it’s original 1.000 just to magnify the effect, you can do the same, or you can magnify it in the fragment shader, but it’s probably going to run quicker if you encode the normal-map with the magnitude of normal perturbation you’re going to use in your final work.

Hint: If you’re on Ubuntu, just install the package gimp-plugin-registry – it comes with a stack of neat & useful plugins, including Normal Map. I tried to install it separately and it conflicted with the plugin-registry package, which would be because it’s already a part of it and I had all the tools I needed already!

Original and Normal-Map Textures
Original Texture on the left, Normal-Map version on the right

Notice how the normal-mapped version of the texture on the right is mainly blue? This is because the data being stored in it isn’t really “colour” anymore – instead of thinking of each value as a RGB triplet, think of it as an XYZ vector. If we look at the data that way, what we’re really seeing when we see “blue” is nothing on the X and Y axis’, and positive on the Z axis. Sneaky, huh?

Now that you’ve got your original texture and the normal-mapped version of it, texture your geometry as usual but add an additional Sampler2D uniform in your fragment shader for your normal-map texture. Bind to another texture unit before loading and glTexImage2D-ing it so you have the texture on one texture image unit and your normal map on another (so you can sample from each independently).

Once you’ve got that all sorted, use shaders along the lines of these:

Vertex Shader

#version 330
 
// Incoming per-vertex attribute values
in vec4 vVertex;
in vec3 vNormal;
in vec4 vTexture;
 
uniform mat4 mvpMatrix;
uniform mat4 mvMatrix;
uniform mat3 normalMatrix;
uniform vec3 vLightPosition;
 
// Outgoing normal and light direction to fragment shader
smooth out vec3 vVaryingNormal;
smooth out vec3 vVaryingLightDir;
smooth out vec2 vTexCoords;
 
void main(void) 
{
 
    // Get surface normal in eye coordinates and pass them through to the fragment shader
    vVaryingNormal = normalMatrix * vNormal;
 
    // Get vertex position in eye coordinates
    vec4 vPosition4 = mvMatrix * vVertex;
    vec3 vPosition3 = vPosition4.xyz / vPosition4.w;
 
    // Get vector to light source
    vVaryingLightDir = normalize(vLightPosition - vPosition3);
 
    // Pass the texture coordinates through the vertex shader so they get smoothly interpolated
    vTexCoords = vTexture.st;
 
    // Transform the geometry through the modelview-projection matrix
    gl_Position = mvpMatrix * vVertex;
}

Fragment Shader

#version 330
 
// Uniforms
uniform vec4 ambientColour;
uniform vec4 diffuseColour;
uniform vec4 specularColour;
uniform sampler2D colourMap; // This is the original texture
uniform sampler2D normalMap; // This is the normal-mapped version of our texture
 
// Input from our vertex shader
smooth in vec3 vVaryingNormal;
smooth in vec3 vVaryingLightDir;
smooth in vec2 vTexCoords;
 
// Output fragments
out vec4 vFragColour;
 
void main(void)
{ 
	const float maxVariance = 2.0; // Mess around with this value to increase/decrease normal perturbation
	const float minVariance = maxVariance / 2.0;
 
	// Create a normal which is our standard normal + the normal map perturbation (which is going to be either positive or negative)
	vec3 normalAdjusted = vVaryingNormal + normalize(texture2D(normalMap, vTexCoords.st).rgb * maxVariance - minVariance);
 
	// Calculate diffuse intensity
	float diffuseIntensity = max(0.0, dot(normalize(normalAdjusted), normalize(vVaryingLightDir)));
 
	// Add the diffuse contribution blended with the standard texture lookup and add in the ambient light on top
	vec3 colour = (diffuseIntensity * diffuseColour.rgb) * texture2D(colourMap, vTexCoords.st).rgb + ambientColour.rgb;
 
	// Set the almost final output color as a vec4 - only specular to go!
	vFragColour = vec4(colour, 1.0);
 
	// Calc and apply specular contribution
	vec3 vReflection        = normalize(reflect(-normalize(normalAdjusted), normalize(vVaryingLightDir)));
	float specularIntensity = max(0.0, dot(normalize(normalAdjusted), vReflection));
 
	// If the diffuse light intensity is over a given value, then add the specular component
	// Only calc the pow function when the diffuseIntensity is high (adding specular for high diffuse intensities only runs faster)
	// Put this as 0 for accuracy, and something high like 0.98 for speed
	if (diffuseIntensity > 0.98)
	{
		float fSpec = pow(specularIntensity, 64.0);
		vFragColour.rgb += vec3(fSpec * specularColour.rgb);
	}
}

Source code after the jump. Cheers!

Continue reading Simple GLSL Bump-Mapping / Normal-Mapping

Anaglyphic 3D in GLSL

I’ve been playing around with getting some red/cyan stereoscopic 3D working of late, and it’s all turned out rather well – take a look… (Red/Blue or Red/Cyan glasses are required for the effect to work):

Anaglyphic 3D in GLSL
Click for bigger version.

If you’ve got suitable glasses you should definitely see a 3D effect, although I don’t think me rescaling the image has done it any favours – so click the image to see the full sized version for the full effect.

The trick to this has been to render the scene twice to two separate FBO textures, then sample from the left and right textures to draw a fullscreen quad with a combined version as follows:

// Fragment shader to perform Analyphic 3D conversion of two textures from the left and right eyes
#version 330
 
uniform sampler2D leftEyeTexture;
uniform sampler2D rightEyeTexture;
 
in vec2 vTexCoord;
 
out vec4 vFragColour;
 
void main(void)
{
	vec4 leftFrag = texture(leftEyeTexture, vTexCoord);
	leftFrag = vec4(1.0, leftFrag.g, leftFrag.b, 1.0); // Left eye is full red and actual green and blue
 
	vec4 rightFrag = texture(rightEyeTexture, vTexCoord);
	rightFrag = vec4(rightFrag.r, 1.0, 1.0, 1.0); // Right eye is full green and blue and actual red
 
	// Multiply left and right components for final ourput colour
	vFragColour = vec4(leftFrag.rgb * rightFrag.rgb, 1.0); 
}

In the code itself, the torus’ spin around on the spot and look pretty good, although there’s no anti-aliasing as yet as I need to create some multisample buffers instead of straight/normal buffers for the FBO, but it’s not decided to play ball just yet – not to worry though, the hard part’s done and I’m sure multisampling will be sorted in a day or so. After that, I might give ColorCode3D (TM)(R)(C)(Blah) a go, as it seems to give a better colour representation whilst still allowing the same amount of depth as traditional anaglyphic techniques. Also, I’ve got to start using asymmetric frustums for the projection to minimise the likelihood of eye-strain, but I don’t see that as being too much of a problem.

Good times! =D

How To: Syntax highlight GLSL shaders in Gedit

GLSL shaders share a lot in common with the C language, and Gedit can syntax highlight C/C++ and a host of other languages – so why not GLSL shaders? Here’s a simple way to make it happen…

1.) Gedit uses gtksourceview for its syntax highlighting rules – so find out where that’s located on your distro with the following:

locate gtksourceview | grep /c.lang

Once you’ve got the location of the c.lang file, navigate there in the bash (on Ubuntu it’s in /usr/share/gtksourceview-2.0/language-specs).

2.) Make a copy of the c.lang file in case you accidentally stuff it up (optional, but better safe than sorry):

sudo cp c.lang c.lang.ORIG

3.) Open the file with your text editor of choice and modify the c.lang file to add in additional file extensions which should be syntax highlighted as per the c.lang definitions:

gksudo gedit ./c.lang

A couple of lines into the file (after the comments at the top) you’ll see the following:

<property name="globs">*.c;</property>

Assuming you’re ending your vertex shaders with .vp and your fragment shaders with .fp (if you’re using .vert and .frag or such just substitute appropriately), change the line to read:

<property name="globs">*.c;*.vp;*.fp</property>

Save it, close gedit, and open a .vp or .fp file with Gedit – syntax highlighty goodness is rightfully yours. Of course, this is normal C highlighting, not true GLSL highlighting – but it’s a good start.

If you wanted to add things like vec3, uniform etc. then you can find the following sections in the c.lang file and add ’em in yourself:

<context id="keywords" style-ref="keyword" class="keyword">
<context id="types" style-ref="type" class="type">

Cheers!

C++/OpenGL/GLSL Texture Manipulation

Just learning some GLSL and playing about with vertex and fragment shaders – not a bad first start, but it’s going to take a significant amount of time and effort to get to really writing some descent shaders – it’s just very, very different from fixed-pipeline stuff… What you’re looking at is a texture where I’ve drawn some stuff on it in bright red, and then in the fragment shader anything found in the texture to be bright red gets discarded, effectively leaving in empty, like a cut-out =D

All in, the project’s around 500 lines of source, with the shaders being merely:

Vertex Shader

#version 110 // Olde...
 
varying vec2 vTexCoord; // varying is now deprecated.. will re-write soon
 
void main(void)
{
   vTexCoord = gl_MultiTexCoord0; 
   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; // 3D vertex -> 2D window position transformation
}

Fragment Shader

sampler2D myTexture;    // Apparently this should not be a uniform variable as you shouldn't loop through uniform variables for performance reasons...
varying vec2 vTexCoord; // Pass through from our vertex shader. Should be type "in" in more recent versions of GLSL
 
void main (void) 
{  
   vec4 color = texture2D(myTexture, vTexCoord); 
 
   if (color.r > 0.9 && (color.g < 0.15 && color.b < 0.15))
      discard; 
 
   gl_FragColor = color;
}

The Linux Code::Blocks project will all source & image can be found below, but you’ll need to have or install libGL, libGLEW, libglfw, libIL, libILU, libILUT for it to work right out of the box. The linkage details are all in the Code::Blocks project file (and also in the .depend file if you don’t use C::B) so you can see what you need if there’s anything missing.

Download: GLSL-Texture-Manipulation-Test.zip

Update: Ha! Hadn’t thought of this before, but I guess what I’m really doing is Chroma-Keying (or Colour-Keying, whatever you prefer)… And you can do the same thing in SDL/OpenGL without using any shaders like this.