我正在编写一个有关Phong着色的小测试,并且将我的头撞在砖墙上,以使镜面组件正常工作。除了将镜面反射光同时应用于对象的前部和后部之外,它似乎正常工作。 (对象本身是透明的,其面孔在CPU端进行了手动排序。)

请注意,漫反射组件工作正常(仅显示在面向光源的一侧)-我认为这似乎可以排除法线到达片段着色器的问题。

据我了解,镜面反射分量与眼睛向量和反射光向量之间的角度的cos成正比。

为了使事情变得非常简单,我的测试代码尝试在世界空间中做到这一点。相机位置和光向量分别被硬编码为(0,0,4)(-0.707, 0, -0.707)。因此,眼睛向量是(0,0,4) - fragPosition

由于模型矩阵仅执行旋转,因此顶点着色器仅通过模型矩阵转换顶点法线。 (注意:这不是一个好习惯,因为许多类型的转换都不能保留正常的正交性。通常对于正常矩阵,you should use是模型矩阵左上3x3的逆转置。)为简单起见,片段着色器在单个 float 颜色 channel 上运行,并跳过镜面反射指数(/使用指数1)。

顶点着色器:

#version 140

uniform mat4 mvpMtx;
uniform mat4 modelMtx;

in vec3 inVPos;
in vec3 inVNormal;

out vec3 normal_world;
out vec3 fragpos_world;

void main(void) {
  gl_Position = mvpMtx * vec4(inVPos, 1.0);
  normal_world = vec3(modelMtx * vec4(inVNormal, 0.0));
  fragpos_world = vec3(modelMtx * vec4(inVPos, 1.0));
}

片段着色器:
#version 140

uniform float
    uLightS,
    uLightD,
    uLightA;

uniform float uObjOpacity;

in vec3 normal_world, fragpos_world;
out vec4 fragOutColour;

void main(void) {

    vec3 vN = normalize(normal_world);
    vec3 vL = vec3(-0.707, 0, -0.707);

    // Diffuse:
    // refl in all directions is proportional to cos(angle between -light vector & normal)
    // i.e. proportional to -l.n
    float df = max(dot(-vL, vN), 0.0);

    // Specular:
    // refl toward eye is proportional to cos(angle between eye & reflected light vectors)
    // i.e. proportional to r.e
    vec3 vE = normalize(vec3(0, 0, 4) - fragpos_world);
    vec3 vR = reflect(vL, vN);
    float sf = max(dot(vR, vE), 0.0);

    float col = uLightA + df*uLightD + sf*uLightS;

    fragOutColour = vec4(col, col, col, uObjOpacity);

}

我在这里找不到错误;谁能解释为什么镜面反射分量出现在对象的后部以及面向光的一侧?

非常感谢。

最佳答案

因为GLSL reflect对法线的符号不敏感,所以您对正面和背面的镜面反射实际上将是相同的。从this reference:

reflect(I, N) = I - 2.0 * dot(N, I) * N

因此,翻转N的符号会引入两个可消除的负号。换句话说,reflect所做的是反转I组件的符号,该符号与N沿同一轴。这并不取决于N所面对的方式。

如果要从背面去除镜面反射分量,我认为您需要专心检查dot(vE,vN)

09-16 23:36