Phong 照明:单独添加镜面照明还是使用环境光和漫反射?

Phong lighting: add specular lighting separately or with ambient and diffuse?

我正在尝试实施 Phong 照明。在一些教程中,镜面光照被添加到环境光照和漫射光照中,然后总光照乘以纹理颜色。我还看到一个教程,其中在添加环境光和漫反射光与纹理颜色相乘后单独添加镜面照明。

这是一个片段着色器,其中包含两个选项和屏幕截图。

#version 330 core
out vec4 FragColor;

in vec2 TexCoord;
in vec3 normals;
in vec3 fragPosition;


//texture samplers
uniform sampler2D texture1;
uniform vec3 ambientLight;
uniform vec3 lightPosition;
uniform vec3 lightColor;
uniform vec3 viewPosition;
uniform float specularStrength;
uniform float shineDamp;

void main()
{
    vec3 norm = normalize(normals);
    vec3 lightDir = normalize(lightPosition - fragPosition); 

    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * lightColor;

    vec3 viewDir = normalize(viewPosition - fragPosition);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), shineDamp);
    vec3 specular = specularStrength * spec * lightColor;  

    // 1. Specular is added to ambient and diffuse lights and this result is multiplied with texture
    //FragColor = vec4((ambientLight + diffuse + specular), 1.0f) * texture(texture1, TexCoord);

    // 2. Specular is added separately to result of multiplication of ambient + diffuse and texture
    //FragColor = vec4((ambientLight + diffuse), 1.0f) * texture(texture1, TexCoord) + vec4(specular, 1.0);
}

在这些屏幕截图中,shineDump 值为 32.0f,specularStrength 为 0.5f。

哪个看起来正确?在我看来,与第一个选项相比,第二个选项看起来是正确的,但是很多教程都使用第一个选项中的公式。

一般来说,在计算机图形学中,光是由 双向反射分布函数 (BRDF) 建模的。 BRDF 是一个函数,它给出了沿出射方向反射的光与从入射方向入射的光之间的关系。

一般来说,BRDF 模型可以分为以下一类或两类:

  • 经验: 他们的主要目的是提供一种简单的公式,专门用于模拟一种反射。因此,我们得到了一个可通过参数调整的快速计算模型,但没有考虑其背后的物理学。
  • 理论:这些模型尝试使用物理定律准确模拟光散射。它们通常会导致复杂的表达和高计算量,因此它们通常不用于渲染系统。
  • 实验性:可以使用 Gonioreflectometer 获取 BRDF,它会机械地改变光源和传感器位置。其他技术使用数码相机通过单张照片获取许多 BRDF 样本。

进一步了解:


Phong model 是一个经验各向同性模型。由于经验和理论获得的 BRDF 模型都只是真实材料反射特性的近似值,因此完全取决于您,为您的场景和表面外观保留 BRDF。

I am trying to implement Phong lighting. In some tutorials specular lighting is added to ambient and diffuse lighting and then total lighting is multiplied by texture color.

这是黑暗时代的历史产物,当时 GPU 中的光照方程式仍然是硬连接的,并且 Gouraud shading 是标准。仅在顶点评估光照模型,并将生成的光照值插值到整个图元。由于纹理通常用于模拟 material 的表面属性,因此通常在每个片段处对纹理进行采样(以便我们可以为我们的基元提供超出几何中指定级别的结构)。但是由于 gouraud 着色,我们需要用每个片段的纹理数据来调制已经计算出的光值。最简单的方法是将两者相乘。

I also saw a tutorial where specular lighting was added separately after the addition of ambient and diffuse lights was multiplied with texture color.

通过纹理颜色调制整个光照会在很多 material 中产生不切实际的结果。为了解决这些问题,有时会分离镜面反射部分。我们现在计算每个顶点的环境+漫反射部分和镜面反射部分,对它们进行插值,用纹理调制环境+漫反射,然后添加每个片段的镜面反射部分。

然而,现在没有人会使用 Gouraud 着色,我们改为计算每个片段的光照。我们不再有不同的频率来评估光模型和纹理采样,所以这些问题变得毫无意义。由于某些表面反射的实际光线取决于material,而我们使用纹理来模拟material,我们可以直接使用纹理作为输入照明计算,即作为漫反射反照率,或镜面反射率,或其他。这也允许我们在基元上改变 任意 material 属性,而不仅仅是 "colors",shininess/roughness 也可以来自纹理作为法线方向以及您的照明模型可能使用的任何参数。