Trouble with normal maps

Started by
19 comments, last by Chozo 19 years, 4 months ago
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.
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
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;//tangentvarying vec3 B;//binormalattribute 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;}
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.
>>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;
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.
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?
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);
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.
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

This topic is closed to new replies.

Advertisement