【问题标题】:normal mapping, TBN matrix calculation法线映射,TBN矩阵计算
【发布时间】:2013-03-10 16:07:24
【问题描述】:

我只是想确保我正确理解 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);

据我了解,tbn 矩阵将向量从切线空间转换为眼睛空间。实际上我们想要反向 - 将向量从眼睛空间转换到切线空间。因此我们需要反转tbn矩阵:

tbn = transpose(tbn); // transpose should be OK here for doing matrix inversion

注意: tbn - 应该只包含旋转,对于这种情况我们可以使用转置来反转矩阵。

我们可以转换我们的向量:

vec3 lightT  = tbn * light_vector;
...          = tbn * ...

在几个教程中,我发现作者使用了这样的源代码:

light.x = dot(light, t);
light.y = dot(light, b);
light.z = dot(light, n);

上述代码的作用与乘以transposed(tbn) 矩阵相同。

问题:

我们应该像我上面解释的那样使用转置的tbn 矩阵吗?或者也许我错过了什么?

注意通过该解决方案,我们在顶点着色器中将向量 (light_vector) 转换为 TBN,然后在片段着色器中我们只需从法线贴图获取法线。其他选择是创建 TBN 矩阵,将 TBN 空间转换为眼睛空间,然后在片段着色器中转换从法线贴图读取的每个法线。

【问题讨论】:

  • 定义“正确”?你把你的法线和切线向量的叉积得到你的双切线向量。一般来说,这对于大多数表面是正确的。它可能对你来说是正确的,但大多数纹理映射不使用完全正交的映射到表面。
  • 对,我已经更新了问题。我也改变了双切线计算。

标签: opengl glsl


【解决方案1】:

转置不是矩阵求逆!!!

我在顶点着色器中的 TBN 矩阵如下所示:

uniform mat4x4 tm_l2g_dir;
layout(location=3) in vec3 tan;
layout(location=4) in vec3 bin;
layout(location=5) in vec3 nor;
out smooth mat3 pixel_TBN;

void main()
{
    vec4 p;
    //...
    p.xyz=tan.xyz; p.w=1.0; pixel_TBN[0]=normalize((tm_l2g_dir*p).xyz);
    p.xyz=bin.xyz; p.w=1.0; pixel_TBN[1]=normalize((tm_l2g_dir*p).xyz);
    p.xyz=nor.xyz; p.w=1.0; pixel_TBN[2]=normalize((tm_l2g_dir*p).xyz);
    //...
}

地点:

  • tm_l2g_dir 是从局部模型空间到全局场景空间的转换矩阵,对您的代码没有任何移动(仅改变方向),它是您的法线矩阵
  • tan、bin 和 TBN 矩阵的向量也不是(作为我的模型的一部分)

nor - 是从实际顶点位置到曲面的法线向量(可以计算为从该顶点开始的两个顶点的向量相乘)

tan,bin 是垂直向量,通常平行于纹理映射轴或模型的细分。如果您选择错误的 tan/bin 向量,有时可能会出现一些照明伪影。例如,如果您有圆柱体,那么 bin 是它的旋转轴,而 tan 沿着圆(切线)垂直于它

tan,bin,nor 不应该互相垂直

您也可以自动计算 TBN,但这会导致一些伪影。要做到这一点,您只需选择一个用于正常计算的顶点作为 tan 向量和 bin=nor x bin

在片段着色器中,我使用带有法线贴图的 pixel_TBN 来计算片段的真实法线

//------------------------------------------------------------------
#version 420 core
//------------------------------------------------------------------
in smooth vec2 pixel_txr;
in smooth mat3 pixel_TBN;
uniform sampler2D   txr_normal;
out layout(location=0) vec4 frag_col;
const vec4 v05=vec4(0.5,0.5,0.5,0.5);
//------------------------------------------------------------------
void main(void)
    {
    vec4 col;
    vec3 normal;
    col=(texture2D(txr_normal,pixel_txr.st)-v05)*2.0;       // normal/bump maping
    normal=pixel_TBN*col.xyz;
    // col=... other stuff to compute col

    frag_col=col;
    }
//------------------------------------------------------------------

【讨论】:

  • 你如何处理片段着色器中的 pixel_TBN?据我了解,您将 tan、bin 或从 Tangent Space 转换为您的全局场景空间。对于仅包含旋转的矩阵,您可以使用转置进行反转。
  • 刚刚为您编辑了简单的部分片段。使用每个片段法线,您可以做任何事情(任何照明模型,从立方体贴图添加铬反射......)
  • 正交 3x3 的转置也是相反的。也许这就是原始海报所指的?
  • 我再次编辑了我的帖子...在片段着色器中,我看到您通过这个 TBN 矩阵从法线贴图转换每个法线。我的解决方案不需要这样:我不转换所有法线,而是只转换顶点着色器中的光向量。
  • 是的,我知道,...但是我在很多事情上都使用法线,不仅是为了照明,因此在世界(场景)空间中每个片段都具有法线被证明非常有用,甚至更简单/更快/没有你的情况那么抽象。当然,如果您不添加除凹凸/法线贴图以外的任何其他东西,那么它就是多余的。附言向顶点着色器添加东西远不如片段着色器中的性能杀手
猜你喜欢
  • 1970-01-01
  • 2015-08-04
  • 2011-05-08
  • 1970-01-01
  • 2018-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多