I've been trying to figure this out for a few days now, but I just can't get it right. I've read multiple tutorials and possible explanations, I even watched a java tutorial without finding the solution. This is what I do:
First I build the tangent values to pass as attributes:
//this parsing has not been updated, unlike the shaders, correct parsing in the answer below
float f;
vec3 edge1, edge2, tangentbuf;
vec2 deltaUV1, deltaUV2;
//for each face, x=vert,y=uv, z=normal
for (size_t i = 0; i < totalrefs.size(); i += 9)
{
edge1 = verticeref[totalrefs[i + 3] - 1] - verticeref[totalrefs[i] - 1];
edge2 = verticeref[totalrefs[i + 6] - 1] - verticeref[totalrefs[i] - 1];
deltaUV1 = texkoordref[totalrefs[i + 4] - 1] - texkoordref[totalrefs[i + 1] - 1];
deltaUV2 = texkoordref[totalrefs[i + 7] - 1] - texkoordref[totalrefs[i + 1] - 1];
f = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y);
tangentbuf = f*vec3(
(deltaUV2.y * edge1.x - deltaUV1.y * edge2.x),
(deltaUV2.y * edge1.y - deltaUV1.y * edge2.y),
(deltaUV2.y * edge1.z - deltaUV1.y * edge2.z));
normalize(tangentref[tangentcount++]);
tangentref[totalrefs[i] - 1] += vec4(normalize(tangentbuf), 1);
tangentref[totalrefs[i + 3] - 1] += vec4(normalize(tangentbuf), 1);
tangentref[totalrefs[i + 6] - 1] += vec4(normalize(tangentbuf), 1);
}
//for (auto &x : tangentref)x = normalize(x / x.w); //find average
for (auto &x : tangentref)x = normalize(x); //just normalize is enough?
Normalizing or dividing seems to give the same result, and I think this is correct this far. With these shaders:
vert
#version 400
layout ( location = 0 ) in vec3 vertex_position;
layout ( location = 1 ) in vec2 tex_cord;
layout ( location = 2 ) in vec3 vertex_normal;
layout ( location = 3 ) in vec3 vertex_tangent;
uniform mat4 model; //model matrix, aka model 2 world
uniform mat4 modelviewmatrix; //view*model matrix
uniform mat3 normalmatrix; // transpose/invert(view*model)
uniform mat4 priv_mat; //projection-view-model pre multiplied
uniform vec4 LightPosition; //after view*lightpos, ie viewspace
out vec3 lightdir;
out vec3 viewdir;
out vec2 fUV;
out vec3 position;
void main()
{
gl_Position=priv_mat*vec4(vertex_position,1.0);
fUV=tex_cord;
position=(model*vec4(vertex_position,1.0)).xyz;
len=length(vertex_position);
// Transform normal and tangent to eye space
vec3 n = normalize(normalmatrix * vertex_normal);
vec3 t = normalize(normalmatrix * vec3(vertex_tangent));
t = normalize(t-dot(t,n)*n); //Gramm-Schmidt process
vec3 bitangent = normalize( cross( n, t ) );
mat3 tbn = transpose(mat3(t,bitangent,n)); //why transpose??
vec3 pos=vec3(modelviewmatrix*vec4(vertex_position,1.0));
// Transform light dir. and view dir. to tangent space
lightdir = normalize( tbn * (LightPosition.xyz - pos) );
viewdir = tbn * normalize(-pos);
}
And frag
#version 400
in float len;
in vec3 lightdir; //tang light
in vec3 viewdir; //tang view
in vec2 fUV;
in vec3 position;
uniform sampler2D png_tex;
uniform sampler2D bump_map;
uniform vec4 LightPosition;
uniform vec3 LightIntensity;
uniform vec3 Kd; // Diffuse reflectivity
uniform vec3 Ka; // Ambient reflectivity
uniform vec3 Ks; // Specular reflectivity
uniform float Shininess; // Specular shininess factor
out vec4 Color;
vec3 phongModel( vec3 norm, vec3 diffr );
void main()
{
vec4 b_normal = (255.0/128.0) * texture( bump_map, fUV )-1.0; // Lookup the normal from the normal map
vec4 texColor = texture( png_tex, fUV ); // The color texture is used as the diffuse reflectivity
Color = vec4( phongModel(b_normal.xyz, texColor.rgb), 1.0 );
}
vec3 phongModel( vec3 norm, vec3 diffr )
{
vec3 r = reflect( -lightdir, norm );
vec3 ambient = LightIntensity * Ka;
float sDotN = max( dot(lightdir, norm), 0.0 );
vec3 diffuse = Kd*LightIntensity * diffr * sDotN;
vec3 spec = vec3(0.0);
if( sDotN > 0.0 )
spec = LightIntensity * Ks * pow( max( dot(r,viewdir), 0.0 ), Shininess );
return ambient + diffuse + spec;
}
I only got this result: the shaders are updated as I went along, so the ones posted above work (for me anyway)
Which has 2 obvious errors: When I rotate the object, the shading doesn't follow the lightsource and goes from dark to full bright, and, it seems to have dents at seams where the cube-sphere wraps. The normalmapping seems to work though, but again I struggle with the shading. Is there an obvious error here?