不使用 Tangent/Bitangent 向量的法线贴图
Normal mapping without using Tangent/Bitangent vectors
不幸的是,许多教程将 TBN 矩阵描述为任何类型的法线贴图事实上必须的,而没有过多地详细说明为什么会这样,这让我在一个特定场景中感到困惑
假设我需要在屏幕上的一个简单四边形上应用 bump/normal 映射,稍后可以通过它的普通矩阵进行转换
如果在任何变换之前 "rest position" 中的四边形表面法线正好指向正 z 方向 (opengl),仅变换您从法线纹理贴图中读取的向量与模型是否足够矩阵?
vec3 bumpnormal = texture2D(texture, Coord.xy);
bumpnormal = mat3(model) * bumpnormal; //assuming no scaling occured
我确实理解如果我们计算立方体上的凹凸法线而不考虑具有相同纹理坐标的不同面实际上有不同的方向,情况会发生怎样的变化,这让我想到了下一个问题
假设整个模型只使用一个法线贴图纹理,在模型的不同部分没有任何重复的所述纹理坐标,是否可以保存为每个存储的 tangent/bitangent 矢量的那 6 个浮点数顶点和 TBN 矩阵的计算 altogheter,同时通过简单地将 bumpnormal 转换为模型的矩阵来获得相同的结果?
如果是这样,为什么它不是首选解决方案?
http://www.thetenthplanet.de/archives/1180
vec3 perturb_normal( vec3 N, vec3 V, vec2 texcoord )
{
// assume N, the interpolated vertex normal and
// V, the view vector (vertex to eye)
vec3 map = texture2D( mapBump, texcoord ).xyz;
#ifdef WITH_NORMALMAP_UNSIGNED
map = map * 255./127. - 128./127.;
#endif
#ifdef WITH_NORMALMAP_2CHANNEL
map.z = sqrt( 1. - dot( map.xy, map.xy ) );
#endif
#ifdef WITH_NORMALMAP_GREEN_UP
map.y = -map.y;
#endif
mat3 TBN = cotangent_frame( N, -V, texcoord );
return normalize( TBN * map );
}
基本上我认为你是在描述这个方法。我同意它在大多数方面都更胜一筹。它使以后的计算更加清晰,而不是陷入混乱的 space 转换。
无需将所有内容都计算到切线的 space 中,您只需找到正确的世界 space 法线即可。这就是我在我的项目中使用的,我很高兴我找到了这个方法。
If the quad's surface normal in "rest position" before any transformation is pointing exactly in positive-z direction (opengl) isn't it sufficient to just transform the vector you read from the normal texture map with the model matrix?
没有
假设您从法线贴图中获得的值为 (1, 0, 0)。所以这意味着地图中的法线指向正确。
所以...那到底在哪里?或者更重要的是,当我们说 "right" 时,我们处于什么 space?
现在,您可能会立即想到模型 space 中的权利就是 +X。但事实是,事实并非如此。为什么?
因为你的纹理坐标。
如果您的模型-space 矩阵围绕模型-space Z 轴顺时针旋转 90 度,并且您通过该矩阵变换法线,那么您得到的法线应该从 (1, 0, 0) 到 (0, -1, 0)。这是预期的结果。
但是如果你有一个面向 +Z 的正方形,并且你将它绕 Z 轴旋转 90 度,这不应该产生与旋转 纹理坐标 相同的结果吗?毕竟,是纹理坐标定义了 U 和 V 的含义相对 与模型 space。
如果你的正方形右上纹理坐标是(1, 1),左下是(0, 0),那么纹理space中的"right"表示"right" 在模型 space 中。但是如果你改变映射,让 (1, 1) 在右下角,(0, 0) 在左上角,那么纹理 space 中的 "right" 就变成了 "down" (-Y) 模型 space.
如果忽略纹理坐标,即从模型 space 位置到纹理位置的映射,则 (1, 0, 0) 法线仍将指向模型中的 "right" space。但是您的纹理映射表示它应该在模型 space 中指向下方 (0, -1, 0)。就像你旋转模型 space 本身一样。
使用切线-space 法线贴图,存储在纹理中的法线与纹理映射到表面的方式相关。定义从模型 space 到切线 space(纹理映射的 space)的映射是 TBN 矩阵的用途。
随着对象和法线之间的映射变得更加复杂,这会变得更加复杂。对于四边形的情况,您可以伪造它,但对于一般图形,它需要是算法的。毕竟,映射不是恒定的。它涉及拉伸和倾斜,因为不同的三角形使用不同的纹理坐标。
现在,有 object-space 法线贴图,它生成明确在模型 space 中的法线。这些避免了对切线-space 基矩阵的需要。但它将法线贴图与其使用的对象密切相关。你甚至不能做基本的纹理坐标动画,更不用说允许法线贴图用于两个单独的对象了。如果你正在做骨骼重量蒙皮,它们几乎是行不通的,因为三角形经常改变大小。
不幸的是,许多教程将 TBN 矩阵描述为任何类型的法线贴图事实上必须的,而没有过多地详细说明为什么会这样,这让我在一个特定场景中感到困惑
假设我需要在屏幕上的一个简单四边形上应用 bump/normal 映射,稍后可以通过它的普通矩阵进行转换
如果在任何变换之前 "rest position" 中的四边形表面法线正好指向正 z 方向 (opengl),仅变换您从法线纹理贴图中读取的向量与模型是否足够矩阵?
vec3 bumpnormal = texture2D(texture, Coord.xy);
bumpnormal = mat3(model) * bumpnormal; //assuming no scaling occured
我确实理解如果我们计算立方体上的凹凸法线而不考虑具有相同纹理坐标的不同面实际上有不同的方向,情况会发生怎样的变化,这让我想到了下一个问题
假设整个模型只使用一个法线贴图纹理,在模型的不同部分没有任何重复的所述纹理坐标,是否可以保存为每个存储的 tangent/bitangent 矢量的那 6 个浮点数顶点和 TBN 矩阵的计算 altogheter,同时通过简单地将 bumpnormal 转换为模型的矩阵来获得相同的结果? 如果是这样,为什么它不是首选解决方案?
http://www.thetenthplanet.de/archives/1180
vec3 perturb_normal( vec3 N, vec3 V, vec2 texcoord )
{
// assume N, the interpolated vertex normal and
// V, the view vector (vertex to eye)
vec3 map = texture2D( mapBump, texcoord ).xyz;
#ifdef WITH_NORMALMAP_UNSIGNED
map = map * 255./127. - 128./127.;
#endif
#ifdef WITH_NORMALMAP_2CHANNEL
map.z = sqrt( 1. - dot( map.xy, map.xy ) );
#endif
#ifdef WITH_NORMALMAP_GREEN_UP
map.y = -map.y;
#endif
mat3 TBN = cotangent_frame( N, -V, texcoord );
return normalize( TBN * map );
}
基本上我认为你是在描述这个方法。我同意它在大多数方面都更胜一筹。它使以后的计算更加清晰,而不是陷入混乱的 space 转换。
无需将所有内容都计算到切线的 space 中,您只需找到正确的世界 space 法线即可。这就是我在我的项目中使用的,我很高兴我找到了这个方法。
If the quad's surface normal in "rest position" before any transformation is pointing exactly in positive-z direction (opengl) isn't it sufficient to just transform the vector you read from the normal texture map with the model matrix?
没有
假设您从法线贴图中获得的值为 (1, 0, 0)。所以这意味着地图中的法线指向正确。
所以...那到底在哪里?或者更重要的是,当我们说 "right" 时,我们处于什么 space?
现在,您可能会立即想到模型 space 中的权利就是 +X。但事实是,事实并非如此。为什么?
因为你的纹理坐标。
如果您的模型-space 矩阵围绕模型-space Z 轴顺时针旋转 90 度,并且您通过该矩阵变换法线,那么您得到的法线应该从 (1, 0, 0) 到 (0, -1, 0)。这是预期的结果。
但是如果你有一个面向 +Z 的正方形,并且你将它绕 Z 轴旋转 90 度,这不应该产生与旋转 纹理坐标 相同的结果吗?毕竟,是纹理坐标定义了 U 和 V 的含义相对 与模型 space。
如果你的正方形右上纹理坐标是(1, 1),左下是(0, 0),那么纹理space中的"right"表示"right" 在模型 space 中。但是如果你改变映射,让 (1, 1) 在右下角,(0, 0) 在左上角,那么纹理 space 中的 "right" 就变成了 "down" (-Y) 模型 space.
如果忽略纹理坐标,即从模型 space 位置到纹理位置的映射,则 (1, 0, 0) 法线仍将指向模型中的 "right" space。但是您的纹理映射表示它应该在模型 space 中指向下方 (0, -1, 0)。就像你旋转模型 space 本身一样。
使用切线-space 法线贴图,存储在纹理中的法线与纹理映射到表面的方式相关。定义从模型 space 到切线 space(纹理映射的 space)的映射是 TBN 矩阵的用途。
随着对象和法线之间的映射变得更加复杂,这会变得更加复杂。对于四边形的情况,您可以伪造它,但对于一般图形,它需要是算法的。毕竟,映射不是恒定的。它涉及拉伸和倾斜,因为不同的三角形使用不同的纹理坐标。
现在,有 object-space 法线贴图,它生成明确在模型 space 中的法线。这些避免了对切线-space 基矩阵的需要。但它将法线贴图与其使用的对象密切相关。你甚至不能做基本的纹理坐标动画,更不用说允许法线贴图用于两个单独的对象了。如果你正在做骨骼重量蒙皮,它们几乎是行不通的,因为三角形经常改变大小。