1. 概述
计算机图形学的第一定律:如果它看上去是对的,那么它就是对的。
光照模型也是一样的,它仅仅是一个经验模型,是用各种数学公式来模拟真实世界中的光照现象,并非进行真实的物理运算。 ——本质上,是一种计算性能与真实感之间的平衡,实际项目中应该用哪个光照模型,应该从项目要求的显示效果来进行选择。
这里使用glsl shader实现各种光照模型,为了追求渲染效果,均在片元着色器中计算,即采用逐像素光照。因此,顶点着色器只需要对模型坐标进行mvp变换操作,各种光照模型的顶点着色器大同小异,顶点着色器:
precision highp float;
uniform mat4 worldViewProjection;
uniform mat4 world;
uniform mat3 normalMatrix;
attribute vec3 position;
attribute vec3 normal;
varying vec3 worldPosition;
varying vec3 worldNormal;
void main() {
gl_Position = worldViewProjection * vec4(position, 1.0);
worldPosition = mat3(world) * position;
worldNormal = normalize(normalMatrix * normal);
}
2. 漫反射
2.1 lambert光照模型
lambert定律:反射光线的强度与表面法线和光源方向之间的余弦值成正比,即反射光线强度与入射角的余弦值成正比
diffuse = (lightColor * diffuseColor) * max(0, dot(normalDir, lightDir))
其中,lightColor为光源颜色,diffuseColor为材质的漫反射颜色,normalDir为法线方向,lightDir为指向光源的方向。
片元着色器:
precision highp float;
uniform vec3 ambientColor;
uniform vec3 lightColor;
uniform vec3 lightPosition;
uniform vec4 diffuseColor;
varying vec3 worldPosition;
varying vec3 worldNormal;
void main() {
vec3 lightDir = normalize(lightPosition - worldPosition);
vec3 diffuse = lightColor * diffuseColor.rgb * max(0.0, dot(worldNormal, lightDir));
gl_FragColor = vec4(ambientColor + diffuse, diffuseColor.a);
}
2.2 half lambert光照模型
lambert光照模型存在一个问题,在光照无法达到的区域,模型颜色都是全黑的,没有任何明暗变换。
此时,可以采用half lambert光照模型,即对余弦值进行缩放和偏移,让它不会变成负值
diffuse = (lightColor * diffuseColor) * (0.5 * dot(normalDir, lightDir) + 0.5)
片元着色器:
precision highp float;
uniform vec3 ambientColor;
uniform vec3 lightColor;
uniform vec3 lightPosition;
uniform vec4 diffuseColor;
varying vec3 worldPosition;
varying vec3 worldNormal;
void main() {
vec3 lightDir = normalize(lightPosition - worldPosition);
vec3 diffuse = lightColor * diffuseColor.rgb * (dot(worldNormal, lightDir) * 0.5 + 0.5);
gl_FragColor = vec4(ambientColor + diffuse, diffuseColor.a);
}
3. 高光反射
3.1 Phong光照模型
Phong光照模型是一种经验模型
specular = (lightColor * specularColor) * max(0, dot(viewDir, reflectDir)) ^ gloss
其中,lightColor为光源颜色,specularColor为高光反射颜色,viewDir为视角方向,reflectDir为反射光方向,gloss为光泽度
片元着色器:
precision highp float;
uniform vec3 ambientColor;
uniform vec3 lightColor;
uniform vec3 lightPosition;
uniform vec4 diffuseColor;
uniform vec3 cameraPosition;
uniform vec3 specularColor;
uniform float gloss;
varying vec3 worldPosition;
varying vec3 worldNormal;
void main() {
vec3 lightDir = normalize(lightPosition - worldPosition);
vec3 diffuse = lightColor * diffuseColor.rgb * max(0.0, dot(worldNormal, lightDir));
vec3 viewDir = normalize(cameraPosition - worldPosition);
vec3 reflectDir = -lightDir - 2.0 * dot(worldNormal, -lightDir) * worldNormal;
vec3 specular = lightColor * specularColor * pow(max(0.0, dot(viewDir, reflectDir)), gloss);
gl_FragColor = vec4(ambientColor + diffuse + specular, diffuseColor.a);
}
3.2 Blinn-Phong光照模型
Phong光照模型存在一个问题,反射光方向计算较为复杂,Blinn提出了引入了一个新得矢量 hDir,可以达到类似的效果
specular = (lightColor * specularColor) * max(0, dot(normalDir, hDir)) ^ gloss
其中,hDir为对viewDir和lightDir取平均后再归一化后得到:
hDir = normalize(viewDir + lightDir)
片元着色器:
precision highp float;
uniform vec3 ambientColor;
uniform vec3 lightColor;
uniform vec3 lightPosition;
uniform vec4 diffuseColor;
uniform vec3 cameraPosition;
uniform vec3 specularColor;
uniform float gloss;
varying vec3 worldPosition;
varying vec3 worldNormal;
void main() {
vec3 lightDir = normalize(lightPosition - worldPosition);
vec3 diffuse = lightColor * diffuseColor.rgb * max(0.0, dot(worldNormal, lightDir));
vec3 viewDir = normalize(cameraPosition - worldPosition);
vec3 halfDir = normalize(viewDir + lightDir);
vec3 specular = lightColor * specularColor * pow(max(0.0, dot(worldNormal, halfDir)), gloss);
gl_FragColor = vec4(ambientColor + diffuse + specular, diffuseColor.a);
}
4. PBR光照模型
PBR光照模型是当前使用最多的光照模型,有以下特点:
- 真正基于物理的光照模型,漫反射和高光反射效果更为写实
- Blinn-Phong光照模型在不同的光照场景中,显示效果需要设计微调参数;而PBR光照模型在不同的光照场景中,渲染效果都相当真实
- 使用各种纹理技术来实现复杂的效果,如金属纹理,粗糙度纹理,环境光遮蔽纹理,法线纹理,自发光纹理
具体shader实现较为复杂,后续会专门写一篇文章进行介绍
5. 参考文献
- 《Unity Shader入门精要》
- 《WebGL编程指南》