使用shader实现各种光照模型

69

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编程指南》