Direct3D轮回:游戏场景之天空

游戏场景中的天空效果实现起来其实很简单,我们使用一个称之为“天空盒”的技术即可~

如下是一个天空盒对象的简单实现:

/* -------------------------------------

代码清单:Skybox.h
来自:
http://www.cnblogs.com/kenkao

-------------------------------------
*/

#include 
" Texture2D.h "

#pragma  once

//  天空盒顶点缓冲结构定义
struct  VertexSkybox{
    VertexSkybox(){}
    VertexSkybox(
float  x,  float  y,  float  z,  float  nx,  float  ny,  float  nz, D3DCOLOR color,  float  u,  float  v){
        _x 
=  x; _y  =  y; _z  =  z;
        _nx 
=  nx; _ny  =  ny; _nz  =  nz;
        _color 
=  color;
        _u 
=  u; _v  =  v;
    }
    
float  _x, _y, _z;
    
float  _nx, _ny, _nz;
    D3DCOLOR _color;
    
float  _u, _v;
    
static   const  DWORD FVF;
};

class  CSkybox
{
public :
    CSkybox(
void );
    
~ CSkybox( void );
public :
    
bool  Create(                                                 //  构建天空盒
         char *  szFrontTex,                                        //  前、后、左、右、上、下 纹理路径
         char *  szBackTex,
        
char *  szLeftTex,
        
char *  szRightTex,
        
char *  szTopTex,
        
char *  szBottomTex
        );
    
void  Draw();                                                 //  绘制天空盒
     void  Release();                                              //  释放天空盒
private :
    
void  InitVertices();                                         //  初始化顶点及索引缓冲区
private :
    CTexture2D
**             m_ppTextureArray;                    //  天空盒纹理数组
    VertexSkybox *            m_pVertex;                           //  顶点缓冲
    UINT16 *                  m_pIndices;                          //  索引缓冲
    D3DXMATRIX              m_WorldTransMatrix;                  //  当前世界变换矩阵
    D3DXMATRIX              m_OriWorldTransMatrix;               //  原始世界变换矩阵
};

 

Direct3D轮回:游戏场景之天空 Skybox.cpp
/* -------------------------------------

代码清单:Skybox.cpp
来自:
http://www.cnblogs.com/kenkao

-------------------------------------
*/

#include 
" StdAfx.h "
#include 
" Skybox.h "
#include 
" D3DGame.h "
#include 
" D3DCamera.h "

extern  IDirect3DDevice9  * g_pD3DDevice;
extern  CD3DCamera        * g_pD3DCamera;

//  顶点格式的初始化放到.cpp里,避免重定义错误
const  DWORD VertexSkybox::FVF  =  (D3DFVF_XYZ  |  D3DFVF_NORMAL  | D3DFVF_DIFFUSE  |  D3DFVF_TEX1);

CSkybox::CSkybox(
void ) : m_ppTextureArray(NULL),
                         m_pVertex(NULL),
                         m_pIndices(NULL)
{
}

CSkybox::
~ CSkybox( void )
{
}

bool  CSkybox::Create( char *  szFrontTex,  char *  szBackTex,  char *  szLeftTex,
                         
char *  szRightTex,  char *  szTopTex,   char *  szBottomTex)
{
    
//  初始化顶点及索引缓冲区
    InitVertices();
    
//  初始化天空盒纹理
    m_ppTextureArray  =   new  CTexture2D * [ 6 ];
    
for  ( int  ti = 0 ;ti < 6 ;ti ++ )
    {
        m_ppTextureArray[ti] 
=   new  CTexture2D;
    }
    
//  如果初始化过程中失败,则立即释放已有资源
     if ! m_ppTextureArray[ 0 ] -> LoadTexture(szFrontTex) ||
        
! m_ppTextureArray[ 1 ] -> LoadTexture(szBackTex)  ||
        
! m_ppTextureArray[ 2 ] -> LoadTexture(szLeftTex)  ||
        
! m_ppTextureArray[ 3 ] -> LoadTexture(szRightTex) ||
        
! m_ppTextureArray[ 4 ] -> LoadTexture(szTopTex)   ||
        
! m_ppTextureArray[ 5 ] -> LoadTexture(szBottomTex)
        )
    {
        Release();
        
return   false ;
    }
    
