Bump Mapping

Started by
5 comments, last by Suen 12 years, 1 month ago
I'm trying to implement bumpmapping through a heightmap but I seem to have a problem with calculating the normal (most likely) as I get nothing but a black screen.

After doing some research on the net I've merely come to understand that if I use a heightmap for doing a bumpmap then I would basically calculate the surface normal on the fly in the fragment shader and use it in my lighting computation instead of my real normal. What I need to do is find two tangent vectors in the current sampled position of my heightmap. These can be found by taking the partial derivatives and those derivatives can be approximated by a finite difference method so I decided to go on with the central difference scheme. Once this is done I go on to take the normalized cross product of these two vectors to get my normal, and then use this normal in my lighting computation

I also created a grayscale image (well a heightmap of course) through photoshop as well.

Unfortunately I seem to be doing something wrong when calculating the normal to be used. I tried feeding in the real normal and it works fine, I just get a regular diffuse lighting then. I also made sure that there's nothing wrong with my texture by mapping it to my sphere and it worked fine as well. So...I'm not entirely sure what I'm doing wrong really.

I'm slightly limited to what I can do at the moment on the CPU side of things as I am being dependant on a custom toolkit for OpenGL to do quite a bit of operations but I don't really think it would prevent me from doing a bump map anyway so there shouldn't be a problem because of that as far as I know.

Update: Fixed some small errors in the fragment shader which I just noticed, have posted the new code. I do get a visual result now but nothing close to resembling bump mapping. From the result it seems like I'm only adding the ambient color to my sphere as the whole surface consist of the same color. It's as if the diffuse part is never even considered. I'm slightly confused about what component I exactly need from my vec4 variables (x_forw etc.) when calculating the tangent vectors later in the code.

Here's the vertex and fragment shaders:

Vertex shader


#version 330

in vec4 vVertex;
in vec3 vNormal;
in vec2 vTexture0;

uniform mat4 mvMatrix;
uniform mat4 mvpMatrix;
uniform mat3 normalMatrix;
uniform vec3 lightPos;

out vec3 eyeNormal;
out vec3 lightDir;
out vec3 viewer;
out vec2 varTexture0;

void main()
{
eyeNormal = normalMatrix*vNormal;
vec4 eyeVert4 = mvMatrix*vVertex;
vec3 eyeVert3 = eyeVert4.xyz/eyeVert4.w;
lightDir = normalize(lightPos-eyeVert3);
viewer = normalize(-eyeVert3);
varTexture0 = vTexture0;
gl_Position = mvpMatrix*vVertex;
}


Fragment Shader


#version 330

//Uniforms
uniform vec4 diffuseColor;
uniform vec4 ambientColor;
uniform vec4 specularColor;
uniform sampler2D heightMap;

in vec3 eyeNormal;
in vec3 lightDir;
in vec3 viewer;
in vec2 varTexture0;

out vec4 fragColor; //Output for fragment shader

void main()
{
//Central Difference Scheme
vec4 x_forw = texture2D(heightMap, varTexture0.xy+vec2(1.0, 0.0));
vec4 x_back = texture2D(heightMap, varTexture0.xy-vec2(1.0, 0.0));
vec4 y_forw = texture2D(heightMap, varTexture0.xy+vec2(0.0, 1.0));
vec4 y_back = texture2D(heightMap, varTexture0.xy-vec2(0.0, 1.0));
vec3 tangX = vec3(1.0, 0.0, x_forw.z-x_back.z);
vec3 tangY = vec3(0.0, 1.0, y_forw.z-y_back.z);

//Calculate surface normal
vec3 heightNormal = normalize(cross(tangX, tangY));

fragColor = ambientColor +diffuseColor*max(0.0, dot(lightDir, heightNormal));
fragColor.a = 1.0;
}
Advertisement
Hi,

