zoukankan      html  css  js  c++  java
  • 基于DirectX的半球形天空类的C++和C#实现

      目前,天空绘制主要有三种方法:矩形天空、天空盒和球形天空。
      (1)矩形天空使用一个与地面垂直或呈一定夹角的矩形表示天空,用接近于天空的颜色或云彩纹理贴于矩形上。这种方法简单易行,但需要不断调整视角或观察点来改变场景可视域,还会对运行效率造成一定影响。
      (2)天空盒是构建一个包含场景的方盒来表示天空,然后在方盒四周和顶部贴上云彩纹理。但这种方法当视角对准两个面的边界时,能够明显看到交接痕迹。
      (3)球形天空通常使用半球形网格模型来表示天空,并在半球形网格上贴上一幅云彩纹理,可以使半球形网格模型绕Y轴旋转来模拟动态效果。
    半球形天空类的头文件:
    #pragma
    once #include <d3d9.h> #include <d3dx9.h> #ifndef SAFE_DELETE #define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } } #endif #ifndef SAFE_RELEASE #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } #endif //-------------------------------------------------------------------------------------- // Name: class CSkybox // Desc: 球形地形 //-------------------------------------------------------------------------------------- class CSkybox { private: LPDIRECT3DDEVICE9 m_pd3dDevice; LPDIRECT3DTEXTURE9 m_pTexture; LPDIRECT3DINDEXBUFFER9 m_pIndexBuf; LPDIRECT3DVERTEXBUFFER9 m_pVertexBuf; INT m_nNumLatitudes; INT m_nNumLongitudes; INT m_nVertsPerLati; INT m_nVertsPerLongi; INT m_nNumVertices; // 顶点数 FLOAT m_fSkyboxRadius; // 半径 struct SKYBOXVERTEX { FLOAT _x, _y, _z; FLOAT _u, _v; SKYBOXVERTEX(FLOAT x, FLOAT y, FLOAT z, FLOAT u, FLOAT v) : _x(x), _y(y), _z(z), _u(u), _v(v) {} static const DWORD FVF = D3DFVF_XYZ | D3DFVF_TEX1; }; public: CSkybox(IDirect3DDevice9 *pd3dDevice); virtual ~CSkybox(void); public: BOOL LoadSkybox(LPCSTR pTextureFile); BOOL InitSkybox(INT nAlpha, INT nBeta, FLOAT nRadius); BOOL DrawSkybox(D3DXMATRIX *pMatWorld, bool bDrawFrame=0); };
    半球形天空类的实现文件:
    
    
    #pragma once  
      
    #include <d3d9.h>  
    #include <d3dx9.h>  
      
    #ifndef SAFE_DELETE  
    #define SAFE_DELETE(p)  { if(p) { delete (p); (p)=NULL; } }  
    #endif   
    #ifndef SAFE_RELEASE  
    #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }  
    #endif  
      
    //--------------------------------------------------------------------------------------  
    // Name: class CSkybox  
    // Desc: 球形地形  
    //--------------------------------------------------------------------------------------  
    class CSkybox  
    {  
    private:  
        LPDIRECT3DDEVICE9       m_pd3dDevice;  
        LPDIRECT3DTEXTURE9      m_pTexture;  
        LPDIRECT3DINDEXBUFFER9  m_pIndexBuf;  
        LPDIRECT3DVERTEXBUFFER9 m_pVertexBuf;  
      
        INT     m_nNumLatitudes;  
        INT     m_nNumLongitudes;  
        INT     m_nVertsPerLati;  
        INT     m_nVertsPerLongi;  
        INT     m_nNumVertices;     // 顶点数  
        FLOAT   m_fSkyboxRadius;    // 半径  
      
        struct SKYBOXVERTEX  
        {  
            FLOAT _x, _y, _z;  
            FLOAT _u, _v;  
            SKYBOXVERTEX(FLOAT x, FLOAT y, FLOAT z, FLOAT u, FLOAT v)   
                : _x(x), _y(y), _z(z), _u(u), _v(v) {}  
            static const DWORD FVF = D3DFVF_XYZ | D3DFVF_TEX1;  
        };  
      
    public:  
        CSkybox(IDirect3DDevice9 *pd3dDevice);  
        virtual ~CSkybox(void);  
      
    public:  
        BOOL LoadSkybox(LPCSTR pTextureFile);  
        BOOL InitSkybox(INT nAlpha, INT nBeta, FLOAT nRadius);  
        BOOL DrawSkybox(D3DXMATRIX *pMatWorld, bool bDrawFrame=0);  
    };  
    
    
    #include "Skybox.h"  
      
    CSkybox::CSkybox(IDirect3DDevice9 *pd3dDevice)  
    {  
        m_pd3dDevice     = pd3dDevice;  
        m_pTexture       = NULL;  
        m_pIndexBuf      = NULL;  
        m_pVertexBuf     = NULL;  
        m_nNumVertices   = 0;  
        m_nNumLatitudes  = 0;  
        m_nNumLongitudes = 0;  
        m_nVertsPerLongi = 0;  
        m_nVertsPerLati  = 0;  
        m_fSkyboxRadius  = 0;  
    }  
      
    CSkybox::~CSkybox(void)  
    {  
        SAFE_RELEASE(m_pTexture);  
        SAFE_RELEASE(m_pIndexBuf);  
        SAFE_RELEASE(m_pVertexBuf);  
    }  
      
    BOOL CSkybox::LoadSkybox(LPCSTR pTextureFile)   
    {  
        // 加载天空纹理  
        if (FAILED(D3DXCreateTextureFromFile(m_pd3dDevice, pTextureFile, &m_pTexture)))  
            return FALSE;  
        return TRUE;  
    }  
      
    BOOL CSkybox::InitSkybox(INT nAlpha, INT nBeta, FLOAT fRadius)   
    {  
        m_fSkyboxRadius  = fRadius;                 // 半球体的半径  
        m_nNumLatitudes  = 360 / nAlpha;            // 维度线的条数  
        m_nNumLongitudes =  90 / nBeta;             // 经度线的条数  
        m_nVertsPerLongi = m_nNumLatitudes + 1;     // 每条经度线上的顶点数  
        m_nVertsPerLati  = m_nNumLongitudes + 1;    // 每条维度线上的顶点数  
        m_nNumVertices   = m_nVertsPerLati * m_nVertsPerLongi;  
      
        // 计算天空的灵活顶点  
        if (FAILED(m_pd3dDevice->CreateVertexBuffer(m_nNumVertices * sizeof(SKYBOXVERTEX),   
            D3DUSAGE_WRITEONLY, SKYBOXVERTEX::FVF, D3DPOOL_MANAGED, &m_pVertexBuf, 0)))  
            return FALSE;  
      
        SKYBOXVERTEX *pVertices = NULL;  
        m_pVertexBuf->Lock(0, 0, (void**)&pVertices, 0);  
      
        int nIndex = 0;  
        FLOAT fAlpha = 2.0f * D3DX_PI * nAlpha / 360.0f;    // 经度角转换为弧度表示  
        FLOAT fBeta  = 2.0f * D3DX_PI * nBeta  / 360.0f;    // 维度角转换为弧度表示  
        for (int row = 0; row < m_nNumLongitudes+1; row++)   
        {  
            for (int col = 0; col < m_nNumLatitudes+1; col++)   
            {  
                // 计算顶点的坐标  
                pVertices[nIndex]._x = fRadius * cosf(row * fBeta) * cosf(col * fAlpha);   
                pVertices[nIndex]._y = fRadius * sinf(row * fBeta);  
                pVertices[nIndex]._z = fRadius * cosf(row * fBeta) * sinf(col * fAlpha);  
                // 计算顶点的纹理坐标  
                pVertices[nIndex]._u = col * fAlpha / (2.0f * D3DX_PI);  
                pVertices[nIndex]._v = row * fBeta  / (D3DX_PI / 2.0f);  
      
                nIndex++;  
            }  
        }  
        m_pVertexBuf->Unlock();  
      
        // 计算天空的顶点索引  
        if (FAILED(m_pd3dDevice->CreateIndexBuffer(m_nNumVertices * 6 *sizeof(WORD),   
            D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &m_pIndexBuf, 0)))  
            return FALSE;  
      
        WORD* pIndices = NULL;  
        m_pIndexBuf->Lock(0, 0, (void**)&pIndices, 0);  
      
        nIndex = 0;   
        for (int row = 0; row < m_nNumLongitudes; row++)   
        {  
            for (int col = 0; col < m_nNumLatitudes; col++)   
            {  
                pIndices[nIndex+0] =   row   * m_nVertsPerLongi + col;  
                pIndices[nIndex+1] = (row+1) * m_nVertsPerLongi + col;  
                pIndices[nIndex+2] = (row+1) * m_nVertsPerLongi + col + 1;  
      
                pIndices[nIndex+3] =   row   * m_nVertsPerLongi + col;  
                pIndices[nIndex+4] = (row+1) * m_nVertsPerLongi + col + 1;  
                pIndices[nIndex+5] =   row   * m_nVertsPerLongi + col + 1;  
                nIndex += 6;  
            }  
        }  
        m_pIndexBuf->Unlock();  
      
        return TRUE;  
    }  
      
    BOOL CSkybox::DrawSkybox(D3DXMATRIX *pMatWorld, bool bDrawFrame)   
    {  
        //纹理模式渲染  
        m_pd3dDevice->SetStreamSource(0, m_pVertexBuf, 0, sizeof(SKYBOXVERTEX));  
        m_pd3dDevice->SetFVF(SKYBOXVERTEX::FVF);  
        m_pd3dDevice->SetIndices(m_pIndexBuf);  
        m_pd3dDevice->SetTexture(0, m_pTexture);  
      
        m_pd3dDevice->SetTransform(D3DTS_WORLD, pMatWorld);  
      
        m_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);  
        m_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);  
      
        m_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0,   
            m_nNumVertices, 0, m_nNumVertices * 2);  
      
        m_pd3dDevice->SetTexture(0, 0);  
        m_pd3dDevice->SetRenderState(D3DRS_LIGHTING, TRUE);  
        m_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);  
      
        //网格模式渲染  
        if (bDrawFrame)  
        {  
            m_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);  
            m_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0,   
                m_nNumVertices, 0, m_nNumVertices * 2);  
            m_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);  
        }  
      
        return TRUE;  
    }  
    半形天空体的演示C#实现代码如下:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    using Microsoft.DirectX;
    using Microsoft.DirectX.Direct3D;
    using Microsoft.DirectX.DirectInput;
    
    namespace TerrainVIS
    {
        class DomeSky
        {
            public IndexBuffer m_pIB;
            private int[] indices; //索引缓冲区的大小为。三角形总数:(Columns)X(Rows)X2,每个三角形需要3个顶点索引值。
    
            public VertexBuffer m_pVB;
            public Texture m_pTex;
            public float m_Radius=1f;  //定义球形天空的半径
    
            int MaxLatitude = 90;
            //int MaxLatitude = 180;
            int MinLatitude = 0;
    
            int MaxLongitude = 360;
            int MinLongitude = 0;
    
            int LaInterval = 6;
            int LongInterval =6;
    
            int Rows = 1;//划分的格网行数,代表高度。//真正的顶点的行数和列数比网格的行数和列数均大1。
            int Columns = 1;//划分的格网列数,代表宽度。
    
            int TotalVertexes=1;
    
            public int GetTotalVertexes()
            {
                this.Columns = (this.MaxLongitude - this.MinLongitude) / this.LongInterval;//在经度上分割得到格网的列数
                this.Rows = (this.MaxLatitude - this.MinLatitude) / this.LaInterval;//在纬度上分割得到格网的行数
                return (this.Rows + 1) * (this.Columns + 1);//真正的顶点的行数和列数比网格的行数和列数均大1。
            }
    
            /// <summary>
            /// 构造函数应该完成成员变量初始化和内存空间申请的任务
            /// </summary>
            /// <param name="pDevice"></param>
            /// <param name="L"></param>
            public DomeSky(Microsoft.DirectX.Direct3D.Device pDevice,float L)
            {
                this.m_pTex = null;
                this.m_Radius = L;
                this.LaInterval = 6;
                this.LongInterval = 6;
                this.TotalVertexes=GetTotalVertexes();//为了完成首尾对接需要申请
                this.m_pVB = new VertexBuffer(typeof(CustomVertex.PositionTextured), this.TotalVertexes, pDevice, Usage.Dynamic | Usage.WriteOnly, CustomVertex.PositionTextured.Format, Pool.Default);
                this.m_pIB = new IndexBuffer(typeof(int), (this.Columns ) * (this.Rows ) * 6, pDevice, Usage.WriteOnly, Pool.Default);
                this.indices = new int[(this.Columns ) * (this.Rows ) * 6];//申请指定大小的索引缓冲区
            }
    
            public void Create(Microsoft.DirectX.Direct3D.Device pDevice)
            {            
                #region 定义顶点数组
                CustomVertex.PositionTextured[] verts = new CustomVertex.PositionTextured[this.TotalVertexes];
                int i = 0, j=0, index=0;
                for (int a = 0; a <= this.MaxLongitude; a += this.LongInterval)//经度
                {
                    i = a / this.LongInterval;//用来遍历列
                    for (int b = 0; b <= this.MaxLatitude; b += this.LaInterval)//纬度
                    {   
                        //把球面坐标转化成空间直角坐标,设置球半径为2,竖直角从天顶到天底为0~90度,水平角0~360度
                        j = b / this.LaInterval;//用来遍历行
                        index = i * this.Rows + j;
                        verts[index] = new CustomVertex.PositionTextured();
                        verts[index].X = Convert.ToSingle(this.m_Radius * Math.Sin((float)b / 180.0 * Math.PI) * Math.Cos((float)a / 180.0 * Math.PI));
                        verts[index].Y = Convert.ToSingle(this.m_Radius * Math.Sin((float)b / 180.0 * Math.PI) * Math.Sin((float)a / 180.0 * Math.PI));
                        verts[index].Z = Convert.ToSingle(this.m_Radius * Math.Cos((float)b / 180.0 * Math.PI));
                        //计算纹理坐标的方法一
                        //verts[index].Tu = (float)(a / this.LongInterval) / this.Columns;
                        //verts[index].Tv = (float)(b / this.LaInterval) / this.Rows;
                        //计算纹理坐标的方法一
                        verts[index].Tu = (float)a / this.MaxLongitude;//单位用度或者弧度做比值结果都一样
                        verts[index].Tv = (float)b / this.MaxLatitude;
    
                        if (i <= this.Columns - 1 && j <= this.Rows - 1)//从最后一行或者一列格网的前一个顶点即可索引下一个顶点,故次数限定范围。
                        {
                            this.indices[index * 6 + 0] = i * this.Rows + j;
                            this.indices[index * 6 + 1] = (i + 1) * this.Rows + j;
                            this.indices[index * 6 + 2] = i * this.Rows + (j + 1);
    
                            this.indices[index * 6 + 3] = i * this.Rows + (j + 1);
                            this.indices[index * 6 + 4] = (i + 1) * this.Rows + j;
                            this.indices[index * 6 + 5] = (i + 1) * this.Rows + (j + 1);
                        }
                    }
                }        
                #endregion
                m_pVB.SetData(verts, 0, LockFlags.None);//设置顶点缓冲区的顶点数据为上面申请的顶点的数组中包含的数据
                m_pTex = TextureLoader.FromFile(pDevice, "DomeSky.jpg");
                m_pIB.SetData(this.indices, 0, LockFlags.None);
            }
    
            public void Render(Microsoft.DirectX.Direct3D.Device pDevice)
            {
                pDevice.VertexFormat = CustomVertex.PositionTextured.Format;
                pDevice.SetStreamSource(0, m_pVB, 0);
                pDevice.Indices = m_pIB;
                pDevice.SetTexture(0, m_pTex);
    
                //绘制半透明状态
                pDevice.SetRenderState(RenderStates.AlphaBlendEnable, true);
                pDevice.SetRenderState(RenderStates.BlendOperation, true);
                pDevice.SetRenderState(RenderStates.SourceBlend, Blend.SourceAlpha.GetHashCode());
                pDevice.SetRenderState(RenderStates.DestinationBlend, Blend.InvSourceAlpha.GetHashCode());
                //下面的代码用于多层纹理映射。在此不起作用。
                //pDevice.SetTextureStageState(0, TextureStageStates.AlphaOperation, TextureOperation.Modulate.GetHashCode());
                //pDevice.SetTextureStageState(0, TextureStageStates.ColorArgument1, TextureArgument.Diffuse.GetHashCode());
                pDevice.SetTextureStageState(0, TextureStageStates.AlphaOperation, TextureOperation.SelectArg1.GetHashCode());
                pDevice.SetTextureStageState(0, TextureStageStates.AlphaArgument1, TextureArgument.TextureColor.GetHashCode());
                pDevice.SetRenderState(RenderStates.MultisampleAntiAlias, true);//启用多重采样,反锯齿。
                //需要调用带索引的图元绘制函数
                pDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, (this.Rows + 1) * (this.Columns + 1), 0, indices.Length / 3);
                //绘制完毕应将及时关闭Alpha混合状态
                //pDevice.SetRenderState(RenderStates.AlphaBlendEnable, false);
            }
            public void Destroy()
            {
                if (m_pVB != null)
                {
                    m_pVB.Dispose();
                }
    
                for (int i = 0; i < 5; i++)
                {
                    if (m_pTex!= null)
                    {
                        m_pTex.Dispose();
                    }
                }
            }
        }
    }
    
    
    


  • 相关阅读:
    Percona 工具包 pt-online-schema-change 简介
    MySQL 中NULL和空值的区别
    MySQL二进制日志文件过期天数设置说明
    MySQL大小写敏感说明
    SpringBoot 配置Druid:不显示SQL监控 —(*) property for user to setup
    IDEA 启用/禁用 Run Dashboard
    java.lang.IllegalAccessException: Class XX can not access a member of class XXX with modifiers "private static"
    Swagger2常用注解说明
    更改IDEA默认使用JDK1.5编译项目
    Spring Boot : Swagger 2
  • 原文地址:https://www.cnblogs.com/rainbow70626/p/4541395.html
Copyright © 2011-2022 走看看