天空盒
就是围绕摄像机的360°的风景照。实质其实就是围绕着摄像机的立方体,而摄像机就在这个立方体的里面。可以用来模拟无限的天空,山脉等现象。
为什么要使用天空盒
在3D游戏中,一般来说要渲染的东西会比较多,而使用天空盒会节约部分渲染的时间;而且如果不采用“天空盒”技术,或者其他技术,而直接进行渲染天空,地面以及周围场景,那么可能会因为距离把握不当,造成“露馅”。
除了在游戏中使用“天空盒”技术较多,在一些二次元动画中也会经常使用到,如MMD(miku miku dance)。
天空盒的结构
就如同一个立方体一样
显然可知,该立方体共有8个顶点,6个面,因此最多需要6张纹理图,这6张纹理图分别覆盖该立方体的不同面。
//SkyBox's FVFstruct SKYBOXVERTEX{float x, y, z;float u, v;};
#define D3DFVF_SKYBOX D3DFVF_XYZ|D3DFVF_TEX1
class CSkyModel{public:CSkyModel(LPDIRECT3DDEVICE9 pDevice);virtual ~CSkyModel();//Data types & constantsenum ModelType{k_box,k_hemisphere,k_sphere,k_dome};bool Init_SkyBox(float Length); //Initialize the sky boxbool Load_SkyTextureFromFile(wchar_t *pFrontTextureFile, wchar_t *pBackTextureFile, wchar_t *pLeftTextureFile, \wchar_t *pRightTextureFile, wchar_t *pTopTextureFile, wchar_t *pBottomTextureFile);void Render_SkyBox(D3DXMATRIX *pMatWorld, bool bRenderFrame); //the first parameter is the sky box's world matrix,//the second parameter is doing or not doing render the line (ÊÇ·ñäÖȾ³öÏß¿ò)bool Init_Floor(float length);void Render_Floor(bool bRenderFrame);private:ModelType m_type;LPD3DXMESH m_pMesh;D3DXHANDLE m_hUVSettings;LPDIRECT3DDEVICE9 m_pd3dDevice; //D3D Device objectLPDIRECT3DVERTEXBUFFER9 m_pVertexBuffer; //the vertex buffer objectLPDIRECT3DVERTEXBUFFER9 m_pFloorVerBuffer; //the floor's vertex bufferLPDIRECT3DTEXTURE9 m_pTexture[6];float m_Length; //the length of skybox};
首先是定义天空盒的顶点格式,x,y,z分别代表坐标位置,u,v代表纹理坐标。
然后再定义其灵活顶点格式。
在类中,我声明了关于立方体长度的变量,以及存储6个纹理的数组;也声明了关于天空盒的类型,是天空盒还是天空球,或还是其他类型。在Demo中只实现了天空盒一种,且用旋转的方式进行模拟天空移动。
该类的定义:
//
//Name: CSkyModel Class
//
CSkyModel::CSkyModel(LPDIRECT3DDEVICE9 pDevice)
{m_pd3dDevice = pDevice;m_pVertexBuffer = NULL;for (size_t i = 0; i != 6; i++){m_pTexture[i] = NULL;}m_Length = 0.0f;
}CSkyModel::~CSkyModel()
{SAFE_RELEASE(m_pd3dDevice);SAFE_RELEASE(m_pVertexBuffer);SAFE_RELEASE(m_pFloorVerBuffer);for (size_t i = 0; i != 6; ++i){SAFE_RELEASE(m_pTexture[i]);}
}bool CSkyModel::Init_SkyBox(float Length)
{m_Length = Length;//1.创建。创建顶点缓存m_pd3dDevice->CreateVertexBuffer(24 * sizeof(SKYBOXVERTEX), 0,D3DFVF_SKYBOX, D3DPOOL_MANAGED, &m_pVertexBuffer, 0);//用一个结构体把顶点数据先准备好SKYBOXVERTEX vertices[] ={//前面的四个顶点{ -m_Length / 2, 0.0f, m_Length / 2, 0.0f, 1.0f, },{ -m_Length / 2, m_Length / 2, m_Length / 2, 0.0f, 0.0f, },{ m_Length / 2, 0.0f, m_Length / 2, 1.0f, 1.0f, },{ m_Length / 2, m_Length / 2, m_Length / 2, 1.0f, 0.0f, },//背面的四个顶点{ m_Length / 2, 0.0f, -m_Length / 2, 0.0f, 1.0f, },{ m_Length / 2, m_Length / 2, -m_Length / 2, 0.0f, 0.0f, },{ -m_Length / 2, 0.0f, -m_Length / 2, 1.0f, 1.0f, },{ -m_Length / 2, m_Length / 2, -m_Length / 2, 1.0f, 0.0f, },//左面的四个顶点{ -m_Length / 2, 0.0f, -m_Length / 2, 0.0f, 1.0f, },{ -m_Length / 2, m_Length / 2, -m_Length / 2, 0.0f, 0.0f, },{ -m_Length / 2, 0.0f, m_Length / 2, 1.0f, 1.0f, },{ -m_Length / 2, m_Length / 2, m_Length / 2, 1.0f, 0.0f, },//右面的四个顶点{ m_Length / 2, 0.0f, m_Length / 2, 0.0f, 1.0f, },{ m_Length / 2, m_Length / 2, m_Length / 2, 0.0f, 0.0f, },{ m_Length / 2, 0.0f, -m_Length / 2, 1.0f, 1.0f, },{ m_Length / 2, m_Length / 2, -m_Length / 2, 1.0f, 0.0f, },//上面的四个顶点{ m_Length / 2, m_Length / 2, -m_Length / 2, 1.0f, 0.0f, },{ m_Length / 2, m_Length / 2, m_Length / 2, 1.0f, 1.0f, },{ -m_Length / 2, m_Length / 2, -m_Length / 2, 0.0f, 0.0f, },{ -m_Length / 2, m_Length / 2, m_Length / 2, 0.0f, 1.0f, },//bottom{-m_Length / 2,0,m_Length / 2,0,0},{-m_Length / 2,0,-m_Length / 2,0,1},{m_Length / 2,0,m_Length / 2,1,0},{m_Length / 2,0,-m_Length / 2,1,1},};//准备填充顶点数据void *pVertices;//Lockm_pVertexBuffer->Lock(0, 0, (void**)&pVertices, 0);//access 把结构体中的数据直接拷到顶点缓冲区中memcpy(pVertices, vertices, sizeof(vertices));//UnLockm_pVertexBuffer->Unlock();//Floorfloat _length = 5000.0f;this->Init_Floor(_length);return true;
}bool CSkyModel::Init_Floor(float tLength)
{//1.创建。创建顶点缓存m_pd3dDevice->CreateVertexBuffer(4 * sizeof(SKYBOXVERTEX), 0,D3DFVF_SKYBOX, D3DPOOL_MANAGED, &m_pFloorVerBuffer, 0);//用一个结构体把顶点数据先准备好SKYBOXVERTEX vertices[] ={//底部{ -tLength,0.0f,-tLength,0.0f,30.0f },{ -tLength,0.0f,tLength,0.0f,0.0f },{ tLength,0.0f,-tLength,30.0f,30.0f },{ tLength,0.0f,tLength,30.0f,0.0f },};//准备填充顶点数据void *pVertices;//Lockm_pFloorVerBuffer->Lock(0, 0, (void**)&pVertices, 0);//access 把结构体中的数据直接拷到顶点缓冲区中memcpy(pVertices, vertices, sizeof(vertices));//UnLockm_pVertexBuffer->Unlock();return true;
}bool CSkyModel::Load_SkyTextureFromFile(wchar_t *pFrontTextureFile, wchar_t *pBackTextureFile, wchar_t *pLeftTextureFile, \wchar_t *pRightTextureFile, wchar_t *pTopTextureFile, wchar_t *pBottomTextureFile)
{//从文件加载五张纹理D3DXCreateTextureFromFileW(m_pd3dDevice, pFrontTextureFile, &m_pTexture[0]); //前面D3DXCreateTextureFromFileW(m_pd3dDevice, pBackTextureFile, &m_pTexture[1]); //后面D3DXCreateTextureFromFileW(m_pd3dDevice, pLeftTextureFile, &m_pTexture[2]); //左面D3DXCreateTextureFromFileW(m_pd3dDevice, pRightTextureFile, &m_pTexture[3]); //右面D3DXCreateTextureFromFileW(m_pd3dDevice, pTopTextureFile, &m_pTexture[4]); //上面D3DXCreateTextureFromFileW(m_pd3dDevice, pBottomTextureFile, &m_pTexture[5]); //底部return TRUE;
}//--------------------------------------------------------------------------------------
// Name: SkyBoxClass::RenderSkyBox()
// Desc: 绘制出天空盒,可以通过第二个参数选择是否绘制出线框
//--------------------------------------------------------------------------------------
void CSkyModel::Render_SkyBox(D3DXMATRIX *pMatWorld, bool bRenderFrame)
{m_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); //将纹理颜色混合的第一个参数的颜色值用于输出m_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); //纹理颜色混合的第一个参数的值就取纹理颜色值m_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR);m_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_MIRROR);m_pd3dDevice->SetTransform(D3DTS_WORLD, pMatWorld); //设置世界矩阵m_pd3dDevice->SetStreamSource(0, m_pVertexBuffer, 0, sizeof(SKYBOXVERTEX)); //把包含的几何体信息的顶点缓存和渲染流水线相关联 m_pd3dDevice->SetFVF(D3DFVF_SKYBOX); //设置FVF灵活顶点格式//一个for循环,将6个面绘制出来for (int i = 0; i != 6; i++){m_pd3dDevice->SetTexture(0, m_pTexture[i]);m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, i * 4, 2);}//对是否渲染线框的处理代码if (bRenderFrame) //如果要渲染出线框的话{m_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); //把填充模式设为线框填充//一个for循环,将5个面的线框绘制出来for (int i = 0; i != 6; i++){m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, i * 4, 2); //绘制顶点 }m_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); //把填充模式调回实体填充}
}void CSkyModel::Render_Floor(bool bRenderFrame)
{m_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);m_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);m_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);m_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);m_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);m_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);D3DXMATRIX matWorld;D3DXMatrixTranslation(&matWorld, 0.0f, 0.0f, 0.0f);m_pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld);m_pd3dDevice->SetStreamSource(0, m_pFloorVerBuffer, 0, sizeof(SKYBOXVERTEX));m_pd3dDevice->SetFVF(D3DFVF_SKYBOX);m_pd3dDevice->SetTexture(0, m_pTexture[5]);m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);//对是否渲染线框的处理代码if (bRenderFrame) //如果要渲染出线框的话{m_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); //把填充模式设为线框填充m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); //绘制顶点 m_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); //把填充模式调回实体填充}
}
最后演示结果
源代码
CSDN内进行下载 当前CSDN已改版,无法进行调整积分值,所以不是很推荐。
天空盒实现还有其他办法,不用手动进行设置立方体的大小,可以通过导入一个立方体的模型,如.X文件,然后通过HLSL进行顶点着色器的编写,也可以实现。
如果想多了解关于3D场景以及地形渲染可以参考
Real Time 3D Terrain Engines Using C++ And Dx9