第11 章 透明光照模型与环境贴图
Snell 定律与Fresnel 定律
光在真空中的速度c 与在透明介质中的速度v 之比,如公式(11-1)所示,称之为该介质的绝对折射率,简称折射率。光在真空中的折射率等于1,通常我们认为光在空气中的折射率也近视为1。
Snell 定律描述光线从一个介质传播到另一个介质时,入射角、折射角和介质折射率的关系。
需要注意:折射率和下面将要阐述的fresnel 折射系数并不是同样的东西。折射率本质上反映的是光在介质中的运行速度,以及折射方向;而fresnel 折射系数反映的是,光在透明介质表面被折射的光通量的比率。
色散
色散分为正常色散和反常色散,通常我们所说的色散都是指反常色散,即,对光波透明的介质,其折射率随着波长的增加而减小。
Fresnel 定律
fresnel 系数指反射系数
我们用Kr表示fresnel 反射系数,用Kt表示折射系数
schlick 给出了Fresnel 反射系数的一个近似
f为入射角度接近0(入射方向靠近法向量)时的Fresnel 反射系数,V 是指向视点的观察方向,H 为半角向量。观察公式(11-3),可以得出一个结论:随着入射角趋近90,反射系数趋近1,即擦地入射时,所有入射光都被反射。
当入射角度接近0 时的fresnel 反射系数的计算方法:
当入射角度接近0 时的fresnel 折射系数的计算方法为:
综合一下,fresnel 反射系数的计算公式为:
如果想动态的计算fresnel 系数,而又不希望消耗太多的时间,可以采用公式:
环境贴图
环境贴图(Environment Mapping, EM)也称为反射贴图(Reflection Mapping),用于模拟光滑表面对周围场景的映射效果
立方体环境贴图:将相机放置在环境的最中央,然后从上、下、左、右、前、后,6 个方向拍摄周围环境,最后将这些信息投影到立方体的6 个面上,所得到的纹理称之为立方体环境贴图。
从视点发射一束射线到反射体上的一个点,然后这束射线以这个点为基准进行反射,并根据反射光线的方向向量检索环境图像的颜色。这就是环境贴图算法的基本思想。
环境贴图算法的步骤如下:
1. 首先根据视线方向和法向量计算反射向量;
2. 然后使用反射向量检索环境贴图上的纹理信息;
3. 最后将该纹理信息融合到当像素颜色中。
环境贴图在Cg 语言中属于smaplerCUBE 类型变量,标准函数库中的texCUBE 函数提供依据反射方向向量检索环境贴图的功能。
实现环境映射效果的着色代程序
void main_v( float4 position: POSITION, float4 normal: NORMAL, out float4 oposition : POSITION, out float3 R : TEXCOORD1, uniform float3 eyePosition, uniform float4x4 modelViewProj, uniform float4x4 modelToWorld) { oposition = mul( modelViewProj, position); //计算世界空间中的物体坐标和法向量 float3 positionW = mul( modelToWorld, position).xyz; float3 normalW = mul( modelToWorld_IT, normal).xyz; normalW = normalize( normalW); float3 I = positionW - eyePosition; //计算反射光线R. R = reflect( I, normalW ); } void main_f( float3 R : TEXCOORD1, out float4 color : COLOR, uniform samplerCUBE environmentMap) { color = texCUBE( environmentMap, R); }
简单透明光照模型
简单的使用颜色调和的方法,即我们最终所看到的颜色,是物体表面的颜色和背景颜色的叠加。
透明物体位于视点与另一个不透明物体之间,透明物体的不透明度为t,点A 为透明物体上的一点,点光源直接照射到A 点上产生的反射光强为Ia ,视线穿过透明体与另一个物体相交处的光强为Ib,则点A 处的最终可观察的光强为:
简单透明光照模型的着色程序如下所示。依然是首先定义结构体,用来包含输入、输出数据流,然后在顶点着色程序中计算顶点投影坐标,并将顶点模型坐标和法向量坐标传递到片段着色程序中,然后在片段着色程序中计算折射光方向向量,并根据折射光向量检索环境贴图,最后按照公式(11-8)进行颜色合成。Cg 标准函数库中的lerp 函数可以提供颜色合成功能
struct VertexIn { float4 position : POSITION; float4 normal : NORMAL; }; struct VertexScreen { float4 oPosition : POSITION; float4 objectPos : TEXCOORD0; float4 objectNormal : TEXCOORD1; }; 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; } 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, uniform float etaRatio, //折射系数 uniform float transmittance, //透明度 uniform samplerCUBE environmentMap //环境贴图 ) { 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); float3 reflectColor = diffuseColor+specularColor; //计算折射光线的方向,注意refract 的输入参数! float3 I = normalize(worldPos - eyePosition); float3 T = refract(I, N, etaRatio); //根据折射光线的方向,检索环境贴图上的颜色信息 float3 refractedColor = texCUBE(environmentMap, T).xyz; color.xyz = lerp(reflectColor, refractedColor, transmittance); color.w = 1; }