c++ - crear - ¿Qué pasa con mi mapeo normal? Creo que son mis tangentes
crear normal map online (3)
Aquí estoy la ilustración que mencioné en el comentario:
Como puede ver, las líneas rojas son normales generadas y cada vértice en la base tiene dos. Esto causa problemas de iluminación porque la cara de cada triángulo está en una dirección diferente. Cuando me enteré de esto tuve que tomar un promedio de ambas normales en cada vértice representado por las líneas amarillas para corregir los cálculos de iluminación.
En cuanto a la apariencia que está obteniendo de su mapa de cubos, esto podría estar en cómo está generando sus vértices para definir su esfera y cómo está aplicando sus coordenadas de textura. No puedo decirlo directamente sin ver toda su solución o proyecto y trabajar con él. El problema puede no involucrar a tus tangentes pero podría estar dentro de tu mapeado de texturas causando un efecto de envoltura.
Esta no es una respuesta directa a su problema, pero debe tener en cuenta las sugerencias, ya que hay muchas maneras diferentes de implementar estos tipos de sombreadores y representaciones.
editar: es posible que desee comenzar en "Editar 3" porque he resuelto mucho de esto
Aquí hay una captura de pantalla de mi mapa de cubos normal aplicado a una icosfera:
Las tangentes para mi icoesfera con mapa de cubos se generan con el siguiente código. m_indices
en un std::vector
de índices en el std::vector
de vértices en 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;
}
Mi VertexData se parece a este por cierto:
struct VertexData
{
GLfloat position[4];
GLfloat normal[3];
GLfloat tcoords[3];
GLfloat tangent[4];
};
Sé que los tcoords
actuales, la position
y la normal
están bien (de lo contrario, no tcoords
la captura de pantalla anterior).
Entonces mi sombreador de vértices se ve así:
#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;
}
Y finalmente mi sombreador de fragmentos se ve así:
#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);
}
Por lo que yo puedo decir:
- Mis tangentes se están calculando correctamente.
- Mi mapa normal parece un mapa normal para mí.
- Estoy cambiando mi luz y veo la dirección al espacio de la tangente para que coincida con mi mapa normal.
El resultado es nada. Es decir, nada se dibuja en la pantalla. No es un color sólido en absoluto. Entonces, como todo lo que está detrás se dibuja sin oclusión.
Si descarto la búsqueda en mi mapa normal, y en su lugar solo uso la luz de matriz tangente y veo lo siguiente:
Hay un destello de lente de postprocesamiento que está produciendo esos bits y bobs divertidos. Lo que es importante, creo que es el resplandor abrumador de la superficie donde las normales parecen algo precisas.
Si simplemente transformo la luz por la matriz tangente, obtengo esto:
Todo esto se combina para decirme que no tengo idea de dónde me estoy equivocando.
Tengo una idea de que es mi generación tangente porque las otras piezas parecen seguir lo que cada tutorial que he leído parece decir. Las tangentes se generan con una icosfera con mapa de cubos en mente. Entonces para determinar las coordenadas <S,T>
o <U,V>
2D de un cubo de mapas de coordenadas 3D usuales, I:
- Usa el valor más grande para determinar la cara en la que estoy
- Use el código de https://www.opengl.org/registry/specs/ARB/texture_cube_map.txt para determinar las coordenadas S, T
Aquí hay un extracto de https://www.opengl.org/registry/specs/ARB/texture_cube_map.txt del que estoy hablando.
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." ...
EDITAR No sé por qué no lo hice antes, pero he emitido las normales, tangentes y bitangentes en un sombreador de geometría para ver la forma en que están enfrentando. Usé este tutorial
Los amarillos son las normales de la cara, los verdes son las normales de los vértices. No estoy seguro de por qué las normales de los vértices parecen incorrectas, no afectan a ninguna otra iluminación, así que probablemente sea solo un error en mi sombreador de geometría.
Las tangentes son rojas, Binormals son azules. Estos parecen (es difícil de decir) como si fueran prependiculares entre sí, lo que es correcto, pero aparte de eso, no apuntan en direcciones uniformes. Esto es lo que le da el tipo de patrón moteado que tenía antes.
No tengo idea de cómo arreglar esto.
EDIT 2 He descubierto el problema con la visualización de mis normales, etc. Esto está solucionado ahora.
El resultado, agregué un poco de sombreado para hacerlo más claro, cada color es una cara de cubo diferente.
Otra cosa que he cambiado es la búsqueda en mi mapa normal. Olvidé ajustar el rango de nuevo a -1 a 1 (de 0 a 1).
normal = texture(mat.normal,textureLookup).rgb * 2.0 - 1.0;
Esto no soluciona mi problema.
La parte confusa es que cuando intento usar las normales de mi textura, no obtengo nada procesado. Nada en absoluto en el buffer de profundidad. He comprobado y comprobado dos veces que la textura es accesible desde el sombreador (de ahí la captura de pantalla original que muestra la textura aplicada a la esfera).
Porque a pesar de que mis Tangentes y Binormales están apuntando en todas direcciones; Todavía esperaría que se muestre algo, incluso si está mal. Pero ni siquiera el color ambiente está llegando. (Esto sucede incluso si dejo mi lightDir
y viewdir
solo. Si simplemente ignoro el vértice normal y busco la textura, pierdo el color ambiental) ...
EDIT 3: Un último problema
Como suele ser el caso, parte del problema no tiene nada que ver con el lugar donde crees que está mal. Mi problema era que estaba sobrescribiendo el atascamiento de mi mapa normal con una textura diferente.
Entonces, con eso fuera del camino, ahora puedo ver cómo se materializan mis colores. Con mi buen mapeo de golpes sexy.
Sin embargo, ahora hay un problema en las costuras del mapa de cubos. No estoy seguro de si se debe a las tangentes que se calculan o a la forma en que se genera mi mapa normal. Mi mapa normal se genera a partir de un mapa de altura para cada cara, de forma independiente.
Esto explicaría un poco de efecto de costura, creo, lo voy a modificar para probar la cara adyacente en esos bordes y ver qué pasa.
Todavía creo que las tangentes que se generan también van a tener un efecto adverso en estas costuras también. Mi pensamiento es que van a estar apuntando en direcciones opuestas en las costuras.
Captura de pantalla:
EDITAR 4 Mientras probaba desde EDIT1 hacia abajo, estaba usando una malla de poli muy baja para mi icosfera. Así que tuve subdivisiones mínimas.
Quería ver cómo lucía mi esfera no mapeada normal perfecta con muchos polys. Esto reveló instantáneamente este problema:
En caso de que no esté claro, corriendo desde la izquierda para escribir es mi viejo amigo, la costura, pero debajo de eso están, lo que parece ser, los bordes del triángulo.
Entonces, después de todo lo anterior, creo que he vuelto a mi problema original de tangentes incorrectas.
Todavía estoy buscando ayuda de cualquiera que esté leyendo esto.
EDITAR 4 Bueno, eso fue rápido. Este sitio aquí http://www.geeks3d.com/20130122/normal-mapping-without-precomputed-tangent-space-vectors/ me dio otra forma de crear tangentes. Aunque el código parece algo similar a lo que estaba haciendo en la CPU, no está resultando en esas tangentes orientadas al azar que estaban produciendo esos bordes en EDIT 3.
Estoy muy cerca ahora. Todavía tengo las costuras, este otro método de generar las tangentes parece haber aumentado su "seaminess"
EDIT 5 Ahora he intentado modificar mi generación normal de mapas. El código anterior fue así:
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
es una std::array
de 6
std::deque
s de glm::vec4
s. O bien, los seis lados de mi mapa de cubos. Los colores en las caras son en escala de grises, no estoy usando flotadores por razones que no importan.
Ahora lo cambié a lo siguiente, advirtiendo, esto es feo y largo.
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)];
}
}
}
Así que ahora estoy como "sangrando" en la superficie del cubo adyacente para probar la altura allí mientras genero el mapa normal. Esto realmente ha aumentado la apariencia de la costura.
Pero este tipo de plantea sus propias preguntas. Por ejemplo ... "¿por qué demonios se incrementa el afecto?" Puedes ver que ahora es una especie de efecto bisel.
Por lo tanto, estoy bastante seguro de haber emparejado mis caras de cubo correctamente cuando "sangro" en el siguiente. Esto me lleva de vuelta a que las tangentes son incorrectas.
Incluso si confundiera completamente las caras del cubo, no daría un efecto de bisel, sería algo completamente irregular. Por ejemplo, incluso en una sección completamente plana, es decir, sangrando la generación normal de mapas en la siguiente cara tendría cero efecto, aún veo un bisel masivo.
Esto me hace pensar que si las tangentes estaban correctas antes, ¿el mapa normal "coincide" con las direcciones de la tangente? No lo sé.
edición rápida Observé que estaba probando efectivamente los bordes de mi cara dos veces durante la generación de mi mapa original. Si elimino este doble muestreo y solo uso 0 para el adicional, termino viendo esas mismas grandes juntas. No estoy seguro de lo que esto significa ...
Otra edición rápida Esta imagen muestra algo que creo que es muy revelador.
Puedo ver aquí que dos caras diferentes están "apuntando" en direcciones opuestas. Esto es con mi generación tangente fragmento.
Así que vuelvo a mis tangentes como un problema.
Los mapas normales funcionan mejor con las matrices de vectores normales que originalmente crearon el mapa normal
Creo que su problema tiene que ver con la alineación no uniforme de sus tangentes en toda la superficie. El mapeo UV es generalmente el primer lugar para mirar con problemas como este. Y mapear una esfera con una imagen 2D no es tan fácil (mira todas las topologías de proyección de tierra y verás a qué me refiero). En algún momento, obtendrá estiramientos, bordes o cizalladura, y muy probablemente alguna combinación de todo lo anterior. Por lo general, con el mapeo UV, el punto es elegir dónde vas a ocultar estos efectos en la superficie. Los polos de los planetas a menudo se eligen para esto. Un lugar que buscaría sería realinear sus tangentes y binormales para que todos compartan una orientación común y global, es decir. tanget = norte, y binormal = este, con la normal hacia fuera (altitud). La falta de uniformidad de sus tangentes y binormales juega un papel directo en los artefactos que a veces surgen en los problemas normales de mapeo, ya que pueden torcer el efecto del mapa normal, en ese lugar, si el mapa normal fue horneado con la suposición de que todos tangentes y binormales están orientados uniformemente.
En esencia, el mapa normal fue horneado / creado con una comprensión implícita de sus tangentes y binormales. Si, cuando se vuelve a aplicar el mapa normal, las tangentes y los binormales de la superficie no se alinean con la comprensión implícita en la que originalmente se creó el mapa normal, entonces obtendrás errores de iluminación y sombreado.
El beneficio o matriz de vector normal ortogonal
El beneficio de esto es que los vectores tangentes y binormales a menudo se usan para buscar la coordenada de textura 2D. Si su matriz no es ortogonal, corre el riesgo de escuchar, girar o perder precisión en ángulos oblicuos.
Definir matrices uniformes, ortogonales de vectores normales
Podrías acercarte a tus cálculos normales / tangentes / binormales en un waut diferente que aseguraría dos factores:
- orientación uniforme del objeto, es decir ... todos apuntando en la misma dirección relativa
- vectores ortogonales, que limitarán el cizallamiento de búsqueda de textura
Esto funcionará transformando una matriz vectorial ortogonal predefinida a través de dos rotaciones y un movimiento. En aras de la explicación, no colapsaré esas tres operaciones matriciales en una sola matriz, pero podría ser necesario hacerlo en tu código.
Primero, comience con una matriz vectorial ya definida
vec3 = [1, 0, 0, 0, 1, 0, 0, 0, 1];
En segundo lugar, realice estas operaciones en el espacio de objetos, no en el espacio mundial
De lo contrario, deberá transformar ese objeto en centro mundial y rotarlo hacia su orientación de origen, luego aplicar las transformaciones normales y luego devolver el objeto a su posición de trabajo y orientación.
Tercero, crea un vector desde vtx [n] al centro del objeto
Este vector le dirá cuánto rotar su matriz normal de vectores en dos direcciones:
- Rotación longitudinal
- Rotación Latitudinal
En cuarto lugar, gire su matriz vectorial normal para alinear
Por último, mueve tu matriz normal de vectores por la distancia
Enjuague y repita
Si necesitara mantener UVs no uniformes, no ortogonales
Puede crear un mapa normal basado en un diseño UV incongruente, de modo que se aplique el diseño y, por lo tanto, se aplique de forma apropiada sin ningún efecto. Pero su mapa normal debería crearse a partir de esta incongruencia innata para que se aplique con elegancia a esos UV.
Interpolación Edge Pixel?
En tercer lugar, al ver cómo el borde del pliegue normal del mapa sigue la forma del mapa de cubos, me preguntaba cómo está interpolando píxeles de borde para su mapa normal.
Búsqueda de textura GLSL para cubemaps?
Además, y tal vez simplemente no encontré la sección de tu respuesta donde abordas esto, pero ¿has considerado utilizar la función de búsqueda de GLSL cubemap? gvec4 texture( gsamplerCube sampler, vec3 P, [float bias]);
Me llevó mucho tiempo recuperarme para comprender cómo calcular el espacio tangente. Tal vez la forma en que finalmente lo consiguió puede ayudar.
Tienes tres vértices v0, v1, v2. Cada uno tiene una posición, normal y uv. Vamos a calcular el espacio tangente para v0. El eje z será v0.normal. Necesitamos calcular los ejes x e y.
Cualquier punto en el triángulo se puede expresar como v0.pos + (v1.pos-v0.pos) * t + (v2.pos-v0.pos) * s. Cualquier coordenada de textura se puede expresar como v0.uv + (v1.uv - v0.uv) * t + (v2.uv - v0.uv) * s.
En el espacio tangente necesitamos tener v1.uv - v0.uv = (1,0) y v2.uv-v0.uv = (0,1). Podemos resolver para que s, t! para ambos casos! Y ese es el s y t para nuestra tangente y binormal. Sólo hay que enchufar en la ecuación posición y que tiene la posición donde uv = (0,1) y uv = (1,0). Restar v0.pos y tienes tu ejes X e Y.! También les normalizarse.
Y que es espacio tangente a V0. Una matriz de 3x3. No es necesariamente ortogonal. Pero eso es aceptable. También permite calcular esta matriz por vértice para cada triángulo con ese vértice. Sólo hay que promediar.
Interpolar los vértices por matrices cuando se representa, y normalizarlos por píxel.
Una buena manera de prueba es simplemente presentar la columna z - debe ser el normal.
Para la iluminación de restar la posición interpolada de la luz y transformarla por la "matriz tangente". Ahora su luz está en el espacio tangente, donde (0,0,1) es hacia la luz, y los mapas normales apuntan hacia arriba.