17.3 对天空进行纹理映射
在本节中, 我们使用一个环境贴图对天空进行纹理映射。 我们创建了一个包围整个场景的椭圆体(用椭圆体来模拟一个较为平坦的天空表面)。我们通过图 17.6 所示的方法,使用一个环境贴图对椭圆体进行纹理映射,将环境贴图投影到椭圆体表面上,模拟天地相接处的远山假象。

我们假设天空椭圆体无限遥远(即,它的中心与世界空间的原点对齐,但是有无限大的半径),无论摄像机如何在世界空间中移动,我们永远都无法靠近或远离天空椭圆体表面。要实现这个无限遥远的天空,我们只需要在世界空间中将天空椭圆体的中心和摄像机的位置对齐,让它跟着摄像机移动。这样无论摄像机移动到哪,我们都不会靠近天空椭圆体表面。如果不这样做,那么当摄像机与天空椭圆体表面越来越近时,整个假象就会被拆穿,我们用来模拟天空的这个小把戏就没什么意思了。
用来实现天空的effect文件如下:
//=============================================================================
// Sky.fx by Frank Luna (C) 2011 All Rights Reserved.
//
// Effect used to shade sky dome.
//=============================================================================
cbuffer cbPerFrame
{
float4x4 gWorldViewProj;
};
// Nonnumeric values cannot be added to a cbuffer.
TextureCube gCubeMap;
SamplerState samTriLinearSam
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Wrap;
AddressV = Wrap;
};
struct VertexIn
{
float3 PosL : POSITION;
};
struct VertexOut
{
float4 PosH : SV_POSITION;
float3 PosL : POSITION;
};
VertexOut VS(VertexIn vin)
{
VertexOut vout;
// 将z设置为w,这样z/w = 1 (即天空球总是在位于平面).
vout.PosH = mul(float4(vin.PosL, 1.0f), gWorldViewProj).xyww;
// 使用局部顶点位置作为立方体贴图的查找向量
vout.PosL = vin.PosL;
return vout;
}
float4 PS(VertexOut pin) : SV_Target
{
return gCubeMap.Sample(samTriLinearSam, pin.PosL);
}
RasterizerState NoCull
{
CullMode = None;
};
DepthStencilState LessEqualDSS
{
// 将深度测试函数设置为LESS_EQUAL而不是LESS.
// 否则,当深度缓冲清除为1时,z=1(NDC)处规范化深度值将通不过深度测试。
DepthFunc = LESS_EQUAL;
};
technique11 SkyTech
{
pass P0
{
SetVertexShader( CompileShader( vs_5_0, VS() ) );
SetGeometryShader( NULL );
SetPixelShader( CompileShader( ps_5_0, PS() ) );
SetRasterizerState(NoCull);
SetDepthStencilState(LessEqualDSS, 0);
}
}
注意:在以前,应用程序会先绘制天空,把它作为一个替代物来清空渲染目标和深度/模板缓冲区。不过,现在出于以下原因《ATI Radeon HD 2000编程指南》建议我们不要再使用这种方式。首先,深度/模板缓冲区应该被直接清空,这样可以获得硬件内部的深度优化,有利于提高性能。这一点与渲染目标的情形相似。其次,天空通常会被其他物体阻挡,比如建筑物和地形。当我们先绘制天空时,天空图像会被与摄像机距离更近的其他物体覆盖,是对资源的浪费。所以,现在的首选方式是始终调用Clear***方法清空各个缓冲区,最后绘制天空。
文件下载(已下载 970 次)发布时间:2014/8/20 下午9:21:09 阅读次数:4741
