基于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();
                }
            }
        }
    }
}

				
时间: 2024-08-09 02:20:07

基于DirectX的半球形天空类的C++和C#实现的相关文章

基于ACE的定时器模板类

1 ACETimerClockGenerator.h 2 ClockGeneratorIF.h 3 在类中定义一个结构体,在结构体中定义一个函数. 4 在结构体中定义一个函数,这样做有什么好呢? 5 6 TimerHandler.h 7 用了模板的方法去构造定时器类.有助于底层调用上层.在构造的时候就初始化一个类中最大的定时器个数,及模板类(也就是parent). 8 TimerHandler(T *parent, int numTimers) : timers(numTimers, -1) 9

基于jQuery适合做图片类网站的特效

分享一款基于jquery适合做图片类网站的特效.这是一款鼠标经过图片滑动弹出标题效果代码.效果图如下: 在线预览   源码下载 实现的代码. html代码: <div class="common"> <ul> <li class="listbox mr20"> <div class="listimg"> <a href="/"><img src="im

Java 基于log4j的日志工具类

Java 基于log4j的日志工具类 对log4j日志类进行了简单封装,使用该封装类的优势在于以下两点: 1.不必在每个类中去创建对象,直接类名 + 方法即可 2.可以很方便的打印出堆栈信息 package com.tradeplatform.receiveorder.util; import java.io.PrintWriter; import java.io.StringWriter; import org.apache.log4j.Logger; /** * @Description 日

C#实现基于Base64的加密解密类实例

本文实例讲述了C#实现基于Base64的加密解密类.分享给大家供大家参考.具体如下: 这个C#类是一个基于Base64的加密和解密类,用户可以可以使用默认的秘钥进行加密.解密,也可以自己设定秘钥进行加密和解密,非常实用 using System; using System.Security.Cryptography; using System.Text; namespace DotNet.Utilities { /// <summary> /// Encrypt 的摘要说明. /// <

基于protostuff的序列化工具类开发

[toc] 基于protostuff的序列化工具类开发 前言 前面在介绍protostuff的基本使用时(可以参考文章protostuff基本使用),都是针对某个类写的序列化和反序列化方法,显然这样不具有通用性,例如在进行远程过程调用时,传输的对象并不唯一,这时就需要开发具有通用性的序列化工具类,即不管序列化的对象是什么类型,都可以使用该工具类进行序列化.下面就来开发这样的工具类. 基于这个需要,下面会开发两个序列化工具类,一个是不具有缓存功能的SerializationUtil,一个是具有缓存

MFC基于TCP协议的CSocket类套接字服务器端代码示范

MFC基于TCP协议的CSocket类套接字服务器端代码示范 https://blog.csdn.net/txwtech/article/details/93417667 原文地址:https://www.cnblogs.com/txwtech/p/11074805.html

基于EasyDarwin实现幼儿园监控类项目

移动互联网越来越普及,幼儿园监控类的项目也越来越多,如何能够以最低的成本.最快的速度搭建一套幼儿园监控类的平台成了许多开发者的需求,那么我们今天就来简单探讨一下如何基于EasyDarwin实现一套幼儿园监控.宝贝在线视频类的流媒体系统平台. 1.流媒体分发:幼儿园监控不同于我们传统意义上的家居监控,家居监控因为同时观看的终端数量有限,多采用p2p的形式,但幼儿园监控里面,同一个摄像机可能会被很多家长同时观看,所以多采用流媒体服务器转发的形式来做,那么就需要用到我们的EasyDarwin流媒体服务

一种基于JS原型链的类的构造与派生。【原创】

转载请注明出处 2016.7.5 by Totooria Hyperion http://demo.th-shr.com:9999/ // 初始化prototype和静态方法 function initClass(constructor,_proto) { // 配置prototype for(var key in _proto) { constructor.prototype[key] = _proto[key]; if(typeof _proto[key] == "function"

基于ADO.NET的SqlHelper类

1.使用Connection连接数据库的步骤: (1).添加命名空间 System.Data.SqlClient(注意:初学者经常会忘记) (2)定义连接字符串.连接SQL Server 数据库时: server=服务器名:database=数据库名:uid=用户名:pwd=密码:例如:要连接本机上的StudentManagement数据库,用户名为sa,密码为111 string connString="server=.;database=StudentManagement:uid=sa:pw