目前,天空绘制主要有三种方法:矩形天空、天空盒和球形天空。
  (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=);
};
半球形天空类的实现文件:

#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=);
};

#include "Skybox.h"  

CSkybox::CSkybox(IDirect3DDevice9 *pd3dDevice)
{
m_pd3dDevice = pd3dDevice;
m_pTexture = NULL;
m_pIndexBuf = NULL;
m_pVertexBuf = NULL;
m_nNumVertices = ;
m_nNumLatitudes = ;
m_nNumLongitudes = ;
m_nVertsPerLongi = ;
m_nVertsPerLati = ;
m_fSkyboxRadius = ;
} 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 = / nAlpha; // 维度线的条数
m_nNumLongitudes = / nBeta; // 经度线的条数
m_nVertsPerLongi = m_nNumLatitudes + ; // 每条经度线上的顶点数
m_nVertsPerLati = m_nNumLongitudes + ; // 每条维度线上的顶点数
m_nNumVertices = m_nVertsPerLati * m_nVertsPerLongi; // 计算天空的灵活顶点
if (FAILED(m_pd3dDevice->CreateVertexBuffer(m_nNumVertices * sizeof(SKYBOXVERTEX),
D3DUSAGE_WRITEONLY, SKYBOXVERTEX::FVF, D3DPOOL_MANAGED, &m_pVertexBuf, )))
return FALSE; SKYBOXVERTEX *pVertices = NULL;
m_pVertexBuf->Lock(, , (void**)&pVertices, ); int nIndex = ;
FLOAT fAlpha = 2.0f * D3DX_PI * nAlpha / 360.0f; // 经度角转换为弧度表示
FLOAT fBeta = 2.0f * D3DX_PI * nBeta / 360.0f; // 维度角转换为弧度表示
for (int row = ; row < m_nNumLongitudes+; row++)
{
for (int col = ; col < m_nNumLatitudes+; 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 * *sizeof(WORD),
D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &m_pIndexBuf, )))
return FALSE; WORD* pIndices = NULL;
m_pIndexBuf->Lock(, , (void**)&pIndices, ); nIndex = ;
for (int row = ; row < m_nNumLongitudes; row++)
{
for (int col = ; col < m_nNumLatitudes; col++)
{
pIndices[nIndex+] = row * m_nVertsPerLongi + col;
pIndices[nIndex+] = (row+) * m_nVertsPerLongi + col;
pIndices[nIndex+] = (row+) * m_nVertsPerLongi + col + ; pIndices[nIndex+] = row * m_nVertsPerLongi + col;
pIndices[nIndex+] = (row+) * m_nVertsPerLongi + col + ;
pIndices[nIndex+] = row * m_nVertsPerLongi + col + ;
nIndex += ;
}
}
m_pIndexBuf->Unlock(); return TRUE;
} BOOL CSkybox::DrawSkybox(D3DXMATRIX *pMatWorld, bool bDrawFrame)
{
//纹理模式渲染
m_pd3dDevice->SetStreamSource(, m_pVertexBuf, , sizeof(SKYBOXVERTEX));
m_pd3dDevice->SetFVF(SKYBOXVERTEX::FVF);
m_pd3dDevice->SetIndices(m_pIndexBuf);
m_pd3dDevice->SetTexture(, 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, , ,
m_nNumVertices, , m_nNumVertices * ); m_pd3dDevice->SetTexture(, );
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, , ,
m_nNumVertices, , m_nNumVertices * );
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 = ;
//int MaxLatitude = 180;
int MinLatitude = ; int MaxLongitude = ;
int MinLongitude = ; int LaInterval = ;
int LongInterval =; int Rows = ;//划分的格网行数,代表高度。//真正的顶点的行数和列数比网格的行数和列数均大1。
int Columns = ;//划分的格网列数,代表宽度。 int TotalVertexes=; public int GetTotalVertexes()
{
this.Columns = (this.MaxLongitude - this.MinLongitude) / this.LongInterval;//在经度上分割得到格网的列数
this.Rows = (this.MaxLatitude - this.MinLatitude) / this.LaInterval;//在纬度上分割得到格网的行数
return (this.Rows + ) * (this.Columns + );//真正的顶点的行数和列数比网格的行数和列数均大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 = ;
this.LongInterval = ;
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 ) * , pDevice, Usage.WriteOnly, Pool.Default);
this.indices = new int[(this.Columns ) * (this.Rows ) * ];//申请指定大小的索引缓冲区
} public void Create(Microsoft.DirectX.Direct3D.Device pDevice)
{
#region 定义顶点数组
CustomVertex.PositionTextured[] verts = new CustomVertex.PositionTextured[this.TotalVertexes];
int i = , j=, index=;
for (int a = ; a <= this.MaxLongitude; a += this.LongInterval)//经度
{
i = a / this.LongInterval;//用来遍历列
for (int b = ; 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 - && j <= this.Rows - )//从最后一行或者一列格网的前一个顶点即可索引下一个顶点,故次数限定范围。
{
this.indices[index * + ] = i * this.Rows + j;
this.indices[index * + ] = (i + ) * this.Rows + j;
this.indices[index * + ] = i * this.Rows + (j + ); this.indices[index * + ] = i * this.Rows + (j + );
this.indices[index * + ] = (i + ) * this.Rows + j;
this.indices[index * + ] = (i + ) * this.Rows + (j + );
}
}
}
#endregion
m_pVB.SetData(verts, , LockFlags.None);//设置顶点缓冲区的顶点数据为上面申请的顶点的数组中包含的数据
m_pTex = TextureLoader.FromFile(pDevice, "DomeSky.jpg");
m_pIB.SetData(this.indices, , LockFlags.None);
} public void Render(Microsoft.DirectX.Direct3D.Device pDevice)
{
pDevice.VertexFormat = CustomVertex.PositionTextured.Format;
pDevice.SetStreamSource(, m_pVB, );
pDevice.Indices = m_pIB;
pDevice.SetTexture(, 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(, TextureStageStates.AlphaOperation, TextureOperation.SelectArg1.GetHashCode());
pDevice.SetTextureStageState(, TextureStageStates.AlphaArgument1, TextureArgument.TextureColor.GetHashCode());
pDevice.SetRenderState(RenderStates.MultisampleAntiAlias, true);//启用多重采样,反锯齿。
//需要调用带索引的图元绘制函数
pDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, , , (this.Rows + ) * (this.Columns + ), , indices.Length / );
//绘制完毕应将及时关闭Alpha混合状态
//pDevice.SetRenderState(RenderStates.AlphaBlendEnable, false);
}
public void Destroy()
{
if (m_pVB != null)
{
m_pVB.Dispose();
} for (int i = ; i < ; i++)
{
if (m_pTex!= null)
{
m_pTex.Dispose();
}
}
}
}
}


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

  1. [安卓] 12、开源一个基于SurfaceView的飞行射击类小游戏

    前言  这款安卓小游戏是基于SurfaceView的飞行射击类游戏,采用Java来写,没有采用游戏引擎,注释详细,条理比较清晰,适合初学者了解游戏状态转化自动机和一些继承与封装的技巧. 效果展示    ...

  2. 一个基于PDO的数据库操作类(新) 一个PDO事务实例

    <?php /* * 作者:胡睿 * 日期:2011/03/19 * 电邮:hooray0905@foxmail.com * * 20110319 * 常用数据库操作,如:增删改查,获取单条记录 ...

  3. NET Core2.0 Memcached踩坑,基于EnyimMemcachedCore整理MemcachedHelper帮助类。

    DotNetCore2.0下使用memcached缓存. Memcached目前微软暂未支持,暂只支持Redis,由于项目历史原因,先用博客园开源项目EnyimMemcachedCore,后续用到的时 ...

  4. Asp.Net Core 2.0 项目实战(5)Memcached踩坑,基于EnyimMemcachedCore整理MemcachedHelper帮助类。

    Asp.Net Core 2.0 项目实战(1) NCMVC开源下载了 Asp.Net Core 2.0 项目实战(2)NCMVC一个基于Net Core2.0搭建的角色权限管理开发框架 Asp.Ne ...

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

    分享一款基于jquery适合做图片类网站的特效.这是一款鼠标经过图片滑动弹出标题效果代码.效果图如下: 在线预览   源码下载 实现的代码. html代码: <div class="c ...

  6. iOS 基于MVC设计模式的基类设计

    iOS 基于MVC设计模式的基类设计 https://www.jianshu.com/p/3b580ffdae00

  7. 并发编程概述 委托(delegate) 事件(event) .net core 2.0 event bus 一个简单的基于内存事件总线实现 .net core 基于NPOI 的excel导出类,支持自定义导出哪些字段 基于Ace Admin 的菜单栏实现 第五节:SignalR大杂烩(与MVC融合、全局的几个配置、跨域的应用、C/S程序充当Client和Server)

    并发编程概述   前言 说实话,在我软件开发的头两年几乎不考虑并发编程,请求与响应把业务逻辑尽快完成一个星期的任务能两天完成绝不拖三天(剩下时间各种浪),根本不会考虑性能问题(能接受范围内).但随着工 ...

  8. 带缓存的基于DateTimeFormatter的日期格式化工具类

    JAVA中的SimpleDateFormat是非线程安全的,所有在1.8的JDK版本里提供了线程安全的DateTimeFormatter类,由于是线程安全的,故我们可以将此类缓存起来多次利用提高效率. ...

  9. 慢牛股票-基于Sencha+Cordova的股票类APP

    13,14这两年,我的业余时间都花在了移动互联网技术和股票技术分析上,14年底,终于开发完成慢牛,上线小米应用商店.应用宝.百度应用商店.   慢牛是一款数据分析类的股票APP,提供数据订阅和数据分析 ...

随机推荐

  1. 火星A+B(hdu1230)进制转化

    火星A+B Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submissi ...

  2. 小希的迷宫(hdu1272)并查集(有点坑)

    纪念我死去的脑细胞们    .....   小希的迷宫 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java ...

  3. 设计模式(15)--Interpreter(解释器模式)--行为型

    作者QQ:1095737364    QQ群:123300273     欢迎加入! 1.模式定义: 解释器模式是类的行为模式.给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解 ...

  4. js-语言精粹-函数记忆

    函数可以将先前操作的结果记录在某个对象里,从而避免无谓的重复运算.这种优化方式被称为记忆(memoization).JavaScript的对象和数组要实现这种优化是非常方便的. 比如说,我们想要一个递 ...

  5. 从零开始学习html(十二)CSS布局模型——上

    一.css布局模型 清楚了CSS 盒模型的基本概念. 盒模型类型, 我们就可以深入探讨网页布局的基本模型了. 布局模型与盒模型一样都是 CSS 最基本. 最核心的概念. 但布局模型是建立在盒模型基础之 ...

  6. cmd--命令短集

    查看ip地址:ipconfig 查看ip地址:ipconfig/all 进入c盘program files目录下:cd %Program Files%,”x:“, 进入x盘根目录.cd “ ”进入某文 ...

  7. MyEclipse中搭建Struts2开发环境

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/53205941 冷血之心的博客) 在MyEclipse中如何搭建St ...

  8. 如何用WebSocket实现一个简单的聊天室以及单聊功能

    百度百科中这样定义WebSocket:WebSocket协议是基于TCP的一种新的网络协议.它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端.简单的说,We ...

  9. 在Android Studio中调用so中的方法

    本节用的so是上节用Android Studio创建的so.想在Android Studio中调用so中的方法,需要先引用so.Android Studio中引用so的方法有二种,下面开始介绍. 一 ...

  10. Reactjs-generator-cli 一款基于Ink构建用于快速搭建React应用的CLI scaffolding工具

    Reactjs-generator-cli 一款基于Ink构建用于快速搭建React应用的CLI scaffolding工具 A simple CLI for scaffolding React.js ...