OpenGL Phong 照明:镜面高光错误
OpenGL Phong lighting: specular highlight is wrong
我在 OpenGL 中的 Phong 光照着色器似乎有一个奇怪的问题。镜面高光出现在物体的错误一侧。
手头的问题:
如你所见,镜面高光出现在立方体的另一侧(从光的角度来看,也出现在立方体垂直边的角上。它应该只出现在最接近的一侧光。
立方体的顶点:
float cubeVertices[] = {
// positions // normals // texture coords
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f
};
顶点着色器:
#version 330 core
layout (location = 0) in vec3 vPos;
layout (location = 1) in vec3 vNormal;
layout (location = 2) in vec2 vTexCoords;
out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoords;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
FragPos = vec3(model * vec4(vPos, 1.0));
Normal = mat3(transpose(inverse(model))) * vNormal;
TexCoords = vTexCoords;
gl_Position = projection * view * vec4(FragPos, 1.0);
}
片段着色器:
#version 330 core
out vec4 FragColor;
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords;
uniform vec3 viewPos;
uniform sampler2D diffuseMap;
struct Light {
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light lights[8];
uniform int numLights;
vec3 calculateAmbient(vec3 fragPos, vec3 materialAmbient)
{
vec3 ambient = vec3(0.0);
for (int i = 0; i < numLights && i < 8; i++)
{
ambient += lights[i].ambient * materialAmbient;
}
return ambient * 0.5;
}
vec3 calculateDiffuse(vec3 fragPos, vec3 normal, vec3 materialDiffuse)
{
vec3 diffuse = vec3(0.0);
for (int i = 0; i < numLights && i < 8; i++)
{
vec3 lightDir = normalize(lights[i].position - fragPos);
float diff = max(dot(normal, lightDir), 0.0);
diffuse += lights[i].diffuse * (diff * materialDiffuse);
}
return diffuse;
}
vec3 calculateSpecular(vec3 fragPos, vec3 normal, vec3 viewDir, vec3 materialSpecular, float shininess)
{
vec3 specular = vec3(0.0);
for (int i = 0; i < numLights && i < 8; i++)
{
vec3 lightDir = normalize(lights[i].position - fragPos);
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0f * shininess);
specular += lights[i].specular * (spec * materialSpecular);
}
return specular;
}
void main()
{
vec4 tex = texture(diffuseMap, TexCoords);
vec3 norm = normalize(Normal);
vec3 viewDir = normalize(viewPos - FragPos);
vec3 ambient = calculateAmbient(FragPos, material.ambient);
vec3 diffuse = calculateDiffuse(FragPos, norm, material.diffuse);
vec3 specular = calculateSpecular(FragPos, norm, viewDir, material.specular, material.shininess);
vec3 result = (ambient + diffuse + specular) * tex.rgb;
FragColor = vec4(result, 1.0);
}
镜面高光也出现在背面朝向光源的面上,因为relfect
实际计算:
refelct(I, N) = I - 2.0 * dot(N, I) * N
在背面法向量指向远离光源,但是refelct(I, N) == refelct(I, -N)
,因为:
I - 2.0 * dot(N, I) * N == I - 2.0 * dot(-N, I) * -N
在Phong reflection model中,如果漫反射光>0,则只添加镜面高光。例如:
for (int i = 0; i < numLights && i < 8; i++)
{
vec3 lightDir = normalize(lights[i].position - fragPos);
float NdotL = dot(normal, lightDir);
if (NdotL > 0.0)
{
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0f * shininess);
specular += lights[i].specular * (spec * materialSpecular);
}
}
我在 OpenGL 中的 Phong 光照着色器似乎有一个奇怪的问题。镜面高光出现在物体的错误一侧。
手头的问题:
如你所见,镜面高光出现在立方体的另一侧(从光的角度来看,也出现在立方体垂直边的角上。它应该只出现在最接近的一侧光。
立方体的顶点:
float cubeVertices[] = {
// positions // normals // texture coords
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f
};
顶点着色器:
#version 330 core
layout (location = 0) in vec3 vPos;
layout (location = 1) in vec3 vNormal;
layout (location = 2) in vec2 vTexCoords;
out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoords;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
FragPos = vec3(model * vec4(vPos, 1.0));
Normal = mat3(transpose(inverse(model))) * vNormal;
TexCoords = vTexCoords;
gl_Position = projection * view * vec4(FragPos, 1.0);
}
片段着色器:
#version 330 core
out vec4 FragColor;
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords;
uniform vec3 viewPos;
uniform sampler2D diffuseMap;
struct Light {
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light lights[8];
uniform int numLights;
vec3 calculateAmbient(vec3 fragPos, vec3 materialAmbient)
{
vec3 ambient = vec3(0.0);
for (int i = 0; i < numLights && i < 8; i++)
{
ambient += lights[i].ambient * materialAmbient;
}
return ambient * 0.5;
}
vec3 calculateDiffuse(vec3 fragPos, vec3 normal, vec3 materialDiffuse)
{
vec3 diffuse = vec3(0.0);
for (int i = 0; i < numLights && i < 8; i++)
{
vec3 lightDir = normalize(lights[i].position - fragPos);
float diff = max(dot(normal, lightDir), 0.0);
diffuse += lights[i].diffuse * (diff * materialDiffuse);
}
return diffuse;
}
vec3 calculateSpecular(vec3 fragPos, vec3 normal, vec3 viewDir, vec3 materialSpecular, float shininess)
{
vec3 specular = vec3(0.0);
for (int i = 0; i < numLights && i < 8; i++)
{
vec3 lightDir = normalize(lights[i].position - fragPos);
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0f * shininess);
specular += lights[i].specular * (spec * materialSpecular);
}
return specular;
}
void main()
{
vec4 tex = texture(diffuseMap, TexCoords);
vec3 norm = normalize(Normal);
vec3 viewDir = normalize(viewPos - FragPos);
vec3 ambient = calculateAmbient(FragPos, material.ambient);
vec3 diffuse = calculateDiffuse(FragPos, norm, material.diffuse);
vec3 specular = calculateSpecular(FragPos, norm, viewDir, material.specular, material.shininess);
vec3 result = (ambient + diffuse + specular) * tex.rgb;
FragColor = vec4(result, 1.0);
}
镜面高光也出现在背面朝向光源的面上,因为relfect
实际计算:
refelct(I, N) = I - 2.0 * dot(N, I) * N
在背面法向量指向远离光源,但是refelct(I, N) == refelct(I, -N)
,因为:
I - 2.0 * dot(N, I) * N == I - 2.0 * dot(-N, I) * -N
在Phong reflection model中,如果漫反射光>0,则只添加镜面高光。例如:
for (int i = 0; i < numLights && i < 8; i++)
{
vec3 lightDir = normalize(lights[i].position - fragPos);
float NdotL = dot(normal, lightDir);
if (NdotL > 0.0)
{
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0f * shininess);
specular += lights[i].specular * (spec * materialSpecular);
}
}