为啥不在卡通着色中的法线向量上应用模型视图矩阵(Lighthouse3d 教程)?

Posted

技术标签:

【中文标题】为啥不在卡通着色中的法线向量上应用模型视图矩阵(Lighthouse3d 教程)?【英文标题】:Why Not Applying the Model View Matrix on Normal Vector in Toon Shading (Lighthouse3d Tutorial)?为什么不在卡通着色中的法线向量上应用模型视图矩阵(Lighthouse3d 教程)? 【发布时间】:2011-06-20 10:58:46 【问题描述】:

我正在通过a tutorial on the Web学习GLSL。

本教程有一个名为卡通着色的示例。 Here is the link to Toon Shading - Version I.

在这个例子中,顶点着色器的写法如下:

 uniform vec3 lightDir;
 varying float intensity;

 void main()
 
      intensity = dot(lightDir,gl_Normal);
      gl_Position = ftransform();
 

据我所知,我知道如果旋转了一个曲面,那么该曲面顶点的法向量也应该旋转相同的量,以便法向量反映曲面的新方向。但是,在上面的代码中,模型视图矩阵并未应用于法线向量。法向量直接用于计算光照强度。

关于我的担忧,教程是这样说的:

“让我们假设光的方向是在世界空间中定义的。”

“如果在 OpenGL 应用程序中没有对模型执行旋转或缩放,则在世界空间中定义的法线,作为 gl_Normal 提供给顶点着色器,与在本地空间中定义的法线重合。”

这些解释给了我几个问题:

1. What are world space and local space? How are they different? (This question
   seems a little bit elementary, but I need to understand...)

2. I figure the fact that "the light’s direction is defined in world space"
   has something to do with not applying the Model View Matrix on to the
   normal vector of a vertex. But, what is that?

3. Finally, If we don't apply the Model View Matrix on the normal vector then
   wouldn't the normal be pointing to a direction different from the actual
   direction of the surface? How do we solve this problem?

我希望我已经把我的问题说清楚了。

谢谢!

【问题讨论】:

【参考方案1】:

世界空间是,嗯,听起来像:世界的空间。它是您虚拟世界中存在的所有对象所在的公共空间。世界空间的主要目的是定义相机(或眼睛)也可以放置在其中的公共空间。

局部空间,也被某些人称为“模型空间”,是您的顶点属性数据所在的空间。如果您的网格来自使用 3DS Max、Blender 等工具的人,那么这些位置的空间和法线不一定与世界空间相同。

简单地说,眼睛空间(也称为相机空间或视图空间)本质上是世界空间,除了一切都与相机的位置和方向有关。当您在世界空间中移动相机时,您实际上只是在改变世界到眼睛的空间转换。眼睛空间中的相机总是在原点。

就我个人而言,我觉得 Lighthouse3D 教程的人有点懒惰。您的顶点法线在世界空间中的情况很少见,因此没有表明您必须转换法线和位置(ftransform() 所做的)会产生误导。

本教程的正确之处在于,如果您在世界空间中有法线,在世界空间中有光线方向(并且您正在做定向照明,而不是点照明),那么您不需要转换任何东西.变换法线的目的是将其从局部空间变换到与您的光照方向相同的空间。在这里,他们只是定义他们在同一个空间中。

遗憾的是,实际用户通常无法定义其顶点法线位于本地以外的任何空间中。所以他们必须改造它们。

最后,如果我们不对法线向量应用模型视图矩阵,那么法线会不会指向与表面实际方向不同的方向?

谁在乎?重要的是这两个方向在同一个空间中。该空间是否与顶点位置的空间相同并不重要。随着您对图形的深入了解,您会发现方便照明的空间可能不是您曾经转换位置的空间。

没关系。因为光照方程只取一个朝向光的方向和一个表面法线。它不需要位置,除非你在做点照明,即使那样,位置也只有在它可以让你计算光的方向和衰减因子的情况下才有用。你可以把光的方向转换成你想要的任何空间。

有些人在局部空间进行照明。如果您在进行凹凸贴图,您通常会希望在与纹理平面相切的空间中进行光照。


附录:

处理法线的标准方法(假设您的矩阵仍然通过 OpenGL 的标准矩阵命令来实现)是假设法线与您的位置数据位于同一空间中。因此,它们也需要进行改造。

但是,出于better explained here, 的原因(注意:为了充分披露,我写了那个页面)你不能只用你将用于位置的矩阵来转换法线。幸运的是,OpenGL 是这样编写的,所以如果你使用标准的 OpenGL 矩阵堆栈,它们会为你提供一个预定义的矩阵来处理这个问题:gl_NormalMatrix

GLSL 中的典型光照场景如下所示:

uniform vec3 eyeSpaceLightDir;
varying float intensity;

void main()

    vec3 eyeSpaceNormal = gl_NormalMatrix * gl_Normal;
    intensity = dot(eyeSpaceLightDir, eyeSpaceNormal);
    gl_Position = ftransform;

我倾向于在我的变量名前面加上它们的位置,以便清楚地知道发生了什么。

【讨论】:

感谢您的回答。但是,我仍然不清楚您对上一个问题的回答。假设您通过指定三个顶点来绘制平面。对于每个顶点,您指定一个法线。然后,您在该平面上执行旋转。当您编写顶点着色器时,为了在顶点上应用旋转,您使用函数 ftransform()。同时,您还必须旋转法线对吗?使他们指向正确的方向。那你怎么做呢? (即在法线上应用模型视图矩阵) @Einiosaurus:您应用于平面的旋转将是局部到世界变换(技术上是局部到眼睛)的一部分。 Lighthouse3D 教程期望法线已经在世界空间中。因此,在不破坏着色器假设的情况下,不允许对平面应用旋转。简而言之,本教程的着色器不允许您的建议。他们只是简单地定义问题,这就是我说他们懒惰的原因。 是的!我明白你的意思。但是,我要问的是教程定义的问题的解决方案。假设法线不在世界空间中,我们必须对其进行变换,你知道我们如何在顶点着色器中变换法线吗? @Einiosaurus:我已经在我的答案中添加了一个新部分。 完美!这正是我正在寻找的。谢谢!先生,您已经获得了公认的答案!

以上是关于为啥不在卡通着色中的法线向量上应用模型视图矩阵(Lighthouse3d 教程)?的主要内容,如果未能解决你的问题,请参考以下文章

为啥是模型视图矩阵?

为什么使用模型视图矩阵的反转置转换法线?

从 Assimp 加载 Collada (dae) 模型显示不正确的法线

在顶点着色器中将法线转换为视图空间

从顶点数据中获取像素法线

光照 法线矩阵(Normal Matrix)