return   true ;
}

void  CSkybox::InitVertices()
{
    
//  初始化索引缓冲
    m_pIndices  =   new  UINT16[ 6 ];

    m_pIndices[
0 =   0 ;
    m_pIndices[
1 =   1 ;
    m_pIndices[
2 =   2 ;
    m_pIndices[
3 =   0 ;
    m_pIndices[
4 =   2 ;
    m_pIndices[
5 =   3 ;

    
//  初始化顶点缓冲
     const   float  t  =   1.0f ;
    
const   float  o  =   0.0f ;
    m_pVertex 
=   new  VertexSkybox[ 24 ];

    
//  前
    m_pVertex[ 0 ]   =  VertexSkybox( - 1 , - 1 , - 1 0 , 0 1 , D3DXCOLOR_WHITE, t, t);
    m_pVertex[
1 ]   =  VertexSkybox(  1 , - 1 , - 1 0 , 0 1 , D3DXCOLOR_WHITE, o, t);
    m_pVertex[
2 ]   =  VertexSkybox(  1 1 , - 1 0 , 0 1 , D3DXCOLOR_WHITE, o, o);
    m_pVertex[
3 ]   =  VertexSkybox( - 1 1 , - 1 0 , 0 1 , D3DXCOLOR_WHITE, t, o);

    
//  后
    m_pVertex[ 4 ]   =  VertexSkybox(  1 , - 1 1 0 , 0 , - 1 , D3DXCOLOR_WHITE, t, t);
    m_pVertex[
5 ]   =  VertexSkybox( - 1 , - 1 1 0 , 0 , - 1 , D3DXCOLOR_WHITE, o, t);
    m_pVertex[
6 ]   =  VertexSkybox( - 1 1 1 0 , 0 , - 1 , D3DXCOLOR_WHITE, o, o);
    m_pVertex[
7 ]   =  VertexSkybox(  1 1 1 0 , 0 , - 1 , D3DXCOLOR_WHITE, t, o);

    
//  左
    m_pVertex[ 8 ]   =  VertexSkybox(  1 , - 1 , - 1 - 1 , 0 , 0 , D3DXCOLOR_WHITE, t, t);
    m_pVertex[
9 ]   =  VertexSkybox(  1 , - 1 1 - 1 , 0 , 0 , D3DXCOLOR_WHITE, o, t);
    m_pVertex[
10 =  VertexSkybox(  1 1 1 - 1 , 0 , 0 , D3DXCOLOR_WHITE, o, o);
    m_pVertex[
11 =  VertexSkybox(  1 1 , - 1 - 1 , 0 , 0 , D3DXCOLOR_WHITE, t, o);

    
//  右
    m_pVertex[ 12 =  VertexSkybox( - 1 , - 1 1 ,   1 , 0 , 0 , D3DXCOLOR_WHITE, t, t);
    m_pVertex[
13 =  VertexSkybox( - 1 , - 1 , - 1 ,   1 , 0 , 0 , D3DXCOLOR_WHITE, o, t);
    m_pVertex[
14 =  VertexSkybox( - 1 1 , - 1 ,   1 , 0 , 0 , D3DXCOLOR_WHITE, o, o);
    m_pVertex[
15 =  VertexSkybox( - 1 1 1 ,   1 , 0 , 0 , D3DXCOLOR_WHITE, t, o);

    
//  上
    m_pVertex[ 16 =  VertexSkybox(  1 1 , - 1 0 , - 1 , 0 , D3DXCOLOR_WHITE, t, t);
    m_pVertex[
17 =  VertexSkybox(  1 1 1 0 , - 1 , 0 , D3DXCOLOR_WHITE, o, t);
    m_pVertex[
18 =  VertexSkybox( - 1 1 1 0 , - 1 , 0 , D3DXCOLOR_WHITE, o, o);
    m_pVertex[
19 =  VertexSkybox( - 1 1 , - 1 0 , - 1 , 0 , D3DXCOLOR_WHITE, t, o);

    
//  下
    m_pVertex[ 20 =  VertexSkybox(  1 , - 1 1 0 1 , 0 , D3DXCOLOR_WHITE, o, o);
    m_pVertex[
21 =  VertexSkybox(  1 , - 1 , - 1 0 1 , 0 , D3DXCOLOR_WHITE, t, o);
    m_pVertex[
22 =  VertexSkybox( - 1 , - 1 , - 1 0 1 , 0 , D3DXCOLOR_WHITE, t, t);
    m_pVertex[
23 =  VertexSkybox( - 1 , - 1 1 0 1 , 0 , D3DXCOLOR_WHITE, o, t);

}

void  CSkybox::Release()
{
    
//  释放天空盒纹理
     for  ( int  ti = 0 ;ti < 6 ;ti ++ )
    {
        ReleaseCOM(m_ppTextureArray[ti]);
    }
    delete[] m_ppTextureArray;
    
//  释放索引缓冲
    delete[] m_pIndices;
    
//  释放顶点缓冲
    delete[] m_pVertex;
}

void  CSkybox::Draw()
{
    
//  绘制之前,根据摄影机位置,更新世界矩阵
    D3DXVECTOR3 pos  =  g_pD3DCamera -> GetCameraPos();
    D3DXMatrixTranslation(
& m_WorldTransMatrix,pos.x,pos.y,pos.z);
    g_pD3DDevice
-> GetTransform(D3DTS_WORLD,  & m_OriWorldTransMatrix);
    g_pD3DDevice
-> SetTransform(D3DTS_WORLD,  & m_WorldTransMatrix);

    
//  禁用深度缓冲
    g_pD3DDevice -> SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
    
//  更改采样方式,平滑纹理间过度
    g_pD3DDevice -> SetSamplerState( 0 , D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
    g_pD3DDevice
-> SetSamplerState( 0 , D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);

    
//  分6次绘制天空盒6个面
     for  ( int  i = 0 ;i < 6 ;i ++ )
    {
        
//  应用纹理
        g_pD3DDevice -> SetTexture( 0 , m_ppTextureArray[i] -> GetTexture());
        
//  应用顶点格式
        g_pD3DDevice -> SetFVF(VertexSkybox::FVF);
        
//  绘制一个面的4个顶点
        g_pD3DDevice -> DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST,  0 4 2 & m_pIndices[ 0 ],
            D3DFMT_INDEX16, 
& m_pVertex[i  *   4 ],  sizeof (VertexSkybox));
    }

    
//  还原默认采样方式
    g_pD3DDevice -> SetSamplerState( 0 , D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
    g_pD3DDevice
-> SetSamplerState( 0 , D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
    
//  重用深度缓冲
    g_pD3DDevice -> SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
    
    
//  还原世界矩阵
    g_pD3DDevice -> SetTransform(D3DTS_WORLD,  & m_OriWorldTransMatrix);

}

 

原理很简单,我们首先绘制一个盒子,并在其6个表面分别贴上对应的天空纹理,而后绘制即可~

需要注意的地方有三点:

1> 虚拟出的天空效果要给人一种“无限远”的视觉体验,因此,我们要保证摄影机始终处在天空盒的中央位置,即天空盒随摄影机的移动而移动;

2> 我们不可能真的做一个无限大的天空盒出来,为了使有限大的天空盒不会遮挡其他物体,绘制时要关闭深度缓冲,且先于其他物体绘制;

3> 绘制时,我们需要临时更改纹理采样状态,以平滑面与面纹理间的过渡~

另外,关于天空盒的绘制,其实还有部分优化的余地~

Irrlicht引擎的做法是,当摄影机呈直角角度,即当摄影机完全正对其中一个面的时候,采用类似CSpriteBatch的方法,仅绘制这个面即可。这个做法是有必要的,不过出现的概率并不大。

我们延续Irrlicht的思路,还可以找到另一种优化的方法,即:无论任何时刻,正常状态下,我们只能看到天空盒6个面其中的3个~

不过,相应的条件判断都要由CPU来完成,各类方法的优劣还未详细测试。大家感兴趣的话不妨一试~

然后是主体代码:

 

Direct3D轮回:游戏场景之天空 D3DGame.cpp
/* -------------------------------------

代码清单:D3DGame.cpp
来自:
http://www.cnblogs.com/kenkao

-------------------------------------
*/

#include 
" StdAfx.h "
#include 
" D3DGame.h "
#include 
" D3DCamera.h "
#include 
" D3DEffect.h "
#include 
" CoordCross.h "
#include 
" SimpleXMesh.h "
#include 
" Texture2D.h "
#include 
" D3DSprite.h "
#include 
" Skybox.h "
#include 
" SpriteBatch.h "
#include 
< stdio.h >

// ---通用全局变量

HINSTANCE  g_hInst;
HWND       g_hWnd;
D3DXMATRIX g_matProjection;

// ---D3D全局变量

IDirect3D9       
* g_pD3D            =  NULL;
IDirect3DDevice9 
* g_pD3DDevice      =  NULL;
CMouseInput      
* g_pMouseInput     =  NULL;
CKeyboardInput   
* g_pKeyboardInput  =  NULL;
CD3DCamera       
* g_pD3DCamera      =  NULL;
CCoordCross      
* g_pCoordCross     =  NULL;
CSimpleXMesh     
* g_pSimpleXMesh    =  NULL;
CD3DEffect       
* g_pD3DEffect      =  NULL;
CD3DSprite       
* g_pD3DSprite      =  NULL;
CTexture2D       
* g_pTexture2D      =  NULL;
CSpriteBatch     
* g_SpriteBatch     =  NULL;
CTexture2D       
* g_pTexture2D2     =  NULL;
CD3DEffect       
* g_pD3DEffect2     =  NULL;
CSkybox          
* g_pSkybox         =  NULL;


// ---HLSL全局变量句柄

D3DXHANDLE   g_CurrentTechHandle 
=  NULL;
D3DXHANDLE   g_matWorldViewProj  
=  NULL;  
D3DXHANDLE   g_matWorld          
=  NULL;
D3DXHANDLE   g_vecEye            
=  NULL;
D3DXHANDLE   g_vecLightDir       
=  NULL;
D3DXHANDLE   g_vDiffuseColor     
=  NULL;
D3DXHANDLE   g_vSpecularColor    
=  NULL;
D3DXHANDLE   g_vAmbient          
=  NULL;

D3DXHANDLE   g_CurrentTechHandle2 
=  NULL;
D3DXHANDLE   g_Scale              
=  NULL;

//  HLSL特效参数设置
void  GetParameters();
void  SetParameters();


void  Initialize(HINSTANCE hInst, HWND hWnd)
{
    g_hInst 
=  hInst;
    g_hWnd  
=  hWnd;
    InitD3D(
& g_pD3D,  & g_pD3DDevice, g_matProjection, hWnd);
    g_pMouseInput 
=   new  CMouseInput;
    g_pMouseInput
-> Initialize(hInst,hWnd);
    g_pKeyboardInput 
=   new  CKeyboardInput;
    g_pKeyboardInput
-> Initialize(hInst,hWnd);
    g_pD3DCamera 
=   new  CD3DCamera;
}

void  LoadContent()
{
    g_pCoordCross 
=   new  CCoordCross;

    g_pD3DCamera
-> SetCameraPos(D3DXVECTOR3( 0.5f , 0.5f , - 5.0f ));

    g_pSimpleXMesh 
=   new  CSimpleXMesh;
    g_pSimpleXMesh
-> LoadXMesh( " teapot.X " );

    g_pD3DEffect 
=   new  CD3DEffect;
    g_pD3DEffect2 
=   new  CD3DEffect;
    g_pD3DEffect
-> LoadEffect( " Light.fx " );
    g_pD3DEffect2
-> LoadEffect( " Thunder.fx " );

    GetParameters();

    g_pD3DSprite 
=   new  CD3DSprite(g_pD3DDevice);
    g_SpriteBatch 
=   new  CSpriteBatch(g_pD3DDevice);

    g_pTexture2D 
=   new  CTexture2D;
    g_pTexture2D
-> LoadTexture( " img.jpg " );
    g_pTexture2D2 
=   new  CTexture2D;
    g_pTexture2D2
-> LoadTexture( " img2.jpg " );

    g_pSkybox 
=   new  CSkybox;
    g_pSkybox
-> Create( " Skybox_0.JPG " , " Skybox_1.JPG " , " Skybox_2.JPG "
        ,
" Skybox_3.JPG " , " Skybox_4.JPG " , " Skybox_5.JPG " );
}

void  Update()
{
    g_pMouseInput
-> GetState();
    g_pKeyboardInput
-> GetState();
    g_pD3DCamera
-> Update();
}

void  Draw()
{
    
//  参数设定
    SetParameters();
    g_pD3DDevice
-> SetTransform(D3DTS_VIEW, & g_pD3DCamera -> GetViewMatrix());

    POINT pos;
    pos.x
= 0 ;
    pos.y
= 0 ;

    POINT pos2;
    pos2.x 
=   440 ;
    pos2.y 
=   260 ;

    g_pD3DDevice
-> Clear( 0 , NULL, D3DCLEAR_TARGET  |  D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA( 100 , 149 , 237 , 255 ),  1.0f 0 );
    
if (SUCCEEDED(g_pD3DDevice -> BeginScene())) 
    {
        
//  天空处在无限远处,因此必须最先绘制天空盒
        g_pSkybox -> Draw();

        
// g_pCoordCross->Draw();

        
/// / 开始绘制并应用特效
         // g_SpriteBatch->Begin(g_pD3DEffect2);

        
/// / CSpriteBatch绘制
         // g_SpriteBatch->Draw(g_pTexture2D2,pos);
        
// g_SpriteBatch->Draw(g_pTexture2D,pos2);

        
/// / 结束绘制并终止特效
         // g_SpriteBatch->End();

        UINT numPasses;
        
//  开启特效
        g_pD3DEffect -> BeginEffect(numPasses);
        
for (UINT i = 0 ;i < numPasses;i ++ )
        {
            
//  开启路径
            g_pD3DEffect -> GetEffect() -> BeginPass(i);
            
for (DWORD j = 0 ;j < g_pSimpleXMesh -> GetMaterialNum();j ++ )
            {
                g_pSimpleXMesh
-> DrawXMeshSubset(j);
            }
            
//  路径结束
            g_pD3DEffect -> GetEffect() -> EndPass();
        }
        
//  特效结束
        g_pD3DEffect -> EndEffect();
    
        g_pD3DDevice
-> EndScene();
    }
    g_pD3DDevice
-> Present(NULL, NULL, NULL, NULL);
}

void  UnloadContent()
{
    ReleaseCOM(g_pSkybox);
    ReleaseCOM(g_pTexture2D2);
    ReleaseCOM(g_pTexture2D);
    ReleaseCOM(g_SpriteBatch);
    ReleaseCOM(g_pD3DSprite);
    ReleaseCOM(g_pD3DEffect2);
    ReleaseCOM(g_pD3DEffect);
    ReleaseCOM(g_pSimpleXMesh);
    ReleaseCOM(g_pCoordCross);
}

void  Dispose()
{
    ReleaseCOM(g_pD3DCamera);
    ReleaseCOM(g_pKeyboardInput);
    ReleaseCOM(g_pMouseInput);
    ReleaseCOM(g_pD3DDevice);
    ReleaseCOM(g_pD3D);
}

void  GetParameters()
{
    
//  获得HLSL中各个全局变量句柄
    g_CurrentTechHandle  =  g_pD3DEffect  ->  GetEffect()  ->  GetTechniqueByName( " SpecularLight " );
    g_matWorldViewProj  
=  g_pD3DEffect  ->  GetEffect()  ->  GetParameterByName( 0 " matWorldViewProj " );
    g_matWorld          
=  g_pD3DEffect  ->  GetEffect()  ->  GetParameterByName( 0 " matWorld " );
    g_vecEye            
=  g_pD3DEffect  ->  GetEffect()  ->  GetParameterByName( 0 " vecEye " );
    g_vecLightDir       
=  g_pD3DEffect  ->  GetEffect()  ->  GetParameterByName( 0 " vecLightDir " );
    g_vDiffuseColor     
=  g_pD3DEffect  ->  GetEffect()  ->  GetParameterByName( 0 " vDiffuseColor " );
    g_vSpecularColor    
=  g_pD3DEffect  ->  GetEffect()  ->  GetParameterByName( 0 " vSpecularColor " );
    g_vAmbient          
=  g_pD3DEffect  ->  GetEffect()  ->  GetParameterByName( 0 " vAmbient " );

    g_CurrentTechHandle2 
=  g_pD3DEffect2  ->  GetEffect()  ->  GetTechniqueByName( " Technique1 " );
    g_Scale              
=  g_pD3DEffect2  ->  GetEffect()  ->  GetParameterByName( 0 " Scale " );
}

void  SetParameters()
{
    
//  设定当前技术
    g_pD3DEffect  ->  GetEffect()  ->  SetTechnique(g_CurrentTechHandle);
    
//  设定HLSL中的各个参数
    D3DXMATRIX worldMatrix;
    D3DXMatrixTranslation(
& worldMatrix, 0.0f , 0.0f , 0.0f );
    g_pD3DEffect 
->  GetEffect()  ->  SetMatrix(g_matWorldViewProj, & (worldMatrix * g_pD3DCamera -> GetViewMatrix() * g_matProjection));
    g_pD3DEffect 
->  GetEffect()  ->  SetMatrix(g_matWorld, & worldMatrix);
    D3DXVECTOR3 cameraPos 
=  g_pD3DCamera -> GetCameraPos();
    D3DXVECTOR4 vecEye 
=  D3DXVECTOR4(cameraPos.x,cameraPos.y,cameraPos.z, 0.0f );
    g_pD3DEffect 
->  GetEffect()  ->  SetVector(g_vecEye, & vecEye);
    D3DXVECTOR4 vLightDirection 
=  D3DXVECTOR4( 0.0f 0.0f - 1.0f 1.0f );
    g_pD3DEffect 
->  GetEffect()  ->  SetVector(g_vecLightDir, & vLightDirection);
    D3DXVECTOR4 vColorDiffuse 
=  D3DXVECTOR4( 0.8f 0.0f 0.0f 1.0f );
    D3DXVECTOR4 vColorSpecular 
=  D3DXVECTOR4( 1.0f 1.0f 1.0f 1.0f );
    D3DXVECTOR4 vColorAmbient 
=  D3DXVECTOR4( 0.1f 0.1f 0.1f 1.0f );
    g_pD3DEffect 
->  GetEffect()  ->  SetVector(g_vDiffuseColor, & vColorDiffuse);
    g_pD3DEffect 
->  GetEffect()  ->  SetVector(g_vSpecularColor, & vColorSpecular);
    g_pD3DEffect 
->  GetEffect()  ->  SetVector(g_vAmbient, & vColorAmbient);

    g_pD3DEffect2 
->  GetEffect()  ->  SetTechnique(g_CurrentTechHandle2);
    g_pD3DEffect2 
->  GetEffect()  ->  SetFloat(g_Scale, 0.8f );
}

 

最后是实际的效果图:

Direct3D轮回:游戏场景之天空

 

大家可以尝试变动一下HLSL中光源的方向,使其朝向天空纹理中的太阳,会有更加真实的视觉体验哦^ ^

 

你可能感兴趣的:(游戏)