Jump to content
  • Advertisement
Sign in to follow this  
gagabla

Problems with GLSL Bumpmapping Shader (with Lumina example)

This topic is 3406 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, i am trying to write an GLSL Bumpmapping shader. To try it out, i use Lumina. I know there is an exmaple coming with Lumina, but i would like to have one, that i understand. The way, the code looks right for me, when i run it, i see my object with applied bumpmapping, but when the object is rotated in space, the bumpmapping does not update. I think i somehow lose the orientation of the object in my vertex shader. (=> See screenshots) My approach is as found often in tutorials: Vertex Shader: 1) calculate the TBN-matrix for the current vertex 2) transform the light direction into the tangential space with the TBN-matrix 3) pass this light direction to the fragment shader Fragment shader: 4) calculate the dot product of the light direction and the current normalmaps normal 5) use this value to lighten the pixel My code is the following: Vertex shader:
attribute vec3 tangent;
vec3 lightDirGlobal = vec3(1.0, 0.0, 0.0);
varying vec3 lightDirTangential;

void main(void){
	gl_Position = ftransform();
	gl_TexCoord[0] = gl_MultiTexCoord0;

	vec3 N = gl_Normal;
	vec3 T = tangent;
	vec3 B = cross(T, N);

	mat3 TBNMatrix = mat3(T, B, N);

	lightDirTangential = TBNMatrix * lightDirGlobal;
}



Fragment shader:
uniform sampler2D texture;
uniform sampler2D normalmap;

varying vec3 lightDirTangential;

void main(void){
	vec4 texture = texture2D(texture, vec2(gl_TexCoord[0]));
	vec3 bumpnormal = texture2D(normalmap, vec2(gl_TexCoord[0])).xyz;

	float strength = max(dot(bumpnormal, lightDirTangential), 0.0);
	
	gl_FragColor = vec4(strength * texture.rgb, 1.0);
}

Has anybody an idea, what could be the problem? Why is the orientation of my object lost in transformation? Get the Lumina file and my two test textures BR Janosch EDIT: please ignore my bad english :)

Share this post


Link to post
Share on other sites
Advertisement
What's happening here:

mat3 TBNMatrix = mat3(T, B, N);

lightDirTangential = TBNMatrix * lightDirGlobal;




is:

|1.0|
[T,B,N]*|0.0|=T
|0.0|




so you are always sending the -untransformed- tangent vector to your fragment shader!

Share this post


Link to post
Share on other sites
Quote:

My approach is as found often in tutorials:
Vertex Shader:
1) calculate the TBN-matrix for the current vertex
2) transform the light direction into the tangential space with the TBN-matrix
3) pass this light direction to the fragment shader
Fragment shader:
4) calculate the dot product of the light direction and the current normalmaps normal
5) use this value to lighten the pixel


1) because you transformed the vertex into the camera's coordinates system, you have to do the same with the TBN matrix.(assuming the modelview matrix is a combination of only rotations and translations you can use gl_NormalMatrix).
2)the TBN matrix transforms a vector from tangent space. So you actually need it's inverse [sad]. This is why it's better to send the TBN matrix as varying to the fragment shader in order to use it to transform the normal retrived from the normal-map.(EDIT: Well, because the TBNMatrix is orthogonal, it's inverse is equal to it's transpose [embarrass])

So here is how it should be done (code not tested)
Vertex shader:

attribute vec3 tangent;
varying mat3 TBNMatrix;

void main(void){
gl_Position = ftransform();
gl_TexCoord[0] = gl_MultiTexCoord0;

vec3 N = gl_Normal;
vec3 T = tangent;
vec3 B = cross(T, N);

TBNMatrix = gl_NormalMatrix * mat3(T, B, N);//Now TBNMAtrix is expressed in camera coordinates system
/*
A more correct (I think) but slower Alternative is:
vec3 N = normalize(gl_NormalMatrix * gl_Normal);
vec3 T = normalize(mat3(gl_ModelViewMatrix) * tangent);
vec3 B = cross(T, N);
TBNMatrix = mat3(T, B, N);
*/

}



Fragment shader:

uniform sampler2D texture;
uniform sampler2D normalmap;

varying mat3 TBNMatrix;

void main(void){
vec4 texture = texture2D(texture, vec2(gl_TexCoord[0]));
vec3 bumpnormal = 2.0*texture2D(normalmap, vec2(gl_TexCoord[0])).xyz - vec3(1.0);
bumpnormal=TBNMatrix*bumpnormal;//here bumpnormal is transformed from tangent to camera coordinates system
vec3 lightDirGlobal = vec3(1.0, 0.0, 0.0);//It's fixed with respect to the camera
float strength = max(dot(bumpnormal, lightDirGlobal), 0.0);

gl_FragColor = vec4(strength * texture.rgb, 1.0);
}




EDIT: If you still want to send the light direction to the fragment shader (an use less varyings) because the TBNMatrix is orthogonal.
Vertex shader:

attribute vec3 tangent;
vec3 lightDirGlobal = vec3(1.0, 0.0, 0.0);//Fixed w.r.t the camera
varying vec3 lightDirTangential;

void main(void){
gl_Position = ftransform();
gl_TexCoord[0] = gl_MultiTexCoord0;

vec3 N = gl_Normal;
vec3 T = tangent;
vec3 B = cross(T, N);

TBNMatrix = gl_NormalMatrix * mat3(T, B, N);//Now TBNMAtrix is expressed in camera coordinates system
/*
A more correct (I think) but slower Alternative is:
vec3 N = normalize(gl_NormalMatrix * gl_Normal);
vec3 T = normalize(mat3(gl_ModelViewMatrix) * tangent);
vec3 B = cross(T, N);
TBNMatrix = mat3(T, B, N);
*/

lightDirTangential = lightDirGlobal * TBNMatrix;//equivalent to: transpose(TBNMatrix) * lightDirGlobal;
}



