法线贴图,如何变换矢量

时间:2013-08-09 08:27:48

标签: opengl glsl shader normals

在顶点着色器中,我们通常创建TBN矩阵:

vec3 n = normalize(gl_NormalMatrix * gl_Normal);
vec3 t = normalize(gl_NormalMatrix * Tangent.xyz);
vec3 b = normalize(gl_NormalMatrix * Bitangent.xyz);
mat3 tbn = mat3(t, b, n);

此矩阵将Tangent空间中的顶点变换为Eye / Camera空间。

现在进行法线贴图(在前向渲染中完成),我们有两个选择:

  1. 反向tbn矩阵并转换light_vectorview_direction并将这些向量发送到片段着色器。之后,这些向量处于切线空间。
    • 在片段着色器中我们只需要从法线贴图中读取法线。由于此类法线位于切线空间(按“定义”),因此它们与已转换的light_vectorview_direction匹配。
    • 这样我们在Tangent空间进行光计算。
  2. tbn矩阵传递给片段着色器,然后通过它转换法线贴图中的每个法线读数。这样我们就可以将这种法线转换为View空间。
    • 这样我们在眼睛/相机空间进行光照计算
  3. 选项 1 似乎更快:我们在顶点着色器中进行了大多数变换,只有一次从法线贴图中读取。

    选项 2 需要通过TBN矩阵从法线贴图转换每个法线。但似乎有点简单。

    问题:

    哪个选项更好?

    是否有任何性能损失? (也许纹理读取将“覆盖”进行矩阵变换的成本)

    更经常使用哪个选项?

1 个答案:

答案 0 :(得分:3)

我现在会告诉你很多 - 根据你的申请,选项1甚至可能都不可能。

在延迟着色图形引擎中,您必须在片段着色器中计算光矢量,这将排除选项1.当在延迟着色中进行照明时,您无法保持TBN矩阵,所以你会在构建普通G缓冲区之前,将法线转换为世界空间或视图空间(这不再经常受到欢迎)(TBN矩阵的计算可以在顶点着色器中完成并传递给片段着色器为flat mat3)。然后使用基础对法线贴图进行采样,并将其写入世界空间。

我可以从经验告诉你,大名称图形引擎(例如虚幻引擎4,CryEngine 3等)现在实际上在世界空间中进行照明。它们也使用延迟着色,因此对于这些引擎,根本没有使用上面建议的选项:)


顺便说一下,如果实际存储的是法线,副法线和切线向量,则会在顶点缓冲区中浪费空间。它们是正交向量空间的基础向量,因此它们都是直角。因此,您可以通过采用叉积来计算给定任意两个的第三个向量。此外,由于它们是直角并且应该已经标准化,因此您不需要对叉积的结果进行标准化(回想一下| a x b | = | a | * | b | * sin(a,b))。因此,这应该在您的顶点着色器中足够了:


   // Normal and tangent should be orthogonal, so the cross-product
   //   is also normalized - no need to re-normalize.
   vec3 binormal = cross (normal,  tangent);
        TBN      = mat3  (tangent, binormal, normal);

这将让你走向世界空间法线(这些日子对于许多流行的后处理效果往往更有效)。如果您打算更改矩阵以产生视图空间法线,则可能需要对矩阵进行重新规范化。