我正在通过a tutorial on the Web学习GLSL。
本教程有一个名为Toon Shading的示例。 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?
我希望我的问题清楚。
谢谢!
答案 0 :(得分:3)
世界空间,它听起来像是:世界的空间。存在于虚拟世界中的所有对象都是公共空间。世界空间的主要目的是定义相机(或眼睛)也可以定位的公共空间。
局部空间,也称为“模型空间”,是您的顶点属性数据所在的空间。如果您的网格来自使用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;
}
我倾向于在我的变量名称前加上说明它们所处的空间,以便明显发生了什么。