经典光照模型(illumination model)
物体表面光照颜色由入射光、物体材质,以及材质和光的交互规律共同决定。
由于环境光给予物体各个点的光照强度相同,且没有方向之分,所以在只有环境光的情况下,同一物体各点的明暗程度均一样。
环境光是对光照现象的最简单抽象,局限性很大。它仅能描述光线在空间中无方向并均匀散布时的状态。
还有一种是平行光,即光线都从同一个方向照射。通过模拟方向光和物体表面的交互模式,可以渲染出具有高真实感(明暗变化、镜面反射等)的三维场景。
漫反射与Lambert 模型
粗糙的物体表面向各个方向等强度地反射光,这种等同地向各个方向散射的现象称为光的漫反射(diffuse reflection)
对于仅暴露在环境光下的朗伯反射体,可以用公式(9-1)表示某点处漫反射的光强:
其中Ia表示环境光强度(简称光强),dk(0
当方向光照射到朗伯反射体上时,漫反射光的光强与入射光的方向和入射点表面法向夹角的余弦成正比,这称之为Lambert 定律,并由此构造出Lambert漫反射模型:
I是点光源强度,θ是入射光方向与顶点法线的夹角,称为入射(0≤θ ≤90°)
若N 为顶点单位法向量,L 表示从顶点指向光源的单位向量(注意,是由顶点指向光源,不要弄反了),则cosθ 等价于N 与L 的点积。所以公式(9-2)可以表示为公式(9-3):
漫反射渲染
漫反射光照模型顶点着色程序(使用结构体)
struct VertexIn { float4 position : POSITION; float4 normal : NORMAL; }; struct VertexScreen { float4 oPosition : POSITION; float4 color : COLOR; }; void main_v(VertexIn posIn, out VertexScreen posOut, uniform float4x4 modelViewProj, uniform float4x4 worldMatrix, uniform float4x4 worldMatrix_IT, uniform float3 globalAmbient, uniform float3 lightPosition, uniform float3 lightColor, uniform float3 Kd) { posOut.oPosition = mul(modelViewProj, posIn.position); float3 worldPos = mul(worldMatrix, posIn.position).xyz; float3 N = mul(worldMatrix_IT, posIn.normal).xyz; N = normalize(N); //计算入射光方向 float3 L = lightPosition - worldPos; L = normalize(L); //计算方向光漫反射光强 float3 diffuseColor = Kd*lightColor*max(dot(N, L), 0); //计算环境光漫反射光强 float3 ambientColor = Kd*globalAmbient; posOut.color.xyz = diffuseColor+ambientColor; posOut.color.w = 1; }
镜面反射与Phong 模型
Lambert 模型较好地表现了粗糙表面上的光照现象,但表现不出光泽,主要原因是该模型没有考虑这些表面的镜面反射效果。
Phong Bui Tuong 提出一个计算镜面反射光强的经验模型,称为phong模型,认为镜面反射的光强与反射光线和视线的夹角相关,其数学表达如公式(9-5)所示:
k 为材质的镜面反射系数, Ns是高光指数,V表示从顶点到视点的观察方向, R代表反射光方向。
高光指数反映了物体表面的光泽程度。Ns越大,反射光越集中,当偏离反射方向时,光线衰减的越厉害,只有当视线方向与反射光线方向非常接近时才能看到镜面反射的高光现象,此时,镜面反射光将会在反射方向附近形成亮且小的光斑; Ns越小,表示物体越粗糙,反射光分散,观察到的光斑区域小,强度弱。
反射光的方向R 可以通过入射光方向L(从顶点指向光源)和物体法向量N求出:
phong 模型渲染
phong 光照模型的顶点着色程序实现
struct VertexIn { float4 position : POSITION; // Vertex in object-space float4 normal : NORMAL; }; struct VertexScreen { float4 oPosition : POSITION; float4 color : COLOR; }; void main_v( VertexIn posIn, out VertexScreen posOut, uniform float4x4 modelViewProj, uniform float4x4 worldMatrix, uniform float4x4 worldMatrix_IT, uniform float3 globalAmbient, uniform float3 eyePosition, uniform float3 lightPosition, uniform float3 lightColor, uniform float3 Kd, uniform float3 Ks, uniform float shininess) { posOut.oPosition = mul(modelViewProj, posIn.position); float3 worldPos = mul(worldMatrix, posIn.position).xyz; float3 N = mul(worldMatrix_IT, posIn.normal).xyz; N = normalize(N); //计算入射光方向、视线方向、反射光线方向 float3 L = normalize(lightPosition - worldPos); float3 V = normalize(eyePosition - worldPos); float3 R = 2*max(dot(N, L), 0)*N-L; R = normalize(R); // 计算漫反射分量 float3 diffuseColor = Kd * globalAmbient+Kd*lightColor*max(dot(N, L), 0); //计算镜面反射分量 float3 specularColor = Ks * lightColor*pow(max(dot(V, R), 0), shininess); posOut.color.xyz = diffuseColor + specularColor; posOut.color.w = 1; }
struct VertexIn { float4 position : POSITION; float4 normal : NORMAL; }; struct VertexScreen { float4 oPosition : POSITION; float4 objectPos : TEXCOORD0; float4 objectNormal : TEXCOORD1; };
首先将几何顶点的模型空间坐标转换为用于光栅化的投影坐标;然后将顶点模型坐标和法向量模型坐标赋值给绑定TEXCOORD 语义词的变量,用于传递到片段着色程序中。
phong 光照模型顶点着色程序
void main_v(VertexIn posIn, out VertexScreen posOut, uniform float4x4 modelViewProj) { posOut.oPosition = mul(modelViewProj, posIn.position); posOut.objectPos = posIn.position; posOut.objectNormal = posIn.normal; }
phong 光照模型片段着色程序
void main_f( VertexScreen posIn, out float4 color : COLOR, uniform float4x4 worldMatrix, uniform float4x4 worldMatrix_IT, uniform float3 globalAmbient, uniform float3 eyePosition, uniform float3 lightPosition, uniform float3 lightColor, uniform float3 Kd, uniform float3 Ks, uniform float shininess) { float3 worldPos = mul(worldMatrix, posIn.objectPos).xyz; float3 N = mul(worldMatrix_IT, posIn.objectNormal).xyz; N = normalize(N); //计算入射光方向、视线方向、反射光线方向 float3 L = normalize(lightPosition - worldPos); float3 V = normalize(eyePosition - worldPos); float3 R = 2*max(dot(N, L), 0)*N-L; R = normalize(R); // 计算漫反射分量 float3 diffuseColor = Kd * globalAmbient+Kd*lightColor*max(dot(N, L), 0); //计算镜面反射分量 float3 specularColor = Ks * lightColor*pow(max(dot(V, R), 0), shininess); color.xyz = diffuseColor + specularColor; color.w = 1; }
Blinn-Phong 光照模型
phong光照模型中,必须计算V R的值,其中R为反射光线方向单位向量,V 为视线方向单位向量,但是在Blinn-phong光照模型中,用N H 的值取代了V R。Blinn-phong光照模型公式为:
H 是“光入射方向L 和视点方向V 的中间向量”,通常也称之为半角向量。
Blinn-phong 模型片段着色程序
void main_f(VertexScreen posIn, out float4 color : COLOR, uniform float4x4 worldMatrix, uniform float4x4 worldMatrix_IT, uniform float3 globalAmbient, uniform float3 eyePosition, uniform float3 lightPosition, uniform float3 lightColor, uniform float3 Kd, uniform float3 Ks, uniform float shininess) { float3 worldPos = mul(worldMatrix, posIn.objectPos).xyz; float3 N = mul(worldMatrix_IT, posIn.objectNormal).xyz; N = normalize(N); //计算入射光方向/视线方向/半角向量 float3 L = normalize(lightPosition - worldPos); float3 V = normalize(eyePosition - worldPos); float3 H = normalize(L + V); // 计算漫反射分量 float3 diffuseColor = Kd * globalAmbient+Kd*lightColor*max(dot(N, L), 0); //计算镜面反射分量 float3 specularColor = Ks * lightColor*pow(max(dot(N, H), 0), shininess); color.xyz = diffuseColor + specularColor; color.w = 1; }