【问题标题】:Why does this Phong shader work?为什么这个 Phong 着色器有效?
【发布时间】:2014-04-20 03:46:38
【问题描述】:

我最近在 GLSL 中编写了一个 Phong 着色器,作为学校作业的一部分。我从教程开始,然后玩弄代码直到我让它工作。据我所知,它工作得非常好,但我特别写了一行我不明白为什么它确实工作。

顶点着色器:

#version 330

layout (location = 0) in vec3 Position;    // Vertex position
layout (location = 1) in vec3 Normal;      // Vertex normal

out vec3 Norm;
out vec3 Pos;
out vec3 LightDir;

uniform mat3 NormalMatrix;      // ModelView matrix without the translation component, and inverted
uniform mat4 MVP;               // ModelViewProjection Matrix
uniform mat4 ModelView;         // ModelView matrix
uniform vec3 light_pos;         // Position of the light

void main()
{
    Norm = normalize(NormalMatrix * Normal);
    Pos = Position;
    LightDir = NormalMatrix * (light_pos - Position);

    gl_Position = MVP * vec4(Position, 1.0);
}

片段着色器:

#version 330

in vec3 Norm;
in vec3 Pos;
in vec3 LightDir;

layout (location = 0) out vec4 FragColor;

uniform mat3 NormalMatrix;
uniform mat4 ModelView;

void main()
{
    vec3 normalDirCameraCoords = normalize(Norm);
    vec3 vertexPosLocalCoords = normalize(Pos);
    vec3 lightDirCameraCoords = normalize(LightDir);

    float dist = max(length(LightDir), 1.0);

    float intensity = max(dot(normalDirCameraCoords, lightDirCameraCoords), 0.0) / pow(dist, 1.001);

    vec3 h = normalize(lightDirCameraCoords - vertexPosLocalCoords);
    float intSpec = max(dot(h, normalDirCameraCoords), 0.0);
    vec4 spec = vec4(0.9, 0.9, 0.9, 1.0) * (pow(intSpec, 100) / pow(dist, 1.2));

    FragColor = max((intensity * vec4(0.7, 0.7, 0.7, 1.0)) + spec, vec4(0.07, 0.07, 0.07, 1.0));
}

所以我正在做的方法是计算光向量和相机向量之间的半向​​量,然后用法线点它。这都很好。但是,我做了两件奇怪的事情。

  1. 通常,一切都在眼睛坐标中完成。但是,我从顶点着色器传递到片段着色器的位置位于局部坐标中。

  2. 这是让我感到困惑的部分。在vec3 h = normalize(lightDirCameraCoords - vertexPosLocalCoords); 线上,我将相机坐标中的光矢量与 local 坐标中的顶点位置相减。这似乎完全错误。

简而言之,我明白这段代码应该做什么,以及 phong 着色的半向量方法是如何工作的。

但是为什么这段代码可以工作?

编辑: 我们提供的起始代码是开源的,因此您可以download the completed project 并直接查看。该项目适用于 Windows 上的 VS 2012(您需要设置 GLEW、GLM 和 freeGLUT),并且应该在没有代码更改的情况下在 GCC 上工作(可能对 makefile 库路径进行一两次更改)。

请注意,在源文件中,“light_pos”称为“gem_pos”,因为我们的光源是您使用 WSADXC 移动的小宝石。按 M 获得具有多个灯的 Phong。

【问题讨论】:

  • 你确定它正确有效吗?它可能只是巧合地看起来并没有完全损坏:)
  • 可能是对象的原点与世界原点对齐
  • melak47:它确实有效,我的作业完成了 100%。它运行良好,无论我将灯光移动到哪里,它都能让我的模型看起来适当闪亮:D
  • Abhishek:既然你提到了,模型确实是世界原点
  • 所以你实际上是在每次相机移动时重新计算所有的法线?无论如何,你似乎不太可能得到 right 结果,但如果你的对象很小(与单位半径相比),以原点为中心并且相当凸,你可能会得到一个看起来有光泽的结果,是的(您的局部顶点坐标然后近似于表面法线)。由于减法,您的中途矢量将位于错误的一侧,因此您的高光将位于错误的位置(尽管再次,它仍然看起来很闪亮:))。

标签: opengl glsl shader


【解决方案1】:

这种方法有效的原因是偶然的,但有趣的是看看它为什么仍然有效。

Phong 着色是三合一的技术

使用 phong 着色,我们有三个术语:speculardiffuseambient;这三个术语代表了 phong 着色中使用的三种技术。

这些术语都没有严格要求向量空间;只要您保持一致,您就可以在世界、本地或相机空间中进行 phong 着色。眼睛空间通常用于照明,因为它更易于使用且转换简单。

但是如果你在原点呢? 现在你乘以零;很容易看出,原点处的任何向量空间之间都没有区别。巧合的是,在原点,你所在的向量空间并不重要。它会工作的。

vec3 h = normalize(lightDirCameraCoords - vertexPosLocalCoords);

请注意,它基本上是减去 0;这是唯一一次使用 local ,并且在一个可以造成最小伤害的地方使用它。由于对象在原点,它的所有顶点也应该在原点或非常接近原点。在原点,近似值是精确的;所有向量空间收敛。非常接近原点,非常接近精确;即使我们使用精确实数,这也会是一个非常小的差异,但我们不使用精确实数,我们使用浮点数,这使问题更加复杂。

基本上,你很幸运;如果对象不在原点,这将不起作用。试着移动一下看看!

另外,你没有使用Phong shading;您使用的是Blinn-Phong shading(这是将reflect()替换为半向量的名称,仅供参考)。

【讨论】:

  • 他的所有顶点似乎不太可能都在原点?
  • @heinrichj 除了 vertexPosLocalCoords 之外的所有内容都是关于眼睛视图的;它是唯一一个奇怪的人,它只用于减法。由于对象位于原点,因此所有顶点也应该(或非常接近)。这意味着尽管使用了不正确的向量空间,但准确度异常高。
  • 即使它的所有顶点都正好在原点,得到的中途向量仍然是完全关闭的,除非光线方向恰好与眼睛方向相同。所以我不会说准确率很高:)
  • @heinrichj 不,中途向量不是“完全关闭”;它非常接近,因为您减去了一个非常小的数字(或 0)。鉴于所有这些都是照明方程的近似值(或只是直接的组合),称其为不准确有点不公平。 Phong 着色本身是不准确的。
  • 中途向量顾名思义:眼睛和光线方向之间的中途。因此,如果从光的方向减去零,除非眼睛方向相同,否则不会得到中途矢量。
猜你喜欢
  • 2021-01-05
  • 2011-06-09
  • 2016-10-15
  • 2020-04-05
  • 2013-04-26
  • 1970-01-01
  • 1970-01-01
  • 2011-05-21
  • 2013-05-09
相关资源
最近更新 更多