when sampling your bump texture, you might want to add/substract "1.0 / textureWidth" and "1.0 / textureHeight", instead of just "1.0" :)
(texture coordinates go from 0 to 1, so when you add 1 to your current coordinate, you'll sample a pixel "outside" your texture)

regards.

Hi,

when sampling your bump texture, you might want to add/substract "1.0 / textureWidth" and "1.0 / textureHeight", instead of just "1.0" smile.png
(texture coordinates go from 0 to 1, so when you add 1 to your current coordinate, you'll sample a pixel "outside" your texture)

regards.


Hello! Thanks for your reply. I tried this yesterday after reading a couple of posts on the net with regards to finite differences. So basically I wrote this yesterday:


vec4 x_forw = texture2D(heightMap, varTexture0.xy+vec2(1.0/512.0, 0.0));
vec4 x_back = texture2D(heightMap, varTexture0.xy-vec2(1.0/512.0, 0.0));
vec4 y_forw = texture2D(heightMap, varTexture0.xy+vec2(0.0, 1.0/512.0));
vec4 y_back = texture2D(heightMap, varTexture0.xy-vec2(0.0, 1.0/512.0));


As you said the texture coordinates range between 0 to 1 so I also thought that dividing by the width and height made sense but unfortunately this gives me the exact same result (a sphere with the same color over the whole surface).

Also as an additional question to your post, shouldn't the sample which goes outside the 0-1 range be merely sampled according to wrap mode I've set? Why would it then be necessary to divide the samples by the corresponding width and height?

Also thanks for the help! (though my problem remains wink.png)
I finally got a result but I would like some opinions on it. To me I consider this to be bumpmapping of some form. Yes it's far from being a very nice-looking bump mapping but it looks like one nonetheless. What do you guys think?

Also I'm still wondering about my question in the previous post regarding wrap modes and dividing the the texture coord. by the width and height.

[s]Also is there any way of further enhancing the effect of this? I might be wrong about this but even though I do visually get the feeling that the surface is being "bumped" it still feels somewhat "flat"[/s]

Update: Finally managed to enhance the effect a bit by scaling the partial derivatives that I approximated in my tangent vectors.

Now, aside from the question above I have another question. I thought of adding a specular highlight to the object by calculating the reflected vector from the surface and then using it in my calculation in a traditional Phong reflection model. At first I did this calculation by using the final normal I got from combining the true normal and the normal from the heightmap but this gave me no specular highlights at all. Instead I decided to only use the surface normal from the height map when calculating the reflected vector and then using this in my lighting calculations and it gave me this result. I'm not entirely sure if this is the way it's supposed to look like or if the method I'm using is entirely wrong in itself for calculating specular highlights on a bump mapped surface.

Here's the result of that:
Here's the code for those who wonder. Aside from some few minor faults I found in my program I changed the fragment shader so that the offsett was divided by the corresponding width and height of the texture as suggested by intyuh (thanks lots!!)

And again I'm still wondering about my two questions in the previous post so help on those would be appreciated smile.png

Thanks!

Fragment shader


#version 330

//Uniforms
uniform vec4 diffuseColor;
uniform vec4 ambientColor;
uniform vec4 specularColor;
uniform sampler2D heightMap;

in vec3 eyeNormal;
in vec3 lightDir;
in vec3 viewer;
in vec2 varTexture0;

out vec4 fragColor;

void main()
{
vec4 x_forw = texture2D(heightMap, varTexture0.xy+vec2(1.0/512.0, 0.0));
vec4 x_back = texture2D(heightMap, varTexture0.xy-vec2(1.0/512.0, 0.0));
vec4 y_forw = texture2D(heightMap, varTexture0.xy+vec2(0.0, 1.0/512.0));
vec4 y_back = texture2D(heightMap, varTexture0.xy-vec2(0.0, 1.0/512.0));
vec3 tangX = vec3(1.0, 0.0, 3.0*(x_forw.x-x_back.x));
vec3 tangY = vec3(0.0, 1.0, 3.0*(y_forw.x-y_back.x));

vec3 heightNormal = normalize(cross(tangX, tangY));
vec3 finalNormal = eyeNormal*heightNormal;

vec3 refDir = normalize(reflect(-lightDir, heightNormal));

fragColor = ambientColor +diffuseColor*max(0.0, dot(lightDir, finalNormal));
fragColor = fragColor+specularColor*pow(max(0.0, dot(refDir, viewer)), 50);
fragColor.a = 1.0;
}
Hi,

If texture sampling is set to repeat, for instance, sampling (x, y) and (x + 1, y) will return the exact same value, whatever x and y. If texture sampling is set to clamp, you'll always get the border values, etc.
When re-constructing the normals, you need to sample a pixel's direct adjacent pixels. And the pixels' coordinates are separated by 1/texture(Width/Height)
I don't know if I'm being clear, but I don't really know how to explain it better in english and without drawing stuff :)

When you consider the pixel at varTexture0, when you add 1/512, if your texture is 512, then you will sample the neighbour pixel. Try to draw a sample 16x16 pixel texture on a piece of paper, with the upper left pixel being (0, 0) and the bottom right (1, 1), then see what happens when you increase a texture coordinate by 1/16 as opposed to increasing by 1.

Regards.

Hi,

If texture sampling is set to repeat, for instance, sampling (x, y) and (x + 1, y) will return the exact same value, whatever x and y. If texture sampling is set to clamp, you'll always get the border values, etc.
When re-constructing the normals, you need to sample a pixel's direct adjacent pixels. And the pixels' coordinates are separated by 1/texture(Width/Height)
I don't know if I'm being clear, but I don't really know how to explain it better in english and without drawing stuff smile.png

When you consider the pixel at varTexture0, when you add 1/512, if your texture is 512, then you will sample the neighbour pixel. Try to draw a sample 16x16 pixel texture on a piece of paper, with the upper left pixel being (0, 0) and the bottom right (1, 1), then see what happens when you increase a texture coordinate by 1/16 as opposed to increasing by 1.

Regards.


intyuh, it does make perfect sense. I guess I was confusing myself about the fact that 1/texture(Width/Height) merely meant the neighboring pixel. You're exactly right about the fact that if I had set it to repeat then I would never really sample the neighboring pixel but the exact same pixel all the time. Clamp/clamp to edge would give me wrong result for obvious reasons though. Thanks for explaining regardless, it made things clearer.

If I may ask, what is your opinion on the specular highlights on the surface. Does it look correct for a basic phong reflection model?

This topic is closed to new replies.

Advertisement