El mapeo normal salió horriblemente mal

Intenté implementar un mapeo normal en mi aplicación opengl, pero no puedo hacerlo funcionar.

Este es el mapa difuso (al que le agrego un color marrón) y este es el mapa normal.

Para obtener los vectores tangente y bitantario (en otros lugares llamados binormals?), 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 &vTangents, std::vector &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 subir 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); } 

Fragmento shader:

 #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) resultados horribles

¿Qué estoy haciendo mal aquí?

Apuesto a la configuración / mezcla de color en el sombreador de fragmentos …

  1. 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 controlador …

  2. está agregando color e intensities lugar de intensities de color*intensity

    pero podría pasar por alto algo.

  3. intente solo sombrear normal / golpe al principio

    Ignore el ambiente, refleje, especular … y luego, si funciona, agregue el rest uno por uno. Siempre revise los registros de comstackción del sombreador

Demasiado perezoso para analizar aún más tu código, así que aquí es cómo lo hago:

ejemplo de mapeo de relieve

El tamaño de la izquierda es un objeto de nave espacial (similar al Viper de ZXS Elite) renderizado con función fija. El lado derecho es el mismo (rotación de objeto un poco diferente) con el sombreador GLSL en su lugar y este mapa normal / de relieve

enter image description here

[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; pw=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; pw=1.0; pixel_TBN[0]=normalize((tm_l2g_dir*p).xyz); p.xyz=bin.xyz; pw=1.0; pixel_TBN[1]=normalize((tm_l2g_dir*p).xyz); p.xyz=nor.xyz; pw=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 (a1.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 una mezcla de cosas diferentes para aplicaciones específicas

Así que extrae solo lo que necesites de él. Si te confunden con los nombres de las variables, entonces comenten conmigo …

  • 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 solo la dirección (el desplazamiento es 0,0,0)
  • g2s significa global para pantalla …
  • es una transformación de perspectiva …

El registro de comstackción GLSL

Tienes que obtener su contenido programáticamente después de la comstackción del shader (¡no de la aplicación!). Lo hago llamando a la función glGetShaderInfoLog para cada shader, progtwig que uso …

[Notas]

Algunos controladores optimizan las variables “sin usar”. 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 mezcla no se usa en esta aplicación, por lo que el controlador la eliminó por sí solo …

Los registros de Shader pueden mostrarle mucho (errores de syntax, advertencias …)

hay pocos IDEs de GLSL para facilitar el sombreado, pero prefiero el mío porque puedo usar el código de la aplicación de destino directamente. El mío se ve así:

GLSL IDE

cada ventana de txt es una fuente de sombreado (vértice, fragmento, …) la parte inferior derecha es portapapeles, la parte superior izquierda es el registro de sombreado después de la última comstackción y la parte inferior izquierda es la vista previa. Logré codificarlo como estilo IDE de Borland (con las teclas también y resaltado de syntax) los otros IDE que vi parecen similares (diferentes colores de grueso :)) de todas formas si quieres jugar con la aplicación de Shader para descargar dicha aplicación o hazlo tú mismo ayudará mucho …

También podría haber un problema con la creación de TBN

Debería verificar visualmente si los vectores TBN (tangente, binormal, normal) corresponden a la superficie del objeto dibujando líneas de color en cada posición de vértice. Solo para estar seguro … algo como esto:

normales

Trataré de hacer que tu código funcione. ¿Lo has probado con la cámara en movimiento?

No puedo ver en ninguna parte que haya transformado el TBNMatrix con las matrices de transformación, vista y modelo. ¿Lo intentó 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 estas 3 matrices deberían usarse:

 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 lo mismo porque la multiplicación de la matriz 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 , tienes que 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 lo que sucede con la tangente y el bitangent, pero de esta manera lo normal se mantendrá perpendicular a ellos. Es fácil de probar. Aquí uso un ° b como producto skalar de los vectores ayb. Así que n sea algo normal, y a es algún vektor en la superficie (por ejemplo, {bi} tangente, borde de un triángulo), y que A sea una 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, entonces tenemos que transformarlo con la transposición inversa de la matriz. Sin embargo, la normal debe tener una longitud de 1, por lo que después de las transformaciones, debe normalizarse.

También puede obtener normal perpendicular al hacer esto:

 vec3 normal = normalize(cross(tangent, bitangent)); 

Donde cross (a, b) es la función que calcula el producto cruzado de a y b, que es siempre perpendicular a a y b.

Lo siento por mi ingles 🙂