Fragment shader:

uniform sampler2D texture;
uniform sampler2D normalmap;

varying vec3 lightDirTangential;

void main(void){
vec4 texture = texture2D(texture, vec2(gl_TexCoord[0]));
vec3 bumpnormal = 2.0*texture2D(normalmap, vec2(gl_TexCoord[0])).xyz-vec3(1.0);
float strength = max(dot(bumpnormal, lightDirTangential), 0.0);

gl_FragColor = vec4(strength * texture.rgb, 1.0);
}




[Edited by - knighty on April 18, 2009 10:10:50 AM]

Share this post


Link to post
Share on other sites
Wow, thanks a lot, knighty!
I tried your last version (since i think it will be the fastest) and it works! And more important, thanks to your explanations, i think i understand it :)

Share this post


Link to post
Share on other sites
Oh, one more question:
Quote:

1) because you transformed the vertex into the camera's coordinates system, you have to do the same with the TBN matrix.

I didn't transform the vertex or the tangent into the camera's coordinate system consciously. In my application, vertices and tangents are calculated in the "world" coordinate system, so where are they transformed? Is this done automatically by OpenGL?

Share this post


Link to post
Share on other sites
Yes, you are right. It's faster. Finally, it's just a modification of your initial code: transformation of normal and tangent vectors to camera space, reversing the multiplication of lightDirGlobal by TBNMatrix and normalizing the normal map's texel. It's all about doing things in the right coordinates system [smile].

Share this post


Link to post
Share on other sites
Quote:
Original post by gagabla
Oh, one more question:
Quote:

1) because you transformed the vertex into the camera's coordinates system, you have to do the same with the TBN matrix.

I didn't transform the vertex or the tangent into the camera's coordinate system consciously. In my application, vertices and tangents are calculated in the "world" coordinate system, so where are they transformed? Is this done automatically by OpenGL?


gl_Position = ftransform();
is "equivalent" to:
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

Edit:{
When you do:
vec3 N = gl_NormalMatrix * gl_Normal;
You are asking OpenGL to transform the normal from the Model coordinates system to camera coordinates system. gl_NormalMatrix is the inverse transpose of the "vectorial" part of the gl_ModelViewMatrix.
}

In OpenGL there is no distinction between model,world and view space. They are packed together. It's up to the programmer to decide where the camera transformation stops and when the model transformation begins. Confusing! But with a little effort (and many exercices[wink]) everey thing will be clear.

It would take too long to explain here. It is explained better than I could ever do in the red book especially the 3rd chapter.

[Edited by - knighty on April 18, 2009 11:02:23 AM]

Share this post


Link to post
Share on other sites
But gl_Vertex and gl_Normal are always in the global coordinate system, aren't they?


I will have a look at the red book, now i am too confused to discuss about it :D

EDIT:
Looking at the following two lines of the vertex shader:

TBNMatrix = gl_NormalMatrix * mat3(T, B, N);//Now TBNMAtrix is expressed in camera coordinates system
lightDirTangential = lightDirGlobal * TBNMatrix;//equivalent to: transpose(TBNMatrix) * lightDirGlobal;


What transformation does TBNMatrix (as is) stand for?
From camera space, to tangent space?
Then, what does transpose(TBNMatrix) * XXX do?
From Tangent space to camera space?

Share this post


Link to post
Share on other sites
Quote:
Original post by gagabla
But gl_Vertex and gl_Normal are always in the global coordinate system, aren't they?

I will have a look at the red book, now i am too confused to discuss about it :D

LOL!(Edit: about you being confused... Don't worry! coordinates system and transformations are perhaps the most confusing stuff in comuter graphics but then you will see that it's not that hard!)

[Edited by - knighty on April 18, 2009 12:38:20 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by gagabla
But gl_Vertex and gl_Normal are always in the global coordinate system, aren't they?


I will have a look at the red book, now i am too confused to discuss about it :D

EDIT:
Looking at the following two lines of the vertex shader:
*** Source Snippet Removed ***
What transformation does TBNMatrix (as is) stand for?
From camera space, to tangent space?
Then, what does transpose(TBNMatrix) * XXX do?
From Tangent space to camera space?


What did we do!
1) we had the normal and a tangent vector in the model coordinates system (C.S.).
2) we calculated the binormal in the model C.S. also.
3) we constructed the TBN Matrix out of tangent, binormal and normal vectors.
4) we transformed the TBN Matrix to the camera C.S. Now the 3 columns of TBNMatrix are actually the tagent, binormal and normal vector expressed in the camera C.S.
Now remember that TBNMatrix is orthogonal, that is if you multiply it by its transpose you will get the identity! so we don't have to do weired math to get it's inverse: it's simply it's transpose!
4) we transformed the light direction from the camera C.S. to tangent space. How? We did:
lightDirTangential = lightDirGlobal * TBNMatrix;
That is equivalent to:
lightDirTangential = transpose(TBNMatrix) lightDirGlobal;
That is equivalent to:
lightDirTangential = Inverse(TBNMatrix) lightDirGlobal;//Inverse doesn't exist in GLSL v1.2 and I don't know if it does in more recent versions.

In GLSL, doing:
vector * matrix
is equivalent to:
transpose(matrix)*vector
But the former is faster!

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!