3
$\begingroup$

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)

when rotated it turns dark

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?

$\endgroup$

1 Answer 1

2
$\begingroup$

Silly me, posting all of this hoping to get someone to solve it for me, I managed to find the solution tho, but perhaps more as a result of trial and error, than of actual understanding. Either way, this is the object I'm looking at now:

UV ordering, transpose and crossing in the wrong order

Looking at my current tangent calculation:

vector<vec4>tangentref(texkoordref.size()); //math UV size, w=amount for divide vec3 tangentbuf; float f; vec3 edge1, edge2; vec2 deltaUV1, deltaUV2; for (size_t i = 0; i < totalrefs.size(); i += 9) { //i=vert, i+1=uv, i+2 vn//i+3=vert2,i+4=uv2,i+5=vn2;//i+6=vert3, i+7=uv3, i+8=vn3; //get the tangent for each face 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)); //one tangent per uv coordinate tangentref[totalrefs[i+1] - 1] += vec4(normalize(tangentbuf), 1); tangentref[totalrefs[i + 4] - 1] += vec4(normalize(tangentbuf), 1); tangentref[totalrefs[i + 7] - 1] += vec4(normalize(tangentbuf), 1); } for (auto &x : tangentref)x = normalize(x / x.w); //find average //some tutorials say to just normalize these 

I cycle through every face, and totalref[i], is here 9 integers per cycle, that each refer to +0=vert, +1=uv, and +2=vert normal. By populating the tangentref according to each of the UVs, I get a list of tangents that go along with the UV values.

I think its worth mentioning specifically, the tangents describe not the normal, or the vertex, but the direction of the uv, together with the bitangent, they make out an xy coordinate that describes the UV direction.

It only makes sense to make tangents according to UVs, and the results are not disagreeing.

Secondly, I was building the tbn matrix incorrectly.

//in the vert shader vec3 bitangent = normalize( cross( n, t ) ); 

The bitangent was in the wrong order, and

mat3 tbn = transpose(mat3(t,bitangent,n)); //why transpose?? 

The tbn was supposed to be transpose. Transpose is cheap (apparently) so thats not a problem, but why exactly this is correct? I'm not sure.

It works (better) however, so maybe It will make sense later.

enter image description here

$\endgroup$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.