OpenGL.Shader: 8-Learning Light-Normal Matrix
El artículo anterior registró el conocimiento básico de los vectores normales de iluminación y principios simples de cálculo de iluminación. Se puede ver en las representaciones que la fuente de luz cambia con la posición de la cámara, y cada lado del cubo presenta diferentes efectos oscuros. ¿Qué pasa si el modelo cambia si la posición de la fuente de luz no cambia? De acuerdo con lo que hemos aprendido antes, la posición del modelo cambia y necesita ser multiplicada por la matriz mvp, entonces, ¿se puede multiplicar el vector normal por la matriz mvp?
Demostración de errores, use directamente mvp como matriz normal
#version 320 es
uniform mat4 _mvp;
uniform mat3 _normalMatrix;
uniform vec3 _lightDir;
uniform vec3 _lightColor;
uniform vec3 _lightDiffuse;
in vec3 _position;
in vec3 _normal;
in vec2 _uv;
out vec2 _outUV;
out vec4 _outComposeColor;
void main()
{
_outUV = _uv;
vec3 normal = normalize(_normalMatrix * _normal); //输入法线*法线矩阵 再归一化
float lightStrength = max(dot(normal, -_lightDir), 0.0);
_outComposeColor = vec4(_lightColor * lightStrength + _lightDiffuse, 1);
gl_Position = _mvp * vec4(_position,1.0);
}
#version 320 es
precision mediump float;
in vec4 _outComposeColor;
in vec2 _outUV;
uniform sampler2D _texture;
out vec4 _fragColor;
void main()
{
vec4 color = texture(_texture,_outUV);
_fragColor = color * _outComposeColor;
}
Primero pegamos el grupo de programas de sombreado usado, introducimos uniform mat3 _normalMatrix; la matriz normal usada para cambiar el vector normal, normalizamos (_normalMatrix * _normal); obtenemos el vector normal cambiado y luego realizamos el cálculo de iluminación.
void render(Camera3D& camera)
{
sprogram.begin();
static float angle = 0;
angle += 0.3f;
CELL::matrix4 matRot;
matRot.rotateYXZ(angle, 0.0f, 0.0f);
// 这里的模型矩阵只进行简单进行旋转操作。
CELL::matrix4 model = mModelMatrix * matRot; //mModelMatrix只是一个简单的单位矩阵
CELL::matrix4 vp = camera.getProject() * camera.getView();
CELL::matrix4 mvp = (vp * model);
glUniformMatrix4fv(sprogram._mvp, 1, GL_FALSE, mvp.data());
// 设置法线矩阵 为 mvp
glUniformMatrix3fv(sprogram._normalMatrix, 1, GL_FALSE, mat4_to_mat3(mvp).data());
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, mCubeSurfaceTexId);
glUniform1i(sprogram._texture, 0);
glUniform3f(sprogram._lightDiffuse, 0.1f, 0.1f, 0.1f); // 漫反射 环境光
glUniform3f(sprogram._lightColor, 1.0f, 1.0f, 1.0f); // 定向光源的颜色
glUniform3f(sprogram._lightDir, // 定向光源的方向
static_cast<GLfloat>(camera._dir.x),
static_cast<GLfloat>(camera._dir.y),
static_cast<GLfloat>(camera._dir.z));
glVertexAttribPointer(static_cast<GLuint>(sprogram._position), 3, GL_FLOAT, GL_FALSE,
sizeof(CubeIlluminate::V3N3T2), &_data[0].x);
glVertexAttribPointer(static_cast<GLuint>(sprogram._normal), 3, GL_FLOAT, GL_FALSE,
sizeof(CubeIlluminate::V3N3T2), &_data[0].nx);
glVertexAttribPointer(static_cast<GLuint>(sprogram._uv), 2, GL_FLOAT, GL_FALSE,
sizeof(CubeIlluminate::V3N3T2), &_data[0].u);
glDrawArrays(GL_TRIANGLES, 0, 36);
sprogram.end();
}
//-------------------------------
tmat3x3<T> mat4_to_mat3(const tmat4x4<T> & m)
{
return tmat3x3<T>(
tvec3<T>(m[0][0],m[0][1],m[0][2])
,tvec3<T>(m[1][0],m[1][1],m[1][2])
,tvec3<T>(m[2][0],m[2][1],m[2][2]));
}
Luego pegue la configuración de los parámetros de renderizado y simplemente gire a lo largo del eje Y. Una cosa a tener en cuenta es que el vector normal es de tipo vec3, que solo representa la dirección y no tiene información de posición. mvp es una matriz de 4x4, por lo que los dos no se pueden multiplicar directamente Personalice el método mat4_to_mat3 para eliminar el componente w.
Se puede ver en el renderizado que el cálculo de iluminación es anormal después de la rotación automática. Deslice manualmente la lente para que la iluminación y el modelo parezcan normales nuevamente después de alcanzar el ángulo especificado. ¿Por qué esto es tan? Analicemos brevemente los principios matemáticos subyacentes:
En OpenGL ES 2.0, un vértice se convierte al sistema de coordenadas del ojo mediante:
vertexEyeSpace = view_Matrix * model_Matrix * vértice;
entonces, ¿por qué no podemos hacer el mismo trabajo como un vector normal? Primero, el vector normal es un vector de 3 flotadores y la matriz modelView es una matriz de 4X4. Esto se puede lograr con el siguiente código:
normalEyeSpace = vec3 (view_Matrix * model_Matrix * vec4 (normal, 0.0));
Hay un problema potencial arriba: el
vértice es (x, y, z) que representa el vector predeterminado (x, y, z, 1); y el vector normal es un vector direccional sin un punto fijo. Porque el vector normal se puede obtener restando dos vértices fijos (x1, y1, z1,1) y (x2, y2, z2,1) sobre la normal. Por tanto, desde este punto se puede ver que el vértice es diferente del vector normal, lo que resulta en una transformación invisible. El vector normal solo puede garantizar la consistencia de la dirección, pero no la consistencia de la posición.En la imagen de arriba vemos un triángulo con un vector normal y un vector plano tangente La siguiente imagen muestra la escena cuando se amplía una matriz de observación. Si todavía llamamos al código anterior:
Como se muestra arriba, la matriz de observación afecta a todos los vértices y normales, obviamente este resultado es incorrecto, las normales no son perpendiculares al plano tangente. Entonces ahora sabemos que la matriz de observación no se puede aplicar a todas las normales. Entonces, ¿qué matriz deberíamos usar para transformar el vector normal? Eche un vistazo a la solución en la siguiente tabla.
De la derivación en el cuadro de arriba, podemos ver que si queremos transformar el vector u correspondiente a la matriz A, si queremos asegurarnos de que el vector normal todavía es perpendicular al vector tangente correspondiente después de la transformación, entonces lo haremos Solo es necesario realizar una transformación de matriz inversa en el vector vertical, para que no tengamos que preocuparnos por la situación correspondiente a la imagen de arriba, y la transformación de matriz inversa es el resultado correcto que esperamos.
La conclusión final es matriz transpuesta = matriz inversa del modelo matriz transformada normal
void render(Camera3D& camera)
{
sprogram.begin();
static float angle = 0;
angle += 0.1f;
CELL::matrix4 matRot;
matRot.rotateYXZ(angle, 0.0f, 0.0f);
// 这里的模型矩阵只进行简单进行旋转操作。
CELL::matrix4 model = mModelMatrix * matRot;
// 法线矩阵 = 模型矩阵的逆矩阵的转置
CELL::matrix3 matNormal= mat4_to_mat3(model)._inverse()._transpose();
glUniformMatrix3fv(sprogram._normalMatrix, 1, GL_FALSE, matNormal.data());
CELL::matrix4 vp = camera.getProject() * camera.getView();
CELL::matrix4 mvp = (vp * model);
glUniformMatrix4fv(sprogram._mvp, 1, GL_FALSE, mvp.data());
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, mCubeSurfaceTexId);
glUniform1i(sprogram._texture, 0);
... ...
}
El efecto de actualización es el siguiente: Puede ver que el efecto de iluminación final es correcto ya sea girando el modelo o deslizando manualmente la cámara para cambiar la posición de la fuente de luz.
Enlace del proyecto de demostración https://github.com/MrZhaozhirong/NativeCppApp -> LightRenderer.cpp CubeIlluminate.hpp CubeIlluminateProgram.hpp