环境映射
环境映射模拟一个物体对于它周围的环境的映射,它可以使用简单的技术展示绚丽的反射折射效果。实际上就是将一张包含周围环境场景的环境贴图贴到物体表面,这样就可以在一定程度上模拟物体对于周围环境的映射而不用去使用光线追踪之类复杂的算法技术。环境贴图,顾名思义,在一副图片上展示周围的场景环境。环境贴图假设进行反射的光源和物体都处于很远的位置,同时反射体不会反射自身。最常使用的就是立方体贴图。
立方体贴图
立方体贴图是由6副组合在一起正好可以形成一个立方体表面的纹理图像组成。生成立方体的方法是将摄像机放到环境最中央,摄像机使用90°视角和正方形横纵比给上下左右前后6个方向拍摄快照。这样生成的6各面紧密缝合便形成一个全方位视景图。立方体贴图的文件形式在DirectX中可以保存为dds格式。
一副2D纹理通过映射一个2D纹理坐标来确定单独纹理图像中的颜色。相比较,你可以将立方体贴图的纹理坐标想象成一个三维坐标集。这个向量看成是从立方体中心射出光线与立方体贴图的表面相交点。当你观察一个高度反射的物体表面上一点时,你看到的是那个点的颜色是从环境发出的光线投射到光滑表面然后反射到眼睛形成。将这个过程逆转,借助视点发出的光线进行反射并根据反射光线R向量来检索环境贴图。
注意,这与phong光照中计算反射向量并不一样,I是从眼睛到视点的向量。可得出:
这同HLSL内置Reflect函数方向一致。
要使用立方体环境映射,首先需要定义一个立方体纹理采样器samplerCUBE。在顶点渲染器中计算入射向量和反射向量,再将反射向量传给像素渲染器。像素器进行立方体纹理采样。
VS:
float4x4 matViewProjection;
float4x4 matWorld;
float4 vViewPosition;
struct VS_OUTPUT
{
float4 Position : POSITION0;
float3 Reflect : TEXCOORD0;
};
VS_OUTPUT vs_main( float4 Position :POSITION,
float3 Normal : NORMAL,
float2 Texcoord : TEXCOORD0 )
{
VS_OUTPUT Output;
Output.Position = mul( Position, matViewProjection );
float3 N = normalize(mul(Normal,matWorld));
float3 position = normalize(mul(Position,matWorld));
float3 Incident = normalize(position - vViewPosition);
Output.Reflect = normalize(reflect(Incident,N));
return( Output );
}
PS:
struct VS_OUTPUT
{
float4 Position : POSITION0;
float3 Reflect : TEXCOORD0;
};
samplerCUBE cubeTex;
float4 ps_main(VS_OUTPUT input) : COLOR0
{
return(texCUBE(cubeTex,input.Reflect));
}
最终效果如下:
反射贴图:
下面继续为效果添加折射部分。折射通过斯涅尔定律(Snell's Law)计算。
斯涅耳定律: n1sinα1=n2sinα2
公式中n1和n2分别是两个介质的折射系数,α1 是L和N之间的夹角,α2是Q和N的夹角。
HLSL有一个内置函数refract,这个函数根据斯涅耳定律基于入射向量、法线和折射率的比值计算折射向量。
光线照射到透明物体上时,一部分发生反射,一部分进入物体内部并在介质交界处发生折射,被反射和折射的光通量存在一定的比率关系,这个比率关系可以通过 Fresnel 定律进行计算。相关知识在此不加赘述,将在后面的水面渲染时进行详细阐述。这里我们使用固定系数模拟。
vs:
float4x4 matViewProjection;
float4x4 matWorld;
float4 vViewPosition;
struct VS_OUTPUT
{
float4 Position : POSITION0;
float3 Reflect : TEXCOORD0;
float3 Refract : TEXCOORD1;
};
VS_OUTPUT vs_main( float4 Position :POSITION,
float3 Normal : NORMAL,
float2 Texcoord : TEXCOORD0 )
{
VS_OUTPUT Output;
Output.Position = mul( Position, matViewProjection );
float3 N = normalize(mul(Normal,matWorld));
float3 position = normalize(mul(Position,matWorld));
float3 Incident = normalize(position - vViewPosition);
Output.Reflect = normalize(reflect(Incident,N));
Output.Refract = normalize(refract(Incident,N,1.5));
return( Output );
}
PS:
struct VS_OUTPUT
{
float4 Position : POSITION0;
float3 Reflect : TEXCOORD0;
float3 Refract : TEXCOORD1;
};
samplerCUBE cubeTex;
float4 ps_main(VS_OUTPUT input) : COLOR0
{
return(texCUBE(cubeTex,input.Reflect)*0.6 + texCUBE(cubeTex,input.Refract)*0.4);
}
最值效果如下: