学习OpenGL——第九天

60 阅读4分钟

基础光照

现实世界的光照极其复杂,受多种因素影响,难以用有限的计算资源完整模拟。因此,OpenGL 采用简化模型近似真实光照,既易于实现,视觉效果也较为真实。这些光照模型基于对光的物理特性理解,其中最常用的是 Phong 光照模型。该模型包含三个主要分量:

  • 环境光(Ambient)
  • 漫反射(Diffuse)
  • 镜面光(Specular)

下图展示了这些光照分量的视觉效果:

image.png


Phong 光照模型三大分量

  • 环境光照(Ambient Lighting)即使在黑暗环境中,世界上也总有一些微弱光线,因此物体几乎不会完全黑暗。通过设置环境光照常量,为物体提供基础色彩。
  • 漫反射光照(Diffuse Lighting)模拟光源对物体的方向性影响,是视觉上最显著的部分。物体表面越正对光源,亮度越高。
  • 镜面光照(Specular Lighting)模拟有光泽物体表面出现的高光点。镜面光的颜色通常更接近光源色,而非物体本身颜色。

环境光

在现实中,光线通常来自四面八方,经由多次反射间接影响物体。这种现象称为全局照明(Global Illumination),但其计算复杂且资源消耗大。

为简化处理,常用环境光照近似全局照明。只需将一个较小的常量光照颜色添加到物体片段的最终颜色中,即使无直接光源,场景也不会全黑。

实现方式:

void main() { 
    float ambientStrength = 0.1; 
    vec3 ambient = ambientStrength * lightColor; 
    vec3 result = ambient * objectColor; 
    FragColor = vec4(result, 1.0); 
}

漫反射

环境光不足以展现真实感,漫反射光照可以让物体根据光源方向产生明暗变化。与光线方向越接近的片段,亮度越高。

image.png

计算方法:

  1. 法向量(Normal Vector):垂直于片段表面的单位向量。
  2. 光照方向向量:光源位置与片段位置的差向量。

注意:向量需标准化为单位向量,才能正确反映夹角余弦。

代码示例:

vec3 norm = normalize(Normal); 
vec3 lightDir = normalize(lightPos - FragPos); 
float diff = max(dot(norm, lightDir), 0.0); 
vec3 diffuse = diff * lightColor;
  • 点乘值越大,漫反射分量越强。
  • 使用 max 保证分量不为负数,避免无定义的负光照。

最终颜色合成:

vec3 result = (ambient + diffuse) * objectColor;
FragColor = vec4(result, 1.0);

关于法向量变换

  • 法向量仅表示方向,无空间位置,也无齐次坐标(w 分量)。
  • 位移不应影响法向量,变换时应仅保留模型矩阵的左上角 3×3 部分(旋转和缩放)。
  • 不等比缩放会破坏法向量的垂直性,需使用法线矩阵(Normal Matrix)进行修正:

image.png

法线矩阵定义: 模型矩阵左上角 3×3 的逆矩阵的转置。

代码实现:

Normal = mat3(transpose(inverse(model))) * aNormal;

矩阵求逆开销较大,建议在 CPU 端计算后通过 uniform 传递给着色器。


镜面光

镜面光照不仅依赖光照方向与法向量,还与观察方向相关,决定于表面的反射特性。如下图所示:

image.png

  • 通过法向量翻折入射光方向,得到反射向量。
  • 反射向量与观察方向越接近,镜面高光越明显。

计算步骤:

  1. 定义镜面强度:

float specularStrength = 0.5;

  1. 计算视线方向和反射方向:
vec3 viewDir = normalize(viewPos - FragPos); 
vec3 reflectDir = reflect(-lightDir, norm);
  1. 计算镜面分量:
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); 
vec3 specular = specularStrength * spec * lightColor;
    • 32 为反光度(Shininess),值越大高光越集中。

image.png

早期光照多在顶点着色器实现,效率高但不够真实。

顶点插值光照称为 Gouraud 着色,片段级光照称为 Phong 着色,后者效果更平滑自然。

image.png


观察空间 VS 世界空间 —— 计算 Phong 光照

世界空间

  • 全局坐标系:所有物体的统一参考系
  • 特点:
    • 原点固定在世界某一点
    • 物体通过模型变换(平移、旋转、缩放)放置其中
    • 光源位置通常在世界空间定义

观察空间

  • 相机坐标系:以摄像机为中心的坐标系
  • 特点:
    • 原点在摄像机位置
    • Z 轴通常指向观察方向(OpenGL 为 -Z,DirectX 为 +Z)
    • 通过视图矩阵从世界空间转换而来

特性观察空间世界空间
视线计算简单(相机在原点)需要相机位置
光源处理需转换到观察空间直接使用
调试便利性较差较好
与投影结合直接需额外步骤
性能通常更优略差
现代应用更常见特定场景