【问题标题】:Specular lighting appears on both eye-facing and rear sides of object镜面光照出现在物体的正面和背面
【发布时间】:2013-11-29 06:14:22
【问题描述】:

我正在编写一个 Phong 着色的小测试,并且正在用头撞砖墙,试图让镜面反射组件工作。它似乎工作正常,除了镜面光同时应用于对象的前后。 (对象本身是透明的,人脸在 CPU 端手动排序。)

请注意,漫反射组件运行良好(仅显示在面向光源的一侧)——我认为这似乎排除了法线到达片段着色器的问题。

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

为了让事情变得非常简单,我的测试代码尝试在世界空间中执行此操作。相机位置和光矢量分别被硬编码(抱歉)为(0,0,4)(-0.707, 0, -0.707)。因此眼睛向量是(0,0,4) - fragPosition

由于模型矩阵只执行旋转,顶点着色器只是简单地通过模型矩阵变换顶点法线。 (注意:这不是很好的做法,因为许多类型的变换不保留正态正交性。通常对于正态矩阵,you should use 是模型矩阵左上角 3x3 的逆转置。)为简单起见,片段着色器在单个浮点颜色通道上运行,并跳过镜面反射指数(/ 使用 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);

}

我在这里找不到错误;谁能解释为什么镜面反射分量出现在物体的背面和面向光的一侧?

非常感谢。

【问题讨论】:

  • 需要效果截图,您使用的是什么几何图形?
  • 另外这太可怕了,你应该把方向作为统一,并有一个单独的矩阵来转换法线,而统一的比例是必需的,只有当统一的比例是等距时,这是' t 总是正确的。
  • 嗨,alec,对硬编码的方向感到抱歉,试着想象它们是制服。我这样做是为了让它们保持超简单,但我在这里找不到不一致的地方。截图:http://ben.am/temp/specular-problem.png
  • 您不想使用模型视图矩阵来转换法线向量。如果您这样做,它们可能不会保持垂直。相反,您想使用左上角 3x3 子矩阵 NormalMatrix = T (ModelView^-1) 的逆转置。这就是 oldschool GLSL 中的 gl_NormalMatrix,但您可以在顶点着色器中或作为另一个统一矩阵轻松计算它。
  • 谢谢安东。在问这个问题之前,我小心翼翼地消除了法线的问题,但由于我不想鼓励使用模型矩阵来转换法线,所以我在问题正文中添加了一个注释。

标签: opengl glsl lighting specular phong


【解决方案1】:

您的镜面反射贡献实际上对于正面和背面是相同的,因为 GLSL reflect 对法线的符号不敏感。来自this reference

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

所以翻转 N 的符号会引入两个取消的减号。换句话说,reflect 所做的是将与N 沿同一轴的I 的分量的符号反转。这不取决于N 面对的方向。

如果您想从背面移除镜面反射分量,我认为您需要明确检查您的dot(vE,vN)

【讨论】:

  • 或乘以兰伯特项——即 sf*=df
  • 非常感谢迈克,这就是问题所在。修复我现在正在使用的后部镜面高光的一种简单方法是:float sf = 0.0; if (df > 0.0) { /* calculate sf */ }
【解决方案2】:

尝试改变

fragpos_world = vec3(modelMtx * vec4(inVPos, 0.0)); 

fragpos_world = vec3(modelMtx * vec4(inVPos, 1.0)); 

因为http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/#Homogeneous_coordinates

【讨论】:

  • 另外值得注意的是,1.0 作为 w 坐标表示“位置”和 0.0 作为方向之间有很好的联系
  • 谢谢@jpaari,你说的很对,fragpos_world 的转换应该用 w = 1.0 来完成。另一方面,法线变换应在 w = 0.0 时完成。但是,不幸的是,问题仍然存在。
猜你喜欢
  • 2012-10-28
  • 1970-01-01
  • 1970-01-01
  • 2021-08-05
  • 2015-01-31
  • 2020-07-24
  • 2021-04-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多