Jump to content
  • Advertisement
Sign in to follow this  
Chozo

Trouble with normal maps

This topic is 5045 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

I've run through this so many times I think I've created a block for myself. Right now I have this vertex shader (GLSL):
varying vec3 V, N;

void main(void)
{
    // set the vertex position
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

    // setup lighting
    V = gl_ModelViewMatrix * gl_Vertex;
    N = gl_NormalMatrix * gl_Normal;

    // setup texturing
    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_TexCoord[1] = gl_MultiTexCoord1;
}

and this fragment shader:
uniform sampler2D detail, normalmap;

varying vec3 V, N;

void main(void)
{
    vec3 normal = normalize(2.0 * texture2D(normalmap, gl_TexCoord[1]).xyz - 1.0);
    vec3 L = normalize(gl_LightSource[0].position.xyz - V);
    vec3 E = normalize(-V);

    float diffuse = dot(normal, L);
    vec3 R = normalize(2.0 * diffuse * normal - L);

    // ambient
    vec4 Iamb = gl_FrontLightProduct[0].ambient;

    // diffuse
    vec4 Idiff = gl_FrontLightProduct[0].diffuse * diffuse;

    // specular
    float specular = pow(max(dot(R, E), 0.0), gl_FrontMaterial.shininess);
    vec4 Ispec = gl_FrontLightProduct[0].specular * specular;

    // final color
    vec4 tv = texture2D(detail, gl_TexCoord[0]);
    gl_FragColor = tv * (gl_FrontLightModelProduct.sceneColor + Iamb + Idiff + Ispec);
}

But I'm getting an odd clipping effect of some kind. I think it's actually just light being applied in odd places, but it basically looks like if you had binoculars and moved them down and to the right, so you could only see the bottom left of what you're looking at, through a semi-circle. Hard to describe, I know, but that's basically it. I can grab a screenshot if that would help. I guess I'm just completely lost on how to use GLSL to make use of Doom3 style normal maps. I figured it'd just be a matter of doing a lookup in the texture to grab the normal, but that doesn't seem to be working. Thanks in advance for any help.

Share this post


Link to post
Share on other sites
Advertisement
for normalmapping youre not supplying enuf info, youve only got the normals there, u also need to supply per vertex the tangents + bitangents as well

Share this post


Link to post
Share on other sites
A screenshot would help.
Like Zedzeek said, you need to specify tangents and binormals per vertex. For my program I found that specifying both a tangent and binormal as a vertex attribute caused errors. I'm not sure if it was the naming or space issues, but the binormal data overwrote the normal data. So I simply pass the tangent as a vertex attribute then calculate the binormal in the vertex shader.

The other thing to do is make sure that all your tangents and binormals are consistent, ie., don't have one tangent pointing left, then on the next face it points right or something.

Oh, one more thing. It's minor, but casting from a vec4 to a vec3 is bad in a shader program. In fact, the compiler should be giving you warnings about that. So, try this just to be more correct:


varying vec3 V;
varying vec3 N;
varying vec3 T;//tangent
varying vec3 B;//binormal
attribute vec3 tangent;

void main(void)
{
// set the vertex position
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

// setup lighting
V = vec3(gl_ModelViewMatrix * gl_Vertex);
N = vec3(gl_NormalMatrix * gl_Normal);
T = tangent;
B = cross(T,N);

// setup texturing
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[1] = gl_MultiTexCoord1;
}


Share this post


Link to post
Share on other sites
I'm looking at the simple bumpmap tutorial:

Quote:

float deltaT0=v0.texCoords.y-v1.texCoords.y;
float deltaT1=v2.texCoords.y-v1.texCoords.y;
sTangent=deltaT1*side0-deltaT0*side1;
sTangent.Normalize();

//Calculate t tangent
float deltaS0=v0.texCoords.x-v1.texCoords.x;
float deltaS1=v2.texCoords.x-v1.texCoords.x;
tTangent=deltaS1*side0-deltaS0*side1;
tTangent.Normalize();


Is it correct to say that v0.texCoords.y is the same as my u texture coordinate? Or is equivalent to the v. I'm seeing how to calculate the tangent/bitangent (I already calculate the normal, so I can throw that in with it). Is there a way that I can pass the per-vertex data into a GLSL program? I'm using VBOs so I'm not sure how to do the vertex attributes (haven't seen a tutorial for them anywhere).

Am I also correct in assuming that using GLSL means there's no need for the cube map? I'm not very clear about how that works into things so I don't see where it would be necessary.

Sorry for all the questions, I'd just like to know what's going on with these things. What I've read so far has it making a lot of sense, it's just the minor stuff I need filled in. Thanks again for the help.

Share this post


Link to post
Share on other sites
>>N = vec3(gl_NormalMatrix * gl_Normal);
T = tangent;<<

the tangent/bitangent needs to be orientated the same as the normal thus perhaps u need to do
T = vec3(gl_NormalMatrix * tangent);

>> Is there a way that I can pass the per-vertex data into a GLSL program?<<
u can use attributes (check the spec) works with VBO's also, another method ppl use is using texturecoordinates to pass data (this is what i do

eg
tangent = gl_MultiTexCoord1.xyz;
bitangent = gl_MultiTexCoord2.xyz;
normal = gl_MultiTexCoord3.xyz;

Share this post


Link to post
Share on other sites
Okay, I've got everything golden except that things still don't look quite right. This is how I get the tangent/binormal:


