edit: you might want to start at "Edit 3" because I've solved a lot of this
Here's a screenshot of my normal cubemap applied to an icosphere:
The tangents for my cubemapped icosphere are generated with the following code. m_indices
in an std::vector
of indices into the std::vector
of vertices in m_vertices
.
std::vector<glm::vec3> storedTan(m_vertices.size(),glm::vec3(0,0,0));
// tangents
for(int i = 0; i < m_indices.size(); i+=3)
{
int i1 = m_indices[i];
int i2 = m_indices[i+1];
int i3 = m_indices[i+2];
VertexData v1 = m_vertices[i1];
VertexData v2 = m_vertices[i2];
VertexData v3 = m_vertices[i3];
glm::vec3 p1 = glm::vec3(v1.position[0],v1.position[1],v1.position[2]);
glm::vec3 p2 = glm::vec3(v2.position[0],v2.position[1],v2.position[2]);
glm::vec3 p3 = glm::vec3(v3.position[0],v3.position[1],v3.position[2]);
glm::vec3 t1 = glm::vec3(v1.tcoords[0],v1.tcoords[1],v1.tcoords[2]);
glm::vec3 t2 = glm::vec3(v2.tcoords[0],v2.tcoords[1],v2.tcoords[2]);
glm::vec3 t3 = glm::vec3(v3.tcoords[0],v3.tcoords[1],v3.tcoords[2]);
std::function<glm::vec2(glm::vec3)> get_uv = [=](glm::vec3 STR)
{
float sc, tc, ma;
float x = std::abs(STR.x);
float y = std::abs(STR.y);
float z = std::abs(STR.z);
if(x > y && x > z)
{
if(STR.x > 0)
{
sc = -STR.z;
tc = -STR.y;
ma = STR.x;
}
else
{
sc = STR.z;
tc = -STR.t;
ma = STR.x;
}
}
else if(y > z)
{
if(STR.y > 0)
{
sc = STR.x;
tc = STR.z;
ma = STR.y;
}
else
{
sc = STR.x;
tc = -STR.z;
ma = STR.y;
}
}
else
{
if(STR.z > 0)
{
sc = STR.x;
tc = -STR.y;
ma = STR.z;
}
else
{
sc = -STR.x;
tc = -STR.y;
ma = STR.z;
}
}
return glm::vec2((sc/std::abs(ma) + 1.0) / 2.0,(tc/std::abs(ma) + 1.0) / 2.0);
};
glm::vec2 uv1 = get_uv(t1);
glm::vec2 uv2 = get_uv(t2);
glm::vec2 uv3 = get_uv(t3);
glm::vec3 edge1 = p2 - p1;
glm::vec3 edge2 = p3 - p1;
glm::vec2 tedge1 = uv2 - uv1;
glm::vec2 tedge2 = uv3 - uv1;
float r = 1.0f / (tedge1.x * tedge2.y - tedge2.x - tedge1.y);
glm::vec3 sdir((tedge2.y * edge1.x - tedge1.y * edge2.x) * r,
(tedge2.y * edge1.y - tedge1.y * edge2.y) * r,
(tedge2.y * edge1.z - tedge1.y * edge2.z) * r);
glm::vec3 tdir((tedge1.x * edge2.x - tedge2.x * edge1.x) * r,
(tedge1.x * edge2.y - tedge2.x * edge1.y) * r,
(tedge1.x * edge2.z - tedge2.x * edge1.z) * r);
m_vertices[i1].tangent[0] += sdir.x;
m_vertices[i1].tangent[1] += sdir.y;
m_vertices[i1].tangent[2] += sdir.z;
m_vertices[i2].tangent[0] += sdir.x;
m_vertices[i2].tangent[1] += sdir.y;
m_vertices[i2].tangent[2] += sdir.z;
m_vertices[i3].tangent[0] += sdir.x;
m_vertices[i3].tangent[1] += sdir.y;
m_vertices[i3].tangent[2] += sdir.z;
storedTan[i1] += sdir;
storedTan[i2] += sdir;
storedTan[i3] += sdir;
}
for(int i = 0; i < m_vertices.size(); ++i)
{
glm::vec3 n = glm::vec3(m_vertices[i].normal[0],m_vertices[i].normal[1],m_vertices[i].normal[2]);
glm::vec3 t = glm::vec3(m_vertices[i].tangent[0],m_vertices[i].tangent[1],m_vertices[i].tangent[2]);
glm::vec3 newT = glm::normalize(t - n * glm::dot(n,t));
m_vertices[i].tangent[0] = newT.x;
m_vertices[i].tangent[1] = newT.y;
m_vertices[i].tangent[2] = newT.z;
m_vertices[i].tangent[3] = (glm::dot(glm::cross(n,t), storedTan[i]) < 0.0f) ? -1.0f : 1.0f;
}
My VertexData looks like this BTW:
struct VertexData
{
GLfloat position[4];
GLfloat normal[3];
GLfloat tcoords[3];
GLfloat tangent[4];
};
I know that the current tcoords
, position
and normal
are fine (otherwise you wouldn't see the screenshot above).
Then my vertex shader looks like this:
#version 400
layout (location = 0) in vec4 in_position;
layout (location = 1) in vec3 in_normal;
layout (location = 2) in vec3 in_UV;
layout (location = 3) in vec4 in_tangent;
struct PointLight
{
bool active;
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float constant;
float linear;
float quadratic;
};
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat4 lightMVP;
uniform PointLight uLight;
smooth out vec3 ex_UV;
out vec3 ex_normal;
out vec3 ex_positionCameraSpace;
out vec3 ex_originalPosition;
out vec3 ex_positionWorldSpace;
out vec4 ex_positionLightSpace;
out vec3 ex_tangent;
out vec3 ex_binormal;
out PointLight ex_light;
void main()
{
gl_Position = projection * view * model * in_position;
ex_UV = in_UV;
ex_normal = mat3(transpose(inverse(view * model))) * in_normal;
ex_positionCameraSpace = vec3(view * model * in_position);
ex_originalPosition = vec3(in_position.xyz);
ex_positionWorldSpace = vec3(model*in_position);
ex_positionLightSpace = lightMVP * model * in_position;
ex_tangent = mat3(transpose(inverse(view * model))) * in_tangent.xyz;
ex_binormal = cross(ex_normal,ex_tangent);
// provide the fragment shader with a light in view space rather than world space
PointLight p = uLight;
p.position = vec3(view * vec4(p.position,1.0));
ex_light = p;
}
And finally my fragment shader looks like this:
#version 400
layout (location = 0) out vec4 color;
struct Material
{
bool useMaps;
samplerCube diffuse;
samplerCube specular;
samplerCube normal;
float shininess;
vec4 color1;
vec4 color2;
};
struct PointLight
{
bool active;
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float constant;
float linear;
float quadratic;
};
uniform Material uMaterial;
smooth in vec3 ex_UV;
in vec3 ex_normal;
in vec3 ex_positionCameraSpace;
in vec3 ex_originalPosition;
in vec3 ex_positionWorldSpace;
in vec4 ex_positionLightSpace;
in vec3 ex_tangent;
in vec3 ex_binormal;
in PointLight ex_light;
/* ******************
Provides a better lookup into a cubemap
******************* */
vec3 fix_cube_lookup(vec3 v, float cube_size)
{
float M = max(max(abs(v.x), abs(v.y)), abs(v.z));
float scale = (cube_size - 1) / cube_size;
if (abs(v.x) != M)
v.x *= scale;
if (abs(v.y) != M)
v.y *= scale;
if (abs(v.z) != M)
v.z *= scale;
return v;
}
/* *********************
Calculates the color when using a point light. Uses shadow map
********************* */
vec3 CalcPointLight(PointLight light, Material mat, vec3 normal, vec3 fragPos, vec3 originalPos, vec3 viewDir)
{
// replace the normal with lookup normal. This is now in tangent space
vec3 textureLookup = fix_cube_lookup(normalize(ex_originalPosition),textureSize(mat.normal,0).x);
normal = texture(mat.normal,textureLookup).rgb;
// the direction the light is in in the light position - fragpos
// light dir and view dir are now in tangent space
vec3 lightDir = transpose(mat3(ex_tangent,ex_binormal,ex_normal)) * normalize(fragPos - light.position);
viewDir = transpose(mat3(ex_tangent,ex_binormal,ex_normal)) * viewDir;
// get the diffuse color
textureLookup = fix_cube_lookup(normalize(ex_originalPosition),textureSize(mat.diffuse,0).x);
vec3 diffuseMat = vec3(0.0);
if(mat.useMaps)
diffuseMat = texture(mat.diffuse,textureLookup).rgb;
else
diffuseMat = mat.color1.rgb;
// get the specular color
textureLookup = fix_cube_lookup(normalize(ex_originalPosition),textureSize(mat.specular,0).x);
vec3 specularMat = vec3(0.0);
if(mat.useMaps)
specularMat = texture(mat.specular,textureLookup).rgb;
else
specularMat = mat.color2.rgb;
// the ambient color is the amount of normal ambient light hitting the diffuse texture
vec3 ambientColor = light.ambient * diffuseMat;
// Diffuse shading
float diffuseFactor = dot(normal, -lightDir);
vec3 diffuseColor = vec3(0,0,0);
vec3 specularColor = vec3(0,0,0);
if(diffuseFactor > 0)
diffuseColor = light.diffuse * diffuseFactor * diffuseMat;
// Specular shading
vec3 reflectDir = normalize(reflect(lightDir, normal));
float specularFactor = pow(dot(viewDir,reflectDir), mat.shininess);
if(specularFactor > 0 && diffuseFactor > 0)
specularColor = light.specular * specularFactor * specularMat;
float lightDistance = length(fragPos - light.position);
float attenuation = light.constant + light.linear * lightDistance + light.quadratic * lightDistance * lightDistance;
return ambientColor + (diffuseColor + specularColor) / attenuation;
}
void main(void)
{
vec3 norm = normalize(ex_normal);
vec3 viewDir = normalize(-ex_positionCameraSpace);
vec3 result = CalcPointLight(ex_light,uMaterial,norm,ex_positionCameraSpace, ex_positionWorldSpace,viewDir);
color = vec4(result,1.0);
}
As far as I can tell:
- My tangents are being calculated correctly.
- My normal map looks like a normal map to me.
- I'm changing my light and view direction to tangent space to match my normal map.
The result is nothing. I.e. nothing is drawn to the screen. Not a solid color at all. So like everything behind is drawn with no occlusion.
If I discard the lookup into my normal map, and instead just use the tangent matrix light and view I get the following:
There's a post-processing lens flare on this that's producing those funny bits and bobs. What's important I think is the overwhelming glare from the surface where the normals seems somewhat accurate.
If I then just transform the light by the tangent matrix I get this:
All of this combines to tell me I have no idea where I'm going wrong.
I have an inkling that it's my tangent generation because the other pieces seem to follow what every tutorial I've read appear to say. The tangents are generated with a cubemapped icosphere in mind. So to determine the <S,T>
or <U,V>
2D coordinates from a cubemaps usual 3D coordinates, I:
- Use the largest value to determine the face I'm in
- Use the code from https://www.opengl.org/registry/specs/ARB/texture_cube_map.txt to determine the S,T coordinates
Here's an excerpt from https://www.opengl.org/registry/specs/ARB/texture_cube_map.txt that I'm talking about.
major axis
direction target sc tc ma
---------- ------------------------------- --- --- ---
+rx TEXTURE_CUBE_MAP_POSITIVE_X_ARB -rz -ry rx
-rx TEXTURE_CUBE_MAP_NEGATIVE_X_ARB +rz -ry rx
+ry TEXTURE_CUBE_MAP_POSITIVE_Y_ARB +rx +rz ry
-ry TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB +rx -rz ry
+rz TEXTURE_CUBE_MAP_POSITIVE_Z_ARB +rx -ry rz
-rz TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB -rx -ry rz
Using the sc, tc, and ma determined by the major axis direction as
specified in the table above, an updated (s,t) is calculated as
follows
s = ( sc/|ma| + 1 ) / 2
t = ( tc/|ma| + 1 ) / 2
This new (s,t) is used to find a texture value in the determined
face's 2D texture image using the rules given in sections 3.8.5
and 3.8.6." ...
EDIT I don't know why I didn't before, but I've output the normals, tangents and bitangents in a geometry shader to see the way they're facing. I used this tutorial.
The yellows are the face normals, the greens are the vertex normals. I'm not sure why the vertex normals seem wrong, they don't affect any other lighting so it's probably just an error in my geometry shader.
Tangents are red, Binormals are blue. These seem (it's hard to tell) like they are prependicular to each-other, which is correct, but other than that they are not pointing in uniform directions. This is what's given the mottled sort of pattern I had before.
I have no idea how to fix this.
EDIT 2 I've figured out the problem with displaying my normals etc. This is fixed now.
The result, I added some shading to make it clearer, each color is a different cube face.
Something else I've changed is the lookup into my normal map. I forgot to adjust the range back into -1 to 1 (from 0 to 1).
normal = texture(mat.normal,textureLookup).rgb * 2.0 - 1.0;
This doesn't fix my problem.
The confusing part is that when I try using the normals from my texture, I don't get anything rendered. Nothing at all into the depth buffer. I've checked and double checked that the texture is accessible from the shader (hence the original screenshot showing the texture applied to the sphere).
Because even though my Tangents and Binormals are pointing every which way; I'd still expect something to be shown, even if it's wrong. But not even the ambient color is coming through. (this happens even if I leave my lightDir
and viewdir
alone. If I just ignore the vertex normal and lookup the texture. I lose ambient color)...
EDIT 3: One last problem
As is often the case, part of the problem had nothing to do with where you think it's wrong. My problem was that I was overwriting the binding of my normal map with a different texture.
So, with that out of the way, I can now see my colours come through. With my nice sexy bump mapping.
However, there's now a problem at the seams of the cubemap. I'm not sure if it's because of the tangents being calculated or because of the way my normal map is generated. My normal map is generated from a height map for each face, independently.
This would explain some seam affect I think, I'm going to modify it to sample the adjacent face on those edges and see what happens.
I still think that the tangents being generated are also going to have an adverse affect on these seams as well. My thoughts is that they are going to be pointing in opposite directions at the seams.
Screenshot:
EDIT 4 While testing from EDIT1 down I was using a very very low poly mesh for my icosphere. So I had minimal sub-divisions.
I wanted to see how my not quite perfect normal mapped sphere looked with lots of polys. This instantly revealed this problem:
In case it's not clear, running from left to write is my old friend, the seam, but below that are, what appears to be, triangle edges.
So after all of the above, I think I'm back to my original problem of incorrect tangents.
Still looking for some help from anyone that's reading this.
EDIT 4 Well, that was quick. This site here http://www.geeks3d.com/20130122/normal-mapping-without-precomputed-tangent-space-vectors/ gave me another way to create tangents. Whilst the code seems somewhat similar to what I was doing on the CPU, it's not resulting in those randomly oriented tangents that was producing those edges from EDIT 3.
I am very close now. I still have the seams, this other method of generating the tangents seems to have increased their "seaminess"
EDIT 5 I've now tried modifying my normal map generation. The previous code went like this:
for(int i = 0; i < 6; ++i)
{
float scale = 15.0;
std::deque<glm::vec4> normalMap(textureSize*textureSize);
for(int x = 0; x < textureSize; ++x)
{
for(int y = 0; y < textureSize; ++y)
{
// center point
int i11 = utils::math::get_1d_array_index_from_2d(x,y,textureSize);
float v11 = cubeFacesHeight[i][i11].r;
// to the left
int i01 = utils::math::get_1d_array_index_from_2d(std::max(x-1,0),y,textureSize);
float v01 = cubeFacesHeight[i][i01].r;
// to the right
int i21 = utils::math::get_1d_array_index_from_2d(std::min(x+1,textureSize-1),y,textureSize);
float v21 = cubeFacesHeight[i][i21].r;
// to the top
int i10 = utils::math::get_1d_array_index_from_2d(x,std::max(y-1,0),textureSize);
float v10 = cubeFacesHeight[i][i10].r;
// and now the bottom
int i12 = utils::math::get_1d_array_index_from_2d(x,std::min(y+1,textureSize-1),textureSize);
float v12 = cubeFacesHeight[i][i12].r;
glm::vec3 S = glm::vec3(1, 0, scale * v21 - scale * v01);
glm::vec3 T = glm::vec3(0, 1, scale * v12 - scale * v10);
glm::vec3 N = (glm::vec3(-S.z,-T.z,1) / std::sqrt(S.z*S.z + T.z*T.z + 1));
N.x = (N.x+1.0)/2.0;
N.y = (N.y+1.0)/2.0;
N.z = (N.z+1.0)/2.0;
normalMap[utils::math::get_1d_array_index_from_2d(x,y,textureSize)] = glm::vec4(N.x,N.y,N.z,v11);
}
}
for(int x = 0; x < textureSize; ++x)
{
for(int y = 0; y < textureSize; ++y)
{
cubeFacesHeight[i][utils::math::get_1d_array_index_from_2d(x,y,textureSize)] = normalMap[utils::math::get_1d_array_index_from_2d(x,y,textureSize)];
}
}
}
cubeFacesHeight
is an std::array
of 6
std::deque
s of glm::vec4
s. Or, the six sides of my cubemap. The colors in the faces are greyscale, I'm not using floats for reasons that don't matter.
I've now changed it to the following, warning, this is ugly and long.
for(int i = 0; i < 6; ++i)
{
// 0 is negative X
// 1 is positive X
// 2 is negative Y
// 3 is positive Y
// 4 is negative Z
// 5 is positive Z
// +X: right -Z (left), left +Z (right), top -Y (right), bottom +Y (right)
// -X: right +Z (left), left -Z (right), top -Y (left), bottom +Y (left)
// -Z: right -X (left), left +X (right), top -Y (bottom), bottom +Y (top)
// +Z: right +X (left), left -X (right), top -Y (top), bottom +Y (bottom)
// -Y: right +X (top), left -X (top), top +Z (top), bottom -Z (top)
// +Y: right +X (bottom), left -X (bottom), top -Z (bottom), bottom +Z (bottom)
//+Z is towards, -Z is distance
const int NEGATIVE_X = 0;
const int NEGATIVE_Y = 2;
const int NEGATIVE_Z = 4;
const int POSITIVE_X = 1;
const int POSITIVE_Y = 3;
const int POSITIVE_Z = 5;
float scale = 15.0;
std::deque<glm::vec4> normalMap(textureSize*textureSize);
for(int x = 0; x < textureSize; ++x)
{
for(int y = 0; y < textureSize; ++y)
{
// center point
int i11 = utils::math::get_1d_array_index_from_2d(x,y,textureSize);
float v11 = cubeFacesHeight[i][i11].r;
// to the left
int i01 = utils::math::get_1d_array_index_from_2d(std::max(x-1,0),y,textureSize);
float v01 = cubeFacesHeight[i][i01].r;
if(x-1 < 0)
{
if(i == NEGATIVE_X)
{
i01 = utils::math::get_1d_array_index_from_2d(textureSize-1,y,textureSize);
v01 = cubeFacesHeight[NEGATIVE_Z][i01].r;
}
else if(i == POSITIVE_X)
{
i01 = utils::math::get_1d_array_index_from_2d(textureSize-1,y,textureSize);
v01 = cubeFacesHeight[POSITIVE_Z][i01].r;
}
else if(i == NEGATIVE_Z)
{
i01 = utils::math::get_1d_array_index_from_2d(textureSize-1,y,textureSize);
v01 = cubeFacesHeight[POSITIVE_X][i01].r;
}
else if(i == POSITIVE_Z)
{
i01 = utils::math::get_1d_array_index_from_2d(textureSize-1,y,textureSize);
v01 = cubeFacesHeight[NEGATIVE_X][i01].r;
}
else if(i == NEGATIVE_Y)
{
i01 = utils::math::get_1d_array_index_from_2d(y,0,textureSize);
v01 = cubeFacesHeight[NEGATIVE_X][i01].r;
}
else if(i == POSITIVE_Y)
{
i01 = utils::math::get_1d_array_index_from_2d(y,textureSize-1,textureSize);
v01 = cubeFacesHeight[NEGATIVE_X][i01].r;
}
}
// to the right
int i21 = utils::math::get_1d_array_index_from_2d(std::min(x+1,textureSize-1),y,textureSize);
float v21 = cubeFacesHeight[i][i21].r;
if(x+1 > textureSize-1)
{
if(i == NEGATIVE_X)
{
i01 = utils::math::get_1d_array_index_from_2d(0,y,textureSize);
v01 = cubeFacesHeight[POSITIVE_Z][i01].r;
}
else if(i == POSITIVE_X)
{
i01 = utils::math::get_1d_array_index_from_2d(0,y,textureSize);
v01 = cubeFacesHeight[NEGATIVE_Z][i01].r;
}
else if(i == NEGATIVE_Z)
{
i01 = utils::math::get_1d_array_index_from_2d(0,y,textureSize);
v01 = cubeFacesHeight[NEGATIVE_X][i01].r;
}
else if(i == POSITIVE_Z)
{
i01 = utils::math::get_1d_array_index_from_2d(0,y,textureSize);
v01 = cubeFacesHeight[POSITIVE_X][i01].r;
}
else if(i == NEGATIVE_Y)
{
i01 = utils::math::get_1d_array_index_from_2d(y,0,textureSize);
v01 = cubeFacesHeight[POSITIVE_X][i01].r;
}
else if(i == POSITIVE_Y)
{
i01 = utils::math::get_1d_array_index_from_2d(y,textureSize-1,textureSize);
v01 = cubeFacesHeight[POSITIVE_X][i01].r;
}
}
// to the top
int i10 = utils::math::get_1d_array_index_from_2d(x,std::max(y-1,0),textureSize);
float v10 = cubeFacesHeight[i][i10].r;
if(y-1 < 0)
{
if(i == NEGATIVE_X)
{
i01 = utils::math::get_1d_array_index_from_2d(0,x,textureSize);
v01 = cubeFacesHeight[NEGATIVE_Y][i01].r;
}
else if(i == POSITIVE_X)
{
i01 = utils::math::get_1d_array_index_from_2d(textureSize-1,x,textureSize);
v01 = cubeFacesHeight[NEGATIVE_Y][i01].r;
}
else if(i == NEGATIVE_Z)
{
i01 = utils::math::get_1d_array_index_from_2d(x,textureSize-1,textureSize);
v01 = cubeFacesHeight[NEGATIVE_Y][i01].r;
}
else if(i == POSITIVE_Z)
{
i01 = utils::math::get_1d_array_index_from_2d(x,0,textureSize);
v01 = cubeFacesHeight[NEGATIVE_Y][i01].r;
}
else if(i == NEGATIVE_Y)
{
i01 = utils::math::get_1d_array_index_from_2d(x,0,textureSize);
v01 = cubeFacesHeight[POSITIVE_Z][i01].r;
}
else if(i == POSITIVE_Y)
{
i01 = utils::math::get_1d_array_index_from_2d(x,textureSize-1,textureSize);
v01 = cubeFacesHeight[NEGATIVE_Z][i01].r;
}
}
// and now the bottom
int i12 = utils::math::get_1d_array_index_from_2d(x,std::min(y+1,textureSize-1),textureSize);
float v12 = cubeFacesHeight[i][i12].r;
if(y+1 > textureSize-1)
{
if(i == NEGATIVE_X)
{
i01 = utils::math::get_1d_array_index_from_2d(0,x,textureSize);
v01 = cubeFacesHeight[POSITIVE_Y][i01].r;
}
else if(i == POSITIVE_X)
{
i01 = utils::math::get_1d_array_index_from_2d(textureSize-1,x,textureSize);
v01 = cubeFacesHeight[POSITIVE_Y][i01].r;
}
else if(i == NEGATIVE_Z)
{
i01 = utils::math::get_1d_array_index_from_2d(x,0,textureSize);
v01 = cubeFacesHeight[POSITIVE_Y][i01].r;
}
else if(i == POSITIVE_Z)
{
i01 = utils::math::get_1d_array_index_from_2d(x,textureSize-1,textureSize);
v01 = cubeFacesHeight[POSITIVE_Y][i01].r;
}
else if(i == NEGATIVE_Y)
{
i01 = utils::math::get_1d_array_index_from_2d(x,0,textureSize);
v01 = cubeFacesHeight[NEGATIVE_Z][i01].r;
}
else if(i == POSITIVE_Y)
{
i01 = utils::math::get_1d_array_index_from_2d(x,textureSize-1,textureSize);
v01 = cubeFacesHeight[POSITIVE_Z][i01].r;
}
}
glm::vec3 S = glm::vec3(1, 0, scale * v21 - scale * v01);
glm::vec3 T = glm::vec3(0, 1, scale * v12 - scale * v10);
glm::vec3 N = (glm::vec3(-S.z,-T.z,1) / std::sqrt(S.z*S.z + T.z*T.z + 1));
N.x = (N.x+1.0)/2.0;
N.y = (N.y+1.0)/2.0;
N.z = (N.z+1.0)/2.0;
normalMap[utils::math::get_1d_array_index_from_2d(x,y,textureSize)] = glm::vec4(N.x,N.y,N.z,v11);
}
}
for(int x = 0; x < textureSize; ++x)
{
for(int y = 0; y < textureSize; ++y)
{
cubeFacesHeight[i][utils::math::get_1d_array_index_from_2d(x,y,textureSize)] = normalMap[utils::math::get_1d_array_index_from_2d(x,y,textureSize)];
}
}
}
So I'm now sort of "bleeding" into the adjacent cubeface to sample the height there while generating the normalmap. This has actually increased the seam appearance.
But this sort of raises it's own questions. For instance... "why in the hell is the affect increased?" You can see that it's now a sort of bevel effect.
So, I'm fairly sure I've matched up my cubefaces correctly when "bleeding" into the next one. This brings me back to the tangents being incorrect.
Even if I completely mixed up the cube faces, it wouldn't give a bevel effect, it would be something completely spotty. For example, even on a completely flat section, i.e., bleeding the normal map generation into the next face would have zero effect, I still see a massive bevel.
This makes me think that if the tangents were correct before, the normal map sort of "matched" the tangent directions? I don't know.
quick edit I noticed that I was effectively sampling my face edges twice during my original map generation. If I remove this double sampling and just use 0 for the additional, I end up seeing those same big seams. I'm not sure what this means...
Another quick edit This image shows something that I think is very telling.
I can see here that two different faces are "pointing" in opposite directions. This is with my in fragment tangent generation.
So I'm back to my tangents being a problem.