目录
光照和材质是计算机图形学中至关重要的元素,它们共同决定了渲染图像的视觉效果。光照模型用于模拟光源与物体表面之间的光交互,而材质则定义了物体表面对这些光线的响应。深入理解光照模型和材质属性对于创建逼真的3D图形至关重要。本章将介绍多种光照模型,包括Phong光照模型、Blinn-Phong光照模型、Cook-Torrance光照模型和Lambert光照模型,并对材质进行详细讲解。
5.1 光照模型基础
光照模型用于计算场景中物体的光照效果,考虑光源的类型、物体表面的材质属性以及光源与物体之间的相对位置。以下是几种常见的光照模型:
环境光(Ambient Light)
定义:环境光是均匀照射在场景中所有表面的光线,模拟环境中的背景光。它不依赖于光源的位置,通常用于为所有表面提供一个基本的光照水平。
计算:环境光的强度是恒定的,通常由一个固定的环境光强度系数和光源颜色相乘得到。例如:
vec3 ambient = ambientStrength * lightColor;
漫反射光(Diffuse Light)
定义:漫反射光模拟光源照射到粗糙表面上的光线,光的强度取决于光线与表面法线之间的夹角。漫反射光的强度与光源到表面的角度和距离有关。
计算:漫反射光的强度可以通过光线与表面法线的点积来计算,即:
vec3 norm = normalize(Normal); vec3 lightDir = normalize(lightPos - FragPos); float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = diff * lightColor;
镜面反射光(Specular Light)
定义:镜面反射光模拟光线在光滑表面上的高光反射。镜面反射光的强度取决于视角和光源之间的角度。
计算:镜面反射光的强度可以通过视角与反射方向的点积来计算,通常使用一个光泽度因子来调整高光的强度。例如:
float specularStrength = 0.5; vec3 viewDir = normalize(viewPos - FragPos); vec3 reflectDir = reflect(-lightDir, norm); float spec = pow(max(dot(viewDir, reflectDir), 0.0), shininess); vec3 specular = specularStrength * spec * lightColor;
- 环境光:展示环境光的均匀分布对物体表面的照射。
- 漫反射光:展示光源照射到粗糙表面时的光线散射效果。
- 镜面反射光:展示光源在光滑表面上的高光效果和视角对高光的影响。
5.2 Phong光照模型
Phong光照模型是计算机图形学中一种经典的光照模型,它结合了环境光、漫反射光和镜面反射光,提供了一个全面的光照解决方案。Phong模型的公式如下:
其中:
- 是最终的光照强度。
- 是环境光强度。
- 是漫反射光强度。
- 是镜面反射光强度。
- 是环境反射系数。
- 是漫反射反射系数。
- 是镜面反射系数。
- norm 是表面法线。
- lightDir是光源方向。
- viewDir是视角方向。
- reflectDir 是反射方向。
- shininess 是材质的光泽度。
示例:Phong光照模型的GLSL代码
- 顶点着色器代码:
#version 330 core layout(location = 0) in vec3 aPos; // 顶点位置 layout(location = 1) in vec3 aNormal; // 顶点法线 out vec3 FragPos; // 传递到片段着色器的片段位置 out vec3 Normal; // 传递到片段着色器的法线 uniform mat4 model; // 模型矩阵 uniform mat4 view; // 视图矩阵 uniform mat4 projection; // 投影矩阵 void main() { FragPos = vec3(model * vec4(aPos, 1.0)); // 计算片段位置 Normal = mat3(transpose(inverse(model))) * aNormal; // 计算变换后的法线 gl_Position = projection * view * vec4(FragPos, 1.0); // 计算最终位置 }
- 片段着色器代码:
#version 330 core in vec3 FragPos; // 从顶点着色器接收的片段位置 in vec3 Normal; // 从顶点着色器接收的法线 out vec4 FragColor; // 片段的最终颜色 uniform vec3 lightPos; // 光源位置 uniform vec3 viewPos; // 观察者位置 uniform vec3 lightColor; // 光源颜色 uniform vec3 objectColor; // 物体颜色 void main() { // 环境光 float ambientStrength = 0.1; vec3 ambient = ambientStrength * lightColor; // 漫反射光 vec3 norm = normalize(Normal); vec3 lightDir = normalize(lightPos - FragPos); float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = diff * lightColor; // 镜面反射光 float specularStrength = 0.5; vec3 viewDir = normalize(viewPos - FragPos); vec3 reflectDir = reflect(-lightDir, norm); float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); vec3 specular = specularStrength * spec * lightColor; // 合成最终颜色 vec3 result = (ambient + diffuse + specular) * objectColor; FragColor = vec4(result, 1.0); }
解释:
- 顶点着色器:
FragPos
:计算片段在世界空间中的位置。Normal
:计算片段的法线,考虑模型的变换。
- 片段着色器:
ambient
:环境光的贡献。diffuse
:漫反射光的贡献,计算光线与表面法线的夹角。specular
:镜面反射光的贡献,计算视角和反射方向的夹角。FragColor
:最终颜色的合成,包括环境光、漫反射光和镜面反射光。
5.3 Blinn-Phong光照模型
Blinn-Phong光照模型是对Phong模型的改进,主要目的是提高计算效率。Blinn-Phong模型用半角向量(lightDir + viewDir的归一化向量)代替了Phong模型中的反射向量来计算镜面反射光,这可以减少计算复杂度,特别是在处理大量光源时。
公式:
其中:
halfVec
是光线方向和视角方向的半角向量,即:
示例:Blinn-Phong光照模型的GLSL代码
顶点着色器代码:与Phong模型相同。
片段着色器代码:
#version 330 core in vec3 FragPos; // 从顶点着色器接收的片段位置 in vec3 Normal; // 从顶点着色器接收的法线 out vec4 FragColor; // 片段的最终颜色 uniform vec3 lightPos; // 光源位置 uniform vec3 viewPos; // 观察者位置 uniform vec3 lightColor; // 光源颜色 uniform vec3 objectColor; // 物体颜色 void main() { // 环境光 float ambientStrength = 0.1; vec3 ambient = ambientStrength * lightColor; // 漫反射光 vec3 norm = normalize(Normal); vec3 lightDir = normalize(lightPos - FragPos); float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = diff * lightColor; // 镜面反射光(Blinn-Phong) float specularStrength = 0.5; vec3 viewDir = normalize(viewPos - FragPos); vec3 halfVec = normalize(lightDir + viewDir); float spec = pow(max(dot(norm, halfVec), 0.0), 32); vec3 specular = specularStrength * spec * lightColor; // 合成最终颜色 vec3 result = (ambient + diffuse + specular) * objectColor; FragColor = vec4(result, 1.0); }
解释:
- Blinn-Phong模型中,镜面反射光使用
halfVec
来代替reflectDir
,计算更为高效。
5.4 Cook-Torrance光照模型
Cook-Torrance光照模型是一种基于物理的光照模型,提供了更为真实的光照效果。它考虑了光线在表面上的微观粗糙度,并引入了菲涅尔反射效应。
公式:
其中:
F
是菲涅尔反射项,计算反射的强度。G
是几何遮蔽项,考虑表面粗糙度对光照的影响。
示例:Cook-Torrance光照模型的GLSL代码
#version 330 core in vec3 FragPos; // 从顶点着色器接收的片段位置 in vec3 Normal; // 从顶点着色器接收的法线 out vec4 FragColor; // 片段的最终颜色 uniform vec3 lightPos; // 光源位置 uniform vec3 viewPos; // 观察者位置 uniform vec3 lightColor; // 光源颜色 uniform vec3 objectColor; // 物体颜色 // 菲涅尔反射项 float Fresnel(float cosTheta) { float f0 = 0.04; // 玻璃的菲涅尔反射 return f0 + (1.0 - f0) * pow(1.0 - cosTheta, 5.0); } // 几何遮蔽项 float Geometry(float NdotH, float roughness) { float k = pow(roughness + 1.0, 2.0) / 8.0; return NdotH / (NdotH * (1.0 - k) + k); } void main() { // 环境光 float ambientStrength = 0.1; vec3 ambient = ambientStrength * lightColor; // 漫反射光 vec3 norm = normalize(Normal); vec3 lightDir = normalize(lightPos - FragPos); float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = diff * lightColor; // 镜面反射光(Cook-Torrance) vec3 viewDir = normalize(viewPos - FragPos); vec3 halfVec = normalize(lightDir + viewDir); float NdotH = max(dot(norm, halfVec), 0.0); float F = Fresnel(dot(viewDir, halfVec)); float G = Geometry(NdotH, 0.5); vec3 specular = F * G * lightColor; // 合成最终颜色 vec3 result = (ambient + diffuse + specular) * objectColor; FragColor = vec4(result, 1.0); }
解释:
- Cook-Torrance模型更为复杂,考虑了菲涅尔反射效应和几何遮蔽,适用于需要高度真实感的渲染。
5.5 Lambert光照模型
Lambert光照模型是一种简单的漫反射光照模型,基于Lambert余弦定律。它假设光线均匀地散射在表面上,使得每个点的光照强度与光线和表面法线的夹角余弦成正比。
公式:
示例:Lambert光照模型的GLSL代码
#version 330 core in vec3 FragPos; // 从顶点着色器接收的片段位置 in vec3 Normal; // 从顶点着色器接收的法线 out vec4 FragColor; // 片段的最终颜色 uniform vec3 lightPos; // 光源位置 uniform vec3 lightColor; // 光源颜色 uniform vec3 objectColor; // 物体颜色 void main() { // 漫反射光(Lambert) vec3 norm = normalize(Normal); vec3 lightDir = normalize(lightPos - FragPos); float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = diff * lightColor; // 合成最终颜色 vec3 result = diffuse * objectColor; FragColor = vec4(result, 1.0); }
解释:
- Lambert模型非常简单,只考虑漫反射光,适合需要快速计算的应用。
5.6 材质属性的深入讲解
材质属性定义了物体表面的光照响应特性,包括环境反射、漫反射和镜面反射。以下是材质属性的详细介绍:
环境反射(Ambient Reflectance)
- 定义:环境反射决定了材质对环境光的响应能力。通常较低的环境反射系数会使物体看起来较为暗淡。
- 计算:环境反射的计算包括环境光强度和材质的环境反射颜色。
漫反射(Diffuse Reflectance)
- 定义:漫反射决定了材质对漫射光的响应能力。粗糙表面的漫反射效果通常比光滑表面更为明显。
- 计算:漫反射光的强度基于光源方向和表面法线之间的夹角。光源和表面法线的点积用于计算漫反射光的贡献。
镜面反射(Specular Reflectance)
- 定义:镜面反射决定了材质对镜面光的响应能力。光滑的表面会产生更强的镜面反射效果。
- 计算:镜面反射的强度基于视角和反射方向之间的夹角。光泽度控制高光的集中程度和强度。
光泽度(Shininess)
- 定义:光泽度控制镜面反射的高光强度。高光泽度会产生更加集中和锐利的高光效果。
- 计算:光泽度的计算通常通过指数函数来调整高光的强度,影响镜面反射的最终效果。
示例:材质属性的GLSL代码
#version 330 core in vec3 FragPos; // 从顶点着色器接收的片段位置 in vec3 Normal; // 从顶点着色器接收的法线 out vec4 FragColor; // 片段的最终颜色 uniform vec3 lightPos; // 光源位置 uniform vec3 viewPos; // 观察者位置 uniform vec3 lightColor; // 光源颜色 uniform vec3 materialAmbient; // 材质环境反射 uniform vec3 materialDiffuse; // 材质漫反射 uniform vec3 materialSpecular; // 材质镜面反射 uniform float materialShininess; // 材质光泽度 void main() { // 环境光 float ambientStrength = 0.1; vec3 ambient = ambientStrength * materialAmbient * lightColor; // 漫反射光 vec3 norm = normalize(Normal); vec3 lightDir = normalize(lightPos - FragPos); float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = diff * materialDiffuse * lightColor; // 镜面反射光 float specularStrength = 0.5; vec3 viewDir = normalize(viewPos - FragPos); vec3 reflectDir = reflect(-lightDir, norm); float spec = pow(max(dot(viewDir, reflectDir), 0.0), materialShininess); vec3 specular = specularStrength * spec * materialSpecular * lightColor; // 合成最终颜色 vec3 result = ambient + diffuse + specular; FragColor = vec4(result, 1.0); }
解释:
- 材质属性:包括环境反射、漫反射、镜面反射和光泽度,影响物体在不同光照条件下的视觉效果。
- 环境反射:决定物体在暗环境中的基础亮度。
- 漫反射:影响物体在光源照射下的亮度分布。
- 镜面反射:影响高光的强度和集中度。
- 光泽度:控制镜面反射的锐利程度。
小结
本章介绍了光照和材质的基本概念及其在GLSL中的实现,包括环境光、漫反射光和镜面反射光的计算方法。通过实现各种光照模型,我们可以创建更加逼真的3D场景。掌握光照和材质的编程技巧有助于提升渲染效果的真实感和视觉效果,为复杂场景的开发奠定了基础。