为什么我的镜面高光在多边形边缘上显示得如此强烈?
Why do my specular highlights show up so strongly on polygon edges?
我有一个简单的应用程序,它使用单个定向光绘制球体。我通过从八面体开始并将每个三角形细分为 4 个较小的三角形来创建球体。
仅使用漫射照明,球体看起来非常光滑。但是,当我添加镜面反射高光时,三角形的边缘显示得相当强烈。以下是一些示例:
仅漫反射:
漫反射和镜面反射:
我相信法线插值正确。只看法线,我明白了:
事实上,如果我切换到平面着色,其中法线是每个多边形而不是每个顶点,我得到这个:
在我的顶点着色器中,我将模型的法线乘以转置逆模型视图矩阵:
#version 330 core
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec3 vNormal;
layout (location = 2) in vec2 vTexCoord;
out vec3 fNormal;
out vec2 fTexCoord;
uniform mat4 transInvModelView;
uniform mat4 ModelViewMatrix;
uniform mat4 ProjectionMatrix;
void main()
{
fNormal = vec3(transInvModelView * vec4(vNormal, 0.0));
fTexCoord = vTexCoord;
gl_Position = ProjectionMatrix * ModelViewMatrix * vPosition;
}
在片段着色器中,我按如下方式计算镜面高光:
#version 330 core
in vec3 fNormal;
in vec2 fTexCoord;
out vec4 color;
uniform sampler2D tex;
uniform vec4 lightColor; // RGB, assumes multiplied by light intensity
uniform vec3 lightDirection; // normalized, assumes directional light, lambertian lighting
uniform float specularIntensity;
uniform float specularShininess;
uniform vec3 halfVector; // Halfway between eye and light
uniform vec4 objectColor;
void main()
{
vec4 texColor = objectColor;
float specular = max(dot(halfVector, fNormal), 0.0);
float diffuse = max(dot(lightDirection, fNormal), 0.0);
if (diffuse == 0.0)
{
specular = 0.0;
}
else
{
specular = pow(specular, specularShininess) * specularIntensity;
}
color = texColor * diffuse * lightColor + min(specular * lightColor, vec4(1.0));
}
我对如何计算 halfVector
有点困惑。我在 CPU 上做这件事并将它作为制服传递。是这样计算的:
vec3 lightDirection(1.0, 1.0, 1.0);
lightDirection = normalize(lightDirection);
vec3 eyeDirection(0.0, 0.0, 1.0);
eyeDirection = normalize(eyeDirection);
vec3 halfVector = lightDirection + eyeDirection;
halfVector = normalize(halfVector);
glUniform3fv(halfVectorLoc, 1, &halfVector [ 0 ]);
halfVector
的公式是否正确?还是也需要在着色器中完成?
将法线插值到面中可能(并且几乎总是会)导致法线的缩短。这就是为什么高光在脸部中心较暗而在角落和边缘较亮的原因。如果这样做,只需在片段着色器中重新规范化法线即可:
fNormal = normalize(fNormal);
顺便说一句,您不能预先计算半向量,因为它依赖于视图(这是镜面照明的全部意义)。在您当前的场景中,只要移动相机(保持方向),高光不会改变。
在着色器中执行此操作的一种方法是为眼睛位置传递一个额外的制服,然后将视图方向计算为 eyePosition - vertexPosition
。然后像 CPU.
一样继续
我有一个简单的应用程序,它使用单个定向光绘制球体。我通过从八面体开始并将每个三角形细分为 4 个较小的三角形来创建球体。
仅使用漫射照明,球体看起来非常光滑。但是,当我添加镜面反射高光时,三角形的边缘显示得相当强烈。以下是一些示例:
仅漫反射:
漫反射和镜面反射:
我相信法线插值正确。只看法线,我明白了:
事实上,如果我切换到平面着色,其中法线是每个多边形而不是每个顶点,我得到这个:
在我的顶点着色器中,我将模型的法线乘以转置逆模型视图矩阵:
#version 330 core
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec3 vNormal;
layout (location = 2) in vec2 vTexCoord;
out vec3 fNormal;
out vec2 fTexCoord;
uniform mat4 transInvModelView;
uniform mat4 ModelViewMatrix;
uniform mat4 ProjectionMatrix;
void main()
{
fNormal = vec3(transInvModelView * vec4(vNormal, 0.0));
fTexCoord = vTexCoord;
gl_Position = ProjectionMatrix * ModelViewMatrix * vPosition;
}
在片段着色器中,我按如下方式计算镜面高光:
#version 330 core
in vec3 fNormal;
in vec2 fTexCoord;
out vec4 color;
uniform sampler2D tex;
uniform vec4 lightColor; // RGB, assumes multiplied by light intensity
uniform vec3 lightDirection; // normalized, assumes directional light, lambertian lighting
uniform float specularIntensity;
uniform float specularShininess;
uniform vec3 halfVector; // Halfway between eye and light
uniform vec4 objectColor;
void main()
{
vec4 texColor = objectColor;
float specular = max(dot(halfVector, fNormal), 0.0);
float diffuse = max(dot(lightDirection, fNormal), 0.0);
if (diffuse == 0.0)
{
specular = 0.0;
}
else
{
specular = pow(specular, specularShininess) * specularIntensity;
}
color = texColor * diffuse * lightColor + min(specular * lightColor, vec4(1.0));
}
我对如何计算 halfVector
有点困惑。我在 CPU 上做这件事并将它作为制服传递。是这样计算的:
vec3 lightDirection(1.0, 1.0, 1.0);
lightDirection = normalize(lightDirection);
vec3 eyeDirection(0.0, 0.0, 1.0);
eyeDirection = normalize(eyeDirection);
vec3 halfVector = lightDirection + eyeDirection;
halfVector = normalize(halfVector);
glUniform3fv(halfVectorLoc, 1, &halfVector [ 0 ]);
halfVector
的公式是否正确?还是也需要在着色器中完成?
将法线插值到面中可能(并且几乎总是会)导致法线的缩短。这就是为什么高光在脸部中心较暗而在角落和边缘较亮的原因。如果这样做,只需在片段着色器中重新规范化法线即可:
fNormal = normalize(fNormal);
顺便说一句,您不能预先计算半向量,因为它依赖于视图(这是镜面照明的全部意义)。在您当前的场景中,只要移动相机(保持方向),高光不会改变。
在着色器中执行此操作的一种方法是为眼睛位置传递一个额外的制服,然后将视图方向计算为 eyePosition - vertexPosition
。然后像 CPU.