摘抄“GPU Programming And Cg Language Primer 1rd Edition” 中文 名“GPU编程与CG语言之阳春白雪下里巴人”
BRDF 光照模型
10.2.1 什么是 BRDF 光照模型
1965 年, Nicodemus, Fred 在论文 “Directional reflectance and emissivity of an opaque surface ” 中提出了 BRDF 的概念。 BRDF , Bidirectional Reflectance Distribution Function ,中文翻译为 “ 双向反射分布函数 ” 。该函数描述了入射光线在非透明物体表面如何进行反射。
BRDF 的结果是一个没有单位的数值,表示在给定入射条件下,某个出射方向上反射光的相对能量,也可以理解为 “ 入射光以特定方向离开的概率 ” (实时计算机图形学第二版 111 页)。如 图 23 所示, Wi表示光线入射方向, Wo表示光线出射方向(入射点到视点),则该情况下的 BRDF 值 表示:光线以Wi 方向入射,然后以 Wo方向出射的概率,或者光强。 这些信息也可以用仪器进行测试记录,并存放在图片上,称为 polynomial texture map 。
依据光学原理, BRDF 的计算公式为:
其中 Lr(Wo)表示从 Wo方向反射的光线的辐射亮度( Radiance ); Ei(Wi)表示从 Wi方向入射的光线在辐射照度( Irradiance )。辐射亮度和辐射照度是表示光照性质的光学量,辐射亮度是每单位 立体角 在垂直于给定方向的平面上的单位正投影面积上的 功率 。辐射照度则是整个入射表面的功率,等于投射在包括该点的一个面元上的辐射通量 dφ 除以该面元的面积 dA 。故而,从物理光学上我们可以将公式理解为: BRDF 函数计算的是 “ 特定反射方向的光强与入射光强的比例 ” 。
所以给定一个具体的 BRDF 数学描述后,就可以放到 rendering equation 中使用(参阅 9.4 节)。
10.2.2 什么是各向异性
各向异性 (anisotropy ) 与 均向性 相反,是指在不同方向具有不同行为的性质,也就是其行为与方向有关。如在物理学上,沿着材料做不同方向的量测,若会出现不同行为,通常称该材料具有某种 “ 各向异性 ” ,这样的材料表面称为各向异性表面( anisotropic surface );
特殊的晶体结构会导致各向异性,材质表面上存在有组织的细小凹凸槽也会导致各向异性。各向异性反射是指:各向异性表面反射光的一种现象。在生活中我们经常见到各向异性光照效果,例如光滑的炊具上的扇面光斑( 图 24 所示)。
由于材质有组织的细微凹凸结构的不同,各向异性也分为基本的三种类型(如 图 25 所示):
1. 线性各向异性;
2. 径向各向异性;
3. 圆柱形各向异性,实际上线性各向异性,单被映像为圆柱形。
10.3 Bank BRDF 经验模型
Bank BRDF 属于经验模型,由于其计算简单,且效果良好,所以该模型在各向异性光照效果的模拟方面非常有用。 Bank BRDF 的镜面反射部分可以表达为公式 ( 10-14 )的 形式:
Ks、 ns分别表示镜面反射系数和高光系数; L表示入射光线方向、V 表示实现观察方向、 T表示该点的切向量。尤其要注意切向量的计算方法,因为一个三维空间点可能存在无数个切向量,通常我采用 “ 顶点的法向量和视线方向做叉积,其结果作为 T 。
Bank BRDF 模型渲染效果如 图 26 、 图 27 所示。 图 27 的渲染图非常明显的呈现出各向异性的光照效果。
下面分别给出 Bank BRDF 的顶点着色程序和片段着色程序代码。
代码 10 Bank BRDF 的顶点着色程序
void main_v(float4 position : POSITION,
float4 normal : NORMAL,
out float4 oPosition : POSITION,
out float3 worldPos : TEXCOORD0,
out float3 worldNormal : TEXCOORD1,
uniform float4x4 modelViewProj,
uniform float4x4 worldMatrix,
uniform float4x4 worldMatrix_IT)
{
oPosition = mul(modelViewProj, position);
worldPos = mul(worldMatrix, position).xyz;
worldNormal = mul(worldMatrix_IT, normal).xyz;
}
代码 11 Bank BRDF 片段着色程序
void main_f(float4 position : TEXCOORD0,
float3 normal : TEXCOORD1,
out float4 color : COLOR,
uniform float3 globalAmbient,
uniform float3 lightColor,
uniform float3 lightPosition,
uniform float3 eyePosition,
uniform float3 Ka,
uniform float3 Kd,
uniform float3 Ks,
uniform float shininess)
{
float3 P = position.xyz;
float3 N = normalize(normal);
float3 ambient = Ka * globalAmbient; // 计算环境光分量
float3 L = normalize(lightPosition - P);
float ln = max(dot(L, N), 0);
float3 diffuse = Kd * lightColor *ln; // 计算有向光漫反射分量
// 计算镜面反射分量
float3 V = normalize(eyePosition - P);
float3 H = normalize(L + V);
float3 specular = float3(0.0,0.0,0.0);
bool back = (dot(V,N)>0) && (dot(L,N));
if(back)
{
float3 T = normalize(cross(N,V)); // 计算顶点切向量
float a = dot(L,T);
float b = dot(V,T);
float c = sqrt(1-pow(a,2.0))* sqrt(1-pow(b,2.0)) - a*b; // 计算 Bank BRDF 系数
float brdf = Ks* pow(c, shininess);
specular = brdf * lightColor *ln;
}
color.xyz = ambient + diffuse + specular;
color.w = 1;
}