OpenGL顶点着色器的NormalMatrix?

Posted

技术标签:

【中文标题】OpenGL顶点着色器的NormalMatrix?【英文标题】:NormalMatrix for OpenGL vertex shader? 【发布时间】:2013-10-30 20:51:36 【问题描述】:

我正在使用 Assimp 导入 3d 模型,它为我提供了法线贴图的切线和双切线,但是我无法弄清楚在顶点着色器中用于将切线和双切线向量相乘的矩阵是什么样的- 是 WorldViewProjection 矩阵还是其他一些特殊用途的“正常矩阵”?

更新:我的顶点着色器

const std::string gVertexShader =   "#version 330                                                                                       \n \
                                                                                                                \n \
                                        layout(std140) uniform;                                                 \n \
                                                                                                                \n \
                                        uniform UnifTransform                                                   \n \
                                                                                                               \n \
                                            mat4 mWVPMatrix;                                                    \n \
                                            mat4 mWorldMatrix;                                                  \n \
                                            float mTextureTilingFactor;                                         \n \
                                         Transform;                                                            \n \
                                                                                                                \n \
                                        layout(location = 0) in vec3 vert_position;                                             \n \
                                        layout(location = 1) in vec3 vert_normal;                                                   \n \
                                        layout(location = 2) in vec2 vert_texcoord;                                                 \n \
                                        layout(location = 3) in vec3 vert_tangent;                                                 \n \
                                        layout(location = 4) in vec3 vert_bitangent;                                                 \n \
                                                                                                                \n \
                                        out vec3 frag_position;                                                 \n \
                                        out vec3 frag_normal;                                                   \n \
                                        out vec2 frag_texcoord;                                                 \n \
                                        out vec3 frag_tangent;                                                 \n \
                                        out vec3 frag_bitangent;                                                 \n \
                                                                                                                \n \
                                        void main()                                                                                                 \n \
                                                                                                                                                           \n \
                                            gl_Position   = Transform.mWVPMatrix * vec4(vert_position, 1.0);    \n \
                                                                                                                \n \
                                            vec4 position = Transform.mWorldMatrix * vec4(vert_position, 1.0);  \n \
                                            vec4 normal   = Transform.mWorldMatrix * vec4(vert_normal, 0.0);    \n \
                                            vec4 tangent = Transform.mWorldMatrix * vec4(vert_tangent, 0.0);   // correct matrix?   \n \
                                            vec4 bitangent = Transform.mWorldMatrix * vec4(vert_bitangent, 0.0);    \n \
                                                                                                                \n \
                                            frag_position = position.xyz;                                       \n \
                                            frag_normal   = normal.xyz;                                         \n \
                                            frag_tangent   = tangent.xyz;                                         \n \
                                            frag_bitangent = bitangent.xyz;                                         \n \
                                            frag_texcoord = Transform.mTextureTilingFactor * vert_texcoord;     \n \

【问题讨论】:

【参考方案1】:

TBN (Tangent, Bitangent, Normal) 矩阵 (3x3) 通常用于从切线空间到对象空间。你可以乘以你的法线来变换它们。请注意,法线本质上是有方向的,因此与您通常处理的大多数转换矩阵不同,您只需要一个 vec3 和 3x3 矩阵来执行此操作;在我看来,这就是使这个特殊矩阵“特别”的原因。根据您使用此矩阵的方式,您可以将光矢量转换为切线空间,或将法线贴图转换为对象空间。

通常,您不会单独将 Tangent、Bitangent 和 Normal 向量相乘。它们定义了你的向量空间基础,应该不理会。用于进出这个向量空间的矩阵就是从这三个向量导出的。


您将通过执行以下操作创建矩阵以从切线空间转换到对象空间(伪代码):

mat3 (Tangent, Bitangent, Normal);

当然,您可以使用逆从对象空间到切线空间。 Assimp 可能会生成(大致)正交基向量,因此可以保证逆存在并且在这种情况下可以表示为转置。

切线和双切线保证是正交的,因为可能存在纹理剪切,而且两者都不一定垂直于法线。一些模型导入软件在输出 TBN 向量时将正交化作为后处理步骤,大多数教程似乎假设这种关系总是存在。

【讨论】:

这个矩阵不是用在片段着色器中了吗?我指的是顶点着色器中的那个,更新的问题 @KaiserJohaan:不一定。您可以在片段着色器中使用它来将采样的切线空间法线转换为对象空间,这在延迟着色中很常见。您还可以使用它将光照方向向量转换为顶点着色器中的切线空间——这在前向着色引擎中非常有用,因为它降低了片段着色器的复杂性。然而,在所有情况下,您都不需要转换每个顶点的法线、切线或双切线。您可能更改 TBN 矩阵以使其在切线空间中变为视图空间,但是(对象空间通常没有用)。 @KaiserJohaan:您能否明确说明您是如何在片段着色器中使用切线、双切线和法线向量的?我现在从您更新的问题中得到的印象是,您正在将采样的切线空间法线转换为不同的向量空间(在这种情况下为世界空间)。【参考方案2】:

切线和双切线(也称为副法线)遵循与法线相同的规则。事实上,切线、副法线和法线构成了曲面局部空间的基础,可以用 3×3 矩阵表示。这个空间是通过模型视图矩阵(也称为法线矩阵)的逆转置来变换的。

【讨论】:

以上是关于OpenGL顶点着色器的NormalMatrix?的主要内容,如果未能解决你的问题,请参考以下文章

跨多个着色器的 OpenGL 统一

将属性传递给 OpenGL 顶点着色器的行为很奇怪

OpenGL学习随笔-- 顶点着色器(VertexShader)

顶点着色器的绘制操作

openGL:带着色器的线条

OpenGL ES(一.概念)