使用具有世界空间法线的切线空间法线贴图

Posted

技术标签:

【中文标题】使用具有世界空间法线的切线空间法线贴图【英文标题】:Using a tangent-space normal map with world-space normals 【发布时间】:2014-06-15 19:41:56 【问题描述】:

我正在研究 GLSL 中的延迟着色。我在世界空间中拥有所有模型法线,以及位置。整个事情与多边形的世界空间法线完全一样,但我不确定如何添加法线贴图。我知道建议在视图空间中进行照明,并且我已经尝试过,但我什至无法达到世界空间中的四分之一。我现在剩下要做的就是添加法线。我有一个常规的法线贴图,它位于切线空间中。我尝试了以下方法:

    将世界空间法线乘以法线贴图似乎不起作用。

    我尝试添加法线贴图的法线,尽管我知道这行不通。

在这一点上,我不确定还有什么可以尝试的。有什么简单的方法可以做到这一点?

法线渲染片段着色器:

#ifdef NORMALMAP
uniform sampler2D m_Normals;
#endif

varying vec3 normal;
varying vec2 texCoord;

void main() 
    vec3 normals;
    #ifdef NORMALMAP
        normals = normalize(normal * (texture(m_Normals, texCoord).xyz));
    #else
        normals = normal;
    #endif
    gl_FragColor = vec4(normals, 1.0);

变量“normal”是从顶点着色器传入的世界空间法线。

【问题讨论】:

为什么要将法线乘以法线贴图中的值?法线贴图为您提供法线。您真正想要乘以它的唯一东西是将其转换为不同的坐标空间(例如切线->世界)的矩阵。当然,根据您的问题,您的法线贴图已经在世界空间中,因此它们不应该(并且不能合理地)被转换;在世界空间中进行照明以使事情变得更简单。 我正在世界空间中进行照明。那就是问题所在。我的代码从世界空间中的深度缓冲区重建位置。我的灯位都在正常空间。正如我所说,在我添加法线贴图之前它工作得很好,因为法线贴图和所有的一样都在切线空间中。我有多边形的 WORLD-SPACE 法线,但有 TANGENT-SPACE 法线贴图。我的问题是如何将切线转换为世界。抱歉,标题有点不清楚。 【参考方案1】:

我的灯位都在正常空间内。

老实说,我不知道“正常空间”是什么意思。数学中有“正常空间”,但那是完全不相关的,也没有称为“正常空间”的标准坐标空间。

我的问题是如何将切线转换为世界。

你的多边形的世界空间法线不足以解决这个问题(事实上,你需要一个每个顶点的法线)。要从切线空间转换到对象空间,您需要三件事:

    您的对象空间法线向量 表示纹理坐标变化的向量 表示纹理 t 坐标变化的向量

这三个东西一起形成了切线空间 -> 基矩阵的对象空间变化(通常称为TBN,因为向量分别称为Tangent、Bitangent和正常)。

但是,您不希望在这个问题中使用对象空间法线,因此您需要将该矩阵乘以模型矩阵,以便它跳过对象空间并直接进入世界空间。同样,如果您的法线向量已经在世界空间中,则需要将其转换回对象空间。在这种情况下,对象空间和世界空间可能是相同的,特别是因为我们在这里只处理一个 3x3 矩阵(没有平移)。但从你给出的描述我无法确定。

最后要注意的是,在这种情况下,您需要传统 TBN 矩阵的逆矩阵。传统上,它用于将向量转入切线空间,但您希望将它们转出。旋转矩阵的逆矩阵与其转置相同,因此您真正需要做的就是将这 3 个向量放在行而不是列中1

1假设您的 T、B 和 N 向量成直角。不一定是这种情况,因为它们是从您的纹理坐标派生的。这一切都将归结为您用于计算切线和双切线向量的内容;很多软件会为你重新正交化它们。

【讨论】:

我可以轻松获得对象空间法线(我在顶点着色器上将它们转换为世界,因此只需将其取出即可),并且我的顶点着色器中有一个 TBN 矩阵。老实说,我不知道我是如何不考虑简单地转置该矩阵的。我现在觉得自己很傻。谢谢! 好的,根据您的新信息,我认为您想要的是:normals = normalize (TBN * texture (m_Normals, texCoord).xyz) * vec3 (0.5) + vec3 (0.5); 最后一部分 (* 0.5 + 0.5) 可能需要也可能不需要,具体取决于您的纹理类型存储你的法线 - 如果它是浮点或 SNORM,你将不需要它,但如果它只是 GL_RGB 你会(并且你需要 * 2 - 1 在你的光照通道中解包它)。这是因为您必须处理具有负值的法线。 使用那里的确切代码,它给了我完美的法线 - 在视图空间中?在我最后的延迟着色着色器中,我有相机的逆 WorldViewMatrix,我已经成功地使用它来将视图内容转换为世界内容。我可以在最终的着色过程中简单地调用这样的东西吗? m_InverseViewMatrix * vec4(normals, 0.0) 将其移动到世界空间?还是有一个很大的禁忌我错过了涉及用这样的向量翻译法线? 你不会想要那样做的。您应该首先在世界空间中输出法线。顺便说一句,我实际上忘记了该代码中的一个步骤(法线当前在 object-space 中),那就是将其转换为 world-space。你应该考虑normals = normalize (Model * TBN * texture (m_Normals, texCoord).xyz) * vec3 (0.5) + vec3 (0.5)。或者你必须从对象空间转换到世界空间的矩阵被称为(我假设Model,但有些人称之为World)。 它给我的法线不在世界空间中。当我移动相机时,面向我的法线变为蓝色(z-forward),面向“向上”的法线变为绿色,依此类推,而不是保持静止。我可能会在这个照明系统上完全重新启动并再给视图空间一个机会,因为这样看起来事情会更容易一些。

以上是关于使用具有世界空间法线的切线空间法线贴图的主要内容,如果未能解决你的问题,请参考以下文章

切线空间法线贴图 - 着色器完整性检查

shader数学基础之法线贴图切线空间

shader数学基础之法线贴图切线空间

带法线贴图的高光着色

带你玩转模型法线,实验一下大胆的想法(法线贴图 | shader | Unity | python | 爬虫)

带你玩转模型法线,实验一下大胆的想法(法线贴图 | shader | Unity | python | 爬虫)