对于天空盒如何制作,我这里就不介绍了,网上有好些制作天空盒的资料,也可看我的其它转载关于天空盒的资料。这里只讲一种简单的方法实现天空盒。这种方法的基于,网上http://blog.csdn.net/xoyojank/article/details/1628932这里提出的用D3DFVF_XYZRHW实现天空盒的想法。不过有个问题,D3DFVF_XYZRHW本身会跳过坐标变换的阶段,也就是说它不会传入VertexShader和视口变换阶段,然而我们的纹理坐标却要在VertexShader中生成,因为在PixelShader中不能使用POSITION,这就很麻烦,于是想用D3DFVF_XYZW,可惜的也是会跳过VertexShader阶段。所以没办法,自能自己用编码实现类D3DFVF_XYZW的功能了,其实弄下来是很简单的,没有那么麻烦。
好了,开始了:
(1)首先准备一张CUBE纹理图
(2)声明顶点坐标
struct Vertex{
float x,y,z;//注意只要这几个坐标即可,无需纹理坐标,因为纹理坐标会在VertexShader去生成的
static const DWORD FVF;
}
const DWORD Vertex::FVF=D3DFVF_XYZ;
(3)装入纹理图
IDirect3DCubeTexture9 *g_pBoxCubeTexture=NULL;
char szCubePath[64]="sky_box0.dds";
D3DXCreateCubeTextureFromFile(Device,szCubePath,&g_pBoxCubeTexture);
(4)这一步很重要,对与顶点的部署,因为天空盒是铺满整个屏幕的,所以我们要渲染的几个三角形要把屏幕铺满,在最简单的情况下只要两个三角形凑成的矩形即可把屏幕覆盖,而且在无穷远处,但是我们的相机里的远裁剪面就知道,不能有所谓的无穷远,如何来构成无穷远呢?我们知道在透视变换之后x,y在(-1.0f,1.0f),而z在(0.0f,1.0f)直接之间,这就是说在Z-Buffer中,最近的是z=0.0f处,最远在z=1.0f处,其实这也是D3DFVF_XYZRHW的效果,所以我们只要保持我们的覆盖矩阵z=1.0f即可。我们要仿照的D3DFVF_XYZRHW也要仿照D3DFVF_XYZW,我们要是我们输入的图形覆盖掉屏幕,那么类似D3DFVF_XYZRHW,但我们要经过VertexShader,这里就和D3DFVF_XYZRHW不同了,假如我们经过了VertexShader,那么也要经过视口变换,所以,我们输入的顶点坐标要不能是视口(屏幕)坐标,而只是透视坐标中覆盖的屏幕,由于我们前面知道透视坐标的范围,所以也很容易得出,下面我给出顶点:
g_pDevice->CreateVertexBuffer(6*sizeof(Vertex),D3DUSAGE_WRITEONLY,Vertex::FVF,D3DPOOL_MANAGED,&g_pSkyBox,NULL);
Vertex *pV=NULL;
g_pSkyBox->Lock(0,0,(void **)&pV,0);
pV[0]=Vertex(-1.0f,1.0f,1.0f);//(注意在透视坐标下,屏幕中心是(0.0f,0.0f),向上为y正轴,右为x正轴,和我们几何里的坐标轴一样)
pV[1]=Vertex(1.0f,1.0f,1.0f);//(所以,右上角为(-1.0f,1.0f),右下角为(-1.0f,-1.0f),左上角及右下角自己推)
pV[2]=Vertex(1.0f,-1.0f,1.0f);//(我们这里的z一定要是1.0f,这才能表示它处于视觉的最末尾处,而且才能被我们场景覆盖)
pV[3]=Vertex(-1.0f,1.0f,1.0f);
pV[4]=Vertex(1.0f,-1.0f,1.0f);
pV[5]=Vertex(-1.0f,-1.0f,1.0f);
g_pSkyBox->Unlock();
(5)下面是处理天空盒的效果文件
/*
作者:秋风扫落叶
时间:2012/5/19 11:30
*/
/*
此effect完成天空盒渲染
*/
//由于CUBETexture是使用向量来表示纹理坐标的,具体为什么经过逆变换之后可以获取纹理坐标这里不讲,请查看推荐的那篇文章,或留言
matrix invWorldViewProj;//逆向转换矩阵,因为我们用的是变换过的坐标,这里的矩阵并不是World,View,Proj相乘的逆矩阵,后面在主控程序区会说
texture SkyBoxTexture;//纹理对象,是我们的CubeTexture
//我们的纹理采样器
samplerCUBE skyboxCube=sampler_state
{
Texture=(SkyBoxTexture);
MinFilter=Linear;
MagFilter=Linear;
AddressU=Clamp;//注,这里不能是Wrap,因为立方体纹理,无此
AddressV=Clamp;
AddressW=Clamp;
};
//顶点输入结构
struct VS_INPUT{
float4 pos:POSITION;
};
//顶点输出结构,注意虽然在输入结构中没有纹理坐标,但是在输出中可以有纹理坐标
struct VS_OUTPUT{
float4 pos:POSITION;
float3 texcoord:TEXCOORD;
};
VS_OUTPUT VSMain(VS_INPUT input){
VS_OUTPUT output=(VS_OUTPUT)0;
//这里,因为我们的坐标就是在透视平面中所以没有所谓的变换,直接放到下游进行视口变换
output.pos=input.pos;
//获取纹理坐标
output.texcoord=normalize(mul(input.pos,invWorldViewProj));
return output;
}
vector PSMain(VS_OUTPUT input):COLOR
{
//获取像素颜色
return texCUBE(skyboxCube,input.texcoord);
}
//technique
technique SkyBox{
pass p0
{
VertexShader=compile vs_2_0 VSMain();
PixelShader=compile ps_2_0 PSMain();
}
}
(6)我们要编译效果文件,假定你会编译
我们如何渲染呢
//在此之前要设置好矩阵,g_mWorld是世界矩阵,g_mView是相机,g_mProj是透视矩阵
void RenderSkyBox(IDirect3DDevice9 *pDevice)
{
//关闭关照
pDevice->SetRenderState(D3DRS_LIGHTING,false);
D3DXMATRIX view;
D3DXMATRIX mInvWorldViewProj;
//获取相机矩阵,这里不用g_mView是不想破坏矩阵
pDevice->GetTransform(D3DTS_VIEW,&view);
//这里是最重要的,一定不能有平移,我们要保证视点在原点,因为我们的天空盒矩形位置没有变过,而纹理坐标是视点到各顶点(天空盒)的向量,接入视点改变,那么,顶点上的纹理坐标会扭曲。(这里很重要,所以大家请思考)
view._41=0.0f;
view._42=0.0f;
view._43=0.0f;
//获取逆矩阵
D3DXMatrixInverse(&mInvWorldViewProj,NULL,&(g_mWorld*view*g_mProj));
//设置相关效果文件常量
g_pEffect->SetMatrix("invWorldViewProj",&mInvWorldViewProj);
g_pEffect->SetTexture("SkyBoxTexture", g_pBoxCubeTexture);//设置纹理,这一句可以放在初始化中
//渲染天空盒
D3DXHANDLE hTechnique=g_pEffect->GetTechniqueByName("SkyBox");
g_pEffect->SetTechnique(hTechnique);
UINT passNum;
g_pEffect->Begin(&passNum,0);
for(UINT iPass=0;iPass<passNum;iPass++)
{
g_pEffect->BeginPass(iPass);
pDevice->SetStreamSource(0,g_pSkyBox,0,sizeof(Vertex));
pDevice->SetFVF(Vertex::FVF);
pDevice->DrawPrimitive(D3DPT_TRIANGLELIST,0,2);
g_pEffect->EndPass();
}
g_pEffect->End();
pDevice->SetRenderState(D3DRS_LIGHTING,true);
}
到此就完成了,可惜的是会有明显的分割线,所以还需改进,或者获取一张质量更好的天空盒图片
下面是截图: