zoukankan      html  css  js  c++  java
  • 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;              // 原始世界变换矩阵
    };
    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,-10,01, D3DXCOLOR_WHITE, t, t);
        m_pVertex[
    1]  = VertexSkybox( 1,-1,-10,01, D3DXCOLOR_WHITE, o, t);
        m_pVertex[
    2]  = VertexSkybox( 11,-10,01, D3DXCOLOR_WHITE, o, o);
        m_pVertex[
    3]  = VertexSkybox(-11,-10,01, D3DXCOLOR_WHITE, t, o);

        
    // 后
        m_pVertex[4]  = VertexSkybox( 1,-110,0,-1, D3DXCOLOR_WHITE, t, t);
        m_pVertex[
    5]  = VertexSkybox(-1,-110,0,-1, D3DXCOLOR_WHITE, o, t);
        m_pVertex[
    6]  = VertexSkybox(-1110,0,-1, D3DXCOLOR_WHITE, o, o);
        m_pVertex[
    7]  = VertexSkybox( 1110,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,-11-1,0,0, D3DXCOLOR_WHITE, o, t);
        m_pVertex[
    10= VertexSkybox( 111-1,0,0, D3DXCOLOR_WHITE, o, o);
        m_pVertex[
    11= VertexSkybox( 11,-1-1,0,0, D3DXCOLOR_WHITE, t, o);

        
    // 右
        m_pVertex[12= VertexSkybox(-1,-11,  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(-11,-1,  1,0,0, D3DXCOLOR_WHITE, o, o);
        m_pVertex[
    15= VertexSkybox(-111,  1,0,0, D3DXCOLOR_WHITE, t, o);

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

        
    // 下
        m_pVertex[20= VertexSkybox( 1,-1101,0, D3DXCOLOR_WHITE, o, o);
        m_pVertex[
    21= VertexSkybox( 1,-1,-101,0, D3DXCOLOR_WHITE, t, o);
        m_pVertex[
    22= VertexSkybox(-1,-1,-101,0, D3DXCOLOR_WHITE, t, t);
        m_pVertex[
    23= VertexSkybox(-1,-1101,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, 042&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来完成,各类方法的优劣还未详细测试。大家感兴趣的话不妨一试~

    然后是主体代码:

    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.0f0);
        
    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.0f0.0f-1.0f1.0f);
        g_pD3DEffect 
    -> GetEffect() -> SetVector(g_vecLightDir,&vLightDirection);
        D3DXVECTOR4 vColorDiffuse 
    = D3DXVECTOR4(0.8f0.0f0.0f1.0f);
        D3DXVECTOR4 vColorSpecular 
    = D3DXVECTOR4(1.0f1.0f1.0f1.0f);
        D3DXVECTOR4 vColorAmbient 
    = D3DXVECTOR4(0.1f0.1f0.1f1.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);
    }

    最后是实际的效果图:

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

  • 相关阅读:
    LintCode "Binary Tree Serialization"
    LeetCode "Find the Duplicate Number"
    LintCode "Route Between Two Nodes in Graph"
    LintCode "Search a 2D Matrix II"
    LintCode "Submatrix Sum"
    LintCode "Sort Letters by Case"
    LeetCode "Peeking Iterator"
    LintCode "Sort Colors II"
    LeetCode "Move Zeroes"
    LintCode "Update Bits"
  • 原文地址:https://www.cnblogs.com/kenkao/p/2101726.html
Copyright © 2011-2022 走看看