void MD5Triangle::calculate_normal_tangents()
{
const EnVector3<GLdouble> a(m_v2->position - m_v1->position), b(m_v3->position - m_v2->position);

m_normal = a ^ b;
m_normal.normalize();

float d0 = m_v1->texture_v - m_v2->texture_v;
float d1 = m_v3->texture_v - m_v2->texture_v;
m_tangent = d1*a - d0*b;
m_tangent.normalize();

d0 = m_v1->texture_u - m_v2->texture_u;
d1 = m_v3->texture_u - m_v2->texture_u;
m_binormal = d1*a - d0*b;
//m_binormal = m_tangent ^ m_normal;
m_binormal.normalize();
}



This is my vertex shader:

attribute vec3 tangent, binormal;
varying vec3 V, L;

void main(void)
{
// set the vertex position
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

// get the vertex in eye space
V = vec3(gl_ModelViewMatrix * gl_Vertex);

// get the light position in object space
// then the vector from it to the vertex
L = normalize((gl_ModelViewMatrixInverse * gl_LightSource[0].position).xyz - gl_Vertex);

// create the tangent matrix
mat3 tbm;
tbm[0] = tangent;
tbm[1] = binormal;
tbm[2] = gl_Normal;

// put the light vector into tangent space
L = L * tbm;

// setup texturing
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[1] = gl_MultiTexCoord1;
}



And my fragment shader:

uniform sampler2D detail, normalmap;

varying vec3 V, L;

void main(void)
{
// get the normal
vec3 normal = normalize(2.0 * texture2D(normalmap, vec2(gl_TexCoord[1])).xyz - 1.0);

// get the eye vector
vec3 E = normalize(-V);

// get the diffuse value
float diffuse = dot(normal, L);

// get the reflection vector
vec3 R = normalize(2.0 * diffuse * normal - L);

// ambient color
vec4 Iamb = gl_FrontLightProduct[0].ambient;

// diffuse color
vec4 Idiff = gl_FrontLightProduct[0].diffuse * diffuse;

// get the specular amount
float specular = pow(max(dot(R, E), 0.0), gl_FrontMaterial.shininess);

// specular color
vec4 Ispec = gl_FrontLightProduct[0].specular * specular;

// final color
vec4 tv = texture2D(detail, vec2(gl_TexCoord[0]));
gl_FragColor = tv * (gl_FrontLightModelProduct.sceneColor + Iamb + Idiff + Ispec);
}



I thought I understood how this would work, so I'm a bit confused about why it doesn't. Hopefully it's something obvious. Thanks for taking a look at it.

Share this post


Link to post
Share on other sites
Okay, after some simplifying, I think it's working. Here's what I'm going:

vert:

attribute vec4 tangent, bitangent;
varying vec3 L;

void main(void)
{
// set the vertex position
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

// create the tangent matrix
mat4 tbn;
tbn[0] = tangent;
tbn[1] = bitangent;
tbn[2].xyz = gl_Normal;
tbn[3] = (0, 0, 0, 1);

// get the light position in object space
vec4 PO = gl_ModelViewMatrixInverse * gl_LightSource[0].position;

// then the vector from it to the vertex
vec4 LV = normalize(PO - gl_Vertex);

// put the light vector into tangent space
L = vec3(tbn * LV);

// setup texturing
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[1] = gl_MultiTexCoord1;
}




frag:

uniform sampler2D detail, normalmap;

varying vec3 L;

void main(void)
{
// get the normal
vec3 normal = normalize(2.0 * texture2D(normalmap, vec2(gl_TexCoord[1])).xyz - 1.0);

// final color
vec4 tv = texture2D(detail, vec2(gl_TexCoord[0]));
gl_FragColor = tv * (max(dot(normal, L), 0.0) * gl_FrontLightProduct[0].diffuse);
}



My problem now is that I want the light to be directional, not positional. I'm not exactly sure what modifications are necessary for that (all the tutorials deal with positional lights rather than directional). Is there an easy way to do that conversion?

Share this post


Link to post
Share on other sites
think about it, a direction light is just like a positional light BUT it remains in the same position WRT the vertex, thus it doesnt matter what the vertex has done (moved rotated, the offset to the light remains the same)
u need to look at these lines (directional lights have a 0.0 w component)

// get the light position in object space
vec4 PO = gl_ModelViewMatrixInverse * gl_LightSource[0].position;
// then the vector from it to the vertex
vec4 LV = normalize(PO - gl_Vertex);

Share this post


Link to post
Share on other sites
So what's going on here then? This is what it looks like right now (with a directional light at 0.0, 0.0, 1.0):

Screenshot 1
Screenshot 2

This is what it looks like if I don't use my shader:


Screenshot 3
Screenshot 4

The light is way brighter than I intended and it looks like it cuts off a lot earlier than the GL version. The params I'm using to create it are:

Quote:

position 0.0 0.0 1.0
ambient 0.0 0.0 0.0 1.0
diffuse 1.0 1.0 1.0 1.0
specular 1.0 1.0 1.0 1.0


Those are the only things I'm setting on the light. It also doesn't even look like the normal map is doing with it should be doing. There's no more detail on the models than there are without using normal maps.

Share this post


Link to post
Share on other sites
the first two are done using normalmapping right?
if thats the case then its not working,
first thing to do is disable ALL texturing (extra details confuse the issue)

in the fragment shader use this

float diffuse = dot( L, vec3(0.0,0.0,1.0) );
gl_FragColor = vec4( diffuse,diffuse,diffuse,1.0 );
thus u dont sample any textures at all.

i think the problem is that the normals/bitangents/tangents aint correct. to check the normal first use this in the fragment shader

float diffuse = dot( vec3(1.0,0.0,0.0), normal );
gl_FragColor = vec4( diffuse,diffuse,diffuse,1.0 );

// normal u get from the vertex shader

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!