c++ - gl_ambient - opengl gl_lighting
El mapeo normal saliĆ³ terriblemente mal (2)
Traté de implementar el mapeo normal en mi aplicación opengl pero no puedo hacer que funcione.
This es el mapa difuso (al que agrego un color marrón) y this es el mapa normal.
Para obtener los vectores tangente y bitangente (en otros lugares llamados binormales?), Ejecuto esta función para cada triángulo en mi malla:
void getTangent(const glm::vec3 &v0, const glm::vec3 &v1, const glm::vec3 &v2,
const glm::vec2 &uv0, const glm::vec2 &uv1, const glm::vec2 &uv2,
std::vector<glm::vec3> &vTangents, std::vector<glm::vec3> &vBiangents)
{
// Edges of the triangle : postion delta
glm::vec3 deltaPos1 = v1-v0;
glm::vec3 deltaPos2 = v2-v0;
// UV delta
glm::vec2 deltaUV1 = uv1-uv0;
glm::vec2 deltaUV2 = uv2-uv0;
float r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
glm::vec3 tangent = (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y)*r;
glm::vec3 bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x)*r;
for(int i = 0; i < 3; i++) {
vTangents.push_back(tangent);
vBiangents.push_back(bitangent);
}
}
Después de eso, llamo a glBufferData para cargar los vértices, normales, uvs, tangentes y bitangentes a la GPU. El sombreador de vértices:
#version 430
uniform mat4 ProjectionMatrix;
uniform mat4 CameraMatrix;
uniform mat4 ModelMatrix;
in vec3 vertex;
in vec3 normal;
in vec2 uv;
in vec3 tangent;
in vec3 bitangent;
out vec2 fsCoords;
out vec3 fsVertex;
out mat3 TBNMatrix;
void main()
{
gl_Position = ProjectionMatrix * CameraMatrix * ModelMatrix * vec4(vertex, 1.0);
fsCoords = uv;
fsVertex = vertex;
TBNMatrix = mat3(tangent, bitangent, normal);
}
Sombreador de fragmentos:
#version 430
uniform sampler2D diffuseMap;
uniform sampler2D normalMap;
uniform mat4 ModelMatrix;
uniform vec3 CameraPosition;
uniform struct Light {
float ambient;
vec3 position;
} light;
uniform float shininess;
in vec2 fsCoords;
in vec3 fsVertex;
in mat3 TBNMatrix;
out vec4 color;
void main()
{
//base color
const vec3 brownColor = vec3(153.0 / 255.0, 102.0 / 255.0, 51.0 / 255.0);
color = vec4(brownColor * (texture(diffuseMap, fsCoords).rgb + 0.25), 1.0);//add a fixed base color (0.25), because its dark as hell
//general vars
vec3 normal = texture(normalMap, fsCoords).rgb * 2.0 - 1.0;
vec3 surfacePos = vec3(ModelMatrix * vec4(fsVertex, 1.0));
vec3 surfaceToLight = normalize(TBNMatrix * (light.position - surfacePos)); //unit vector
vec3 eyePos = TBNMatrix * CameraPosition;
//diffuse
float diffuse = max(0.0, dot(normal, surfaceToLight));
//specular
float specular;
vec3 incidentVector = -surfaceToLight; //unit
vec3 reflectionVector = reflect(incidentVector, normal); //unit vector
vec3 surfaceToCamera = normalize(eyePos - surfacePos); //unit vector
float cosAngle = max(0.0, dot(surfaceToCamera, reflectionVector));
if(diffuse > 0.0)
specular = pow(cosAngle, shininess);
//add lighting to the fragment color (no attenuation for now)
color.rgb *= light.ambient;
color.rgb += diffuse + specular;
}
La imagen que obtengo es completamente incorrecta. (luz colocada en la cámara)
¿Qué estoy haciendo mal aquí?
Intentaré hacer que tu código funcione. ¿Lo has probado con cámara en movimiento?
No puedo ver en ninguna parte que haya transformado la
TBNMatrix
con las matrices de transformación, vista y modelo.
¿
vec3 normal = TBNMatrix[2];
con el
vec3 normal = TBNMatrix[2];
normales originales?
(Fragmento sombreador)
Lo siguiente podría ayudar. En el sombreador Vertex tienes:
uniform mat4 ProjectionMatrix;
uniform mat4 CameraMatrix;
uniform mat4 ModelMatrix;
Sin embargo, aquí solo se deben usar estas 3 matrices:
uniform mat4 PCM;
uniform mat4 MIT; //could be mat3
uniform mat4 ModelMatrix; //could be mat3
Es más eficiente calcular el producto de esas matrices en la CPU (y produce el mismo porque la multiplicación de matrices es asociativa). Entonces este producto, el PCM se puede usar para calcular la nueva posición con una multiplicación por vértice:
gl_Position = PCM * vec4(vertex, 1.0);
El
MIT
es la transposición inversa de
ModelMatrix
, debe calcularlo en la CPU.
Esto se puede usar para transformar las normales:
vec4 tang = ModelMatrix*vec4(tangent,0);
vec4 bita= ModelMatrix*vec4(bitangent,0);
vec4 norm= PCMIT*vec4(tangent,0);
TBNMatrix = mat3(normalize(tang.xyz), normalize(bita.xyz), normalize(normal.xyz));
No estoy seguro de qué sucede con la tangente y la bitangente, pero de esta manera lo normal se mantendrá perpendicular a ellas. Es fácil de probar. Aquí uso a ° b como el producto skalar de los vectores ayb. Entonces, n sea algo normal, y a es un vector en la superficie (por ejemplo, {bi} tangente, borde de un triángulo), y sea A cualquier transformación. Entonces:
0 = an = A ^ (- 1) A a ° n = A a ° A ^ (- T) n = 0
Donde utilicé la igualdad A x ° y = x ° A ^ T y. Por lo tanto, si a es perpendicular a n, entonces A a es perpendicular a A ^ (- T) n, por lo que tenemos que transformarlo con la transposición inversa de la matriz. Sin embargo, lo normal debe tener una longitud de 1, por lo que después de las transformaciones, debe normalizarse.
También puede obtener una normalidad perpendicular haciendo esto:
vec3 normal = normalize(cross(tangent, bitangent));
Donde cross (a, b) es la función que calcula el producto cruzado de a y b, la bruja siempre es perpendicular a a y b.
Lo siento por mi ingles :)
Mi apuesta es la configuración de color / mezcla en fragment shader ...
-
está configurando el color de salida más de una vez
Si recuerdo correctamente en algunos controladores gfx que hacen un gran problema, por ejemplo, todo después de la línea
color = vec4(brownColor * (texture(diffuseMap, fsCoords).rgb + 0.25), 1.0);//add a fixed base color (0.25), because its dark as hell
podría ser eliminado por el conductor ...
-
agrega
color
eintensities
lugar decolor*intensity
pero podría pasar por alto algo.
-
prueba solo el sombreado normal / irregular al principio
Ignora el ambiente, el reflejo, el especular ... y luego, si funciona, agrega el resto uno por uno. Compruebe siempre los registros de compilación del sombreador
Demasiado perezoso para analizar aún más su código, así que así es como lo hago:
El tamaño de la izquierda es el objeto de la nave espacial (similar al Viper de ZXS Elite) renderizado con una función fija. Lado derecho igual (una rotación de objeto un poco diferente) con el sombreador GLSL en su lugar y este mapa normal / de relieve
[Vértice]
//------------------------------------------------------------------
#version 420 core
//------------------------------------------------------------------
// texture units:
// 0 - texture0 map 2D rgba
// 1 - texture1 map 2D rgba
// 2 - normal map 2D xyz
// 3 - specular map 2D i
// 4 - light map 2D rgb rgb
// 5 - enviroment/skybox cube map 3D rgb
uniform mat4x4 tm_l2g;
uniform mat4x4 tm_l2g_dir;
uniform mat4x4 tm_g2s;
uniform mat4x4 tm_l2s_per;
uniform mat4x4 tm_per;
layout(location=0) in vec3 pos;
layout(location=1) in vec4 col;
layout(location=2) in vec2 txr;
layout(location=3) in vec3 tan;
layout(location=4) in vec3 bin;
layout(location=5) in vec3 nor;
out smooth vec3 pixel_pos;
out smooth vec4 pixel_col;
out smooth vec2 pixel_txr;
//out flat mat3 pixel_TBN;
out smooth mat3 pixel_TBN;
//------------------------------------------------------------------
void main(void)
{
vec4 p;
p.xyz=pos;
p.w=1.0;
p=tm_l2g*p;
pixel_pos=p.xyz;
p=tm_g2s*p;
gl_Position=p;
pixel_col=col;
pixel_txr=txr;
p.xyz=tan.xyz; p.w=1.0; pixel_TBN[0]=normalize((tm_l2g_dir*p).xyz);
p.xyz=bin.xyz; p.w=1.0; pixel_TBN[1]=normalize((tm_l2g_dir*p).xyz);
p.xyz=nor.xyz; p.w=1.0; pixel_TBN[2]=normalize((tm_l2g_dir*p).xyz);
}
//------------------------------------------------------------------
[Fragmento]
//------------------------------------------------------------------
#version 420 core
//------------------------------------------------------------------
in smooth vec3 pixel_pos;
in smooth vec4 pixel_col;
in smooth vec2 pixel_txr;
//in flat mat3 pixel_TBN;
in smooth mat3 pixel_TBN;
uniform sampler2D txr_texture0;
uniform sampler2D txr_texture1;
uniform sampler2D txr_normal;
uniform sampler2D txr_specular;
uniform sampler2D txr_light;
uniform samplerCube txr_skybox;
const int _lights=3;
uniform vec3 light_col0=vec3(0.1,0.1,0.1);
uniform vec3 light_dir[_lights]= // direction to local star in ellipsoid space
{
vec3(0.0,0.0,+1.0),
vec3(0.0,0.0,+1.0),
vec3(0.0,0.0,+1.0),
};
uniform vec3 light_col[_lights]= // local star color * visual intensity
{
vec3(1.0,0.0,0.0),
vec3(0.0,1.0,0.0),
vec3(0.0,0.0,1.0),
};
out layout(location=0) vec4 frag_col;
const vec4 v05=vec4(0.5,0.5,0.5,0.5);
const bool _blend=false;
const bool _reflect=true;
//------------------------------------------------------------------
void main(void)
{
float a=0.0,b,li;
vec4 col,blend0,blend1,specul,skybox;
vec3 normal;
col=(texture2D(txr_normal,pixel_txr.st)-v05)*2.0; // normal/bump maping
// normal=pixel_TBN*col.xyz;
normal=pixel_TBN[0];
blend0=texture(txr_texture0,pixel_txr.st);
blend1=texture(txr_texture1,pixel_txr.st);
specul=texture(txr_specular,pixel_txr.st);
skybox=texture(txr_skybox,normal);
if (_blend)
{
a=blend1.a;
blend0*=1.0-a;
blend1*=a;
blend0+=blend1;
blend0.a=a;
}
col.xyz=light_col0; col.a=0.0; li=0.0; // normal shading (aj s bump mapingom)
for (int i=0;i<_lights;i++)
{
b=dot(light_dir[i],normal.xyz);
if (b<0.0) b=0.0;
// b*=specul.r;
li+=b;
col.xyz+=light_col[i]*b;
}
col*=blend0;
if (li<=0.1)
{
blend0=texture2D(txr_light,pixel_txr.st);
blend0*=1.0-a;
blend0.a=a;
col+=blend0;
}
if (_reflect) col+=skybox*specul.r;
col*=pixel_col;
if (col.r<0.0) col.r=0.0;
if (col.g<0.0) col.g=0.0;
if (col.b<0.0) col.b=0.0;
a=0.0;
if (a<col.r) a=col.r;
if (a<col.g) a=col.g;
if (a<col.b) a=col.b;
if (a>1.0)
{
a=1.0/a;
col.r*=a;
col.g*=a;
col.b*=a;
}
frag_col=col;
}
//------------------------------------------------------------------
Estos códigos fuente son un poco antiguos y combinan diferentes cosas para una aplicación específica
Entonces extraiga solo lo que necesita de él. Si está confundido con los nombres de las variables, coménteme ...
-
tm_
significa matriz de transformación -
l2g
significa sistema de coordenadas local para transformar el sistema de coordenadas global -
dir
significa que la transformación cambia de dirección (el desplazamiento es 0,0,0) -
g2s
significa global para la pantalla ... -
per
es perspectiva transformar ...
El registro de compilación GLSL
Debe obtener su contenido programáticamente después de compilar el sombreador (¡no la aplicación!).
Lo hago llamando a la función
glGetShaderInfoLog
para cada sombreador, programa que uso ...
[Notas]
Algunos controladores optimizan las variables "no utilizadas".
Como puede ver en la imagen,
txr_texture1
no se encuentra incluso si el sombreador de fragmentos lo tiene en el código, pero la combinación no se usa en esta aplicación, por lo que el controlador la eliminó por sí sola ...
Los registros de sombreado pueden mostrarle mucho (errores de sintaxis, advertencias ...)
Hay pocos IDE GLSL para facilitar el sombreado, pero prefiero el mío porque puedo usar directamente el código de la aplicación de destino. El mío se ve así:
cada ventana txt es una fuente de sombreador (vértice, fragmento, ...) la parte inferior derecha es el portapapeles, la parte superior izquierda es el registro del sombreador después de la última compilación y la parte inferior izquierda es la vista previa. Logré codificarlo como el IDE de estilo Borland (con las teclas también y el resaltado de sintaxis). Los otros IDE que vi son similares (diferentes colores de grueso :)) de todos modos si quieres jugar con la aplicación de descarga del sombreador o hacerlo tú mismo ayudará mucho ...
También podría haber un problema con la creación de TBN
Debe verificar visualmente si los vectores TBN (tangente, binormal, normal) corresponden a la superficie del objeto dibujando líneas de colores en cada posición de vértice. Solo para estar seguro ... algo como esto: