验证顶点着色器中变换矩阵的使用。正确性或法线变换

Posted

技术标签:

【中文标题】验证顶点着色器中变换矩阵的使用。正确性或法线变换【英文标题】:Verification of transformation matrix usage in vertex shader. Correctness or normals transformation 【发布时间】:2018-02-09 22:48:24 【问题描述】:

我需要能够根据变换矩阵修改顶点坐标,但我有逐顶点光照,所以我不确定我的方法是否适用于法线:

#version 120
uniform mat4 transformationMatrix;
void main() 
    vec3 normal, lightDir;
    vec4 diffuse, ambient, globalAmbient;
    float NdotL;
    // Transformation part
    normal = gl_NormalMatrix * gl_Normal * transpose(mat3(transformationMatrix));
    gl_Position = gl_ModelViewProjectionMatrix * transformationMatrix * gl_Vertex;
    // Calculate color
    lightDir = normalize(vec3(gl_LightSource[0].position));
    NdotL = max(abs(dot(normal, lightDir)), 0.0);
    diffuse = gl_Color * gl_LightSource[0].diffuse;
    ambient = gl_Color * gl_LightSource[0].ambient;
    globalAmbient = gl_LightModel.ambient * gl_Color;
    gl_FrontColor =  NdotL * diffuse + globalAmbient + ambient;
  

我在第 8-9 行执行所有转换。 您能评论一下这是否正确吗?

【问题讨论】:

【参考方案1】:

如果你想创建一个普通矩阵,那么你必须使用左上角3*3的inversetranspose,这个4*4的矩阵。

见Why transforming normals with the transpose of the inverse of the modelview matrix? 和Why is the transposed inverse of the model view matrix used to transform the normal vectors?

这意味着您必须像这样编写代码:

normal = gl_NormalMatrix * transpose(inverse(mat3(transformationMatrix))) * gl_Normal;

但是,如果一个向量从左边乘以一个矩阵,结果相当于从右边乘一个列向量到转置矩阵。

见GLSL Programming/Vector and Matrix Operations

这意味着你可以这样写代码,避免transpose操作:

normal = gl_NormalMatrix * (gl_Normal * inverse(mat3(transformationMatrix)));

如果4*4矩阵transformationMatrix是Orthogonal matrix,这意味着X、Y、Z轴是Orthonormal(单位向量,相互垂直),那么使用上面的就足够了左 3*3。在这种情况下,逆矩阵等于转置矩阵。

见In which cases is the inverse matrix equal to the transpose?

这将简化您的代码:

normal = gl_NormalMatrix * mat3(transformationMatrix) * gl_Normal;

当然也可以这样表达:

normal = gl_NormalMatrix * (gl_Normal * transpose(mat3(transformationMatrix)));

请注意,这与您在代码中所做的不同,因为* 操作是从左到右处理的(请参阅GLSL - The OpenGL Shading Language 4.6, 5.1 Operators, page 97),结果是

vec3 v;
mat3 m1, m2;

(m1 * v) * m2

不等于

m1 * (v * m2);

【讨论】:

谢谢。我的变换矩阵不应该是正交的,所以我应该使用你的第二个解决方案。我阅读了提供的链接,我有最后一个问题:如果我的法线被规范化,似乎一些转换可能会破坏这种规范化,所以我需要 normal = normalize ( gl_NormalMatrix * gl_Normal * inverse(mat3(transformationMatrix))); ? @zlon 如果矩阵不是正交矩阵,则矩阵变换会丢失法线向量的归一化,您必须在变换后对其进行归一化。【参考方案2】:

正常的变换看起来不正确。

由于 v * transpose(M) 与 M * v 完全相同,因此您根本没有对非均匀缩放进行任何特殊情况处理。

您正在寻找的很可能是使用反转置矩阵:

normal = gl_NormalMatrix * transpose(inverse(mat3(transformationMatrix))) * gl_Normal;

有关这背后的数学的更多详细信息,请查看this。

【讨论】:

以上是关于验证顶点着色器中变换矩阵的使用。正确性或法线变换的主要内容,如果未能解决你的问题,请参考以下文章

Unity Shader 获取深度纹理和法线纹理

unity_ObjectToWorld 怎么用

法线变换需要右乘矩阵,顶点变换需要左乘矩阵

法线变换需要右乘矩阵,顶点变换需要左乘矩阵

最简单光照模型中的光照方向及其变换

顶点着色器 学习笔记