Directx11教程(56) 建立一个skydome
原文:Directx11教程(56) 建立一个skydome
本章建立一个skydome(天空穹),主要学习如何使用cube mapping。
cube map就是把六张纹理当作一个cube的六个面,而cube的中心,则是坐标轴,而六个面则是垂直于坐标轴某个轴,如下图所示,在cube mapping中,我们不在使用二维纹理坐标,而是用(u,v,w)三维纹理坐标,用这个坐标产生一个查询向量,这个向量和cube 纹理的交点,即为该顶点对应的纹理texel。

可以通过微软的Directx Texture tool制作cube map纹理,前提是要准备好6张无缝过度的图片,这可能需要专业的工具。如下图,就是用6张图片生成的cube map纹理图,每章图片对应cube的一个面。

我们建立的skydome是一个球形体,并不是skybox,但使用cube map的方法是一样的。
首先是建立一个SkyDomeModelClass类,这个类中的顶点结构比较简单,只有一个参数:position,至于skydome贴图用的纹理坐标,我们则是在vs中生成。
struct VertexType
{
D3DXVECTOR3 position;
};
SkyDome模型是通过SkyDomeModelClass中的函数BuildGeoSphere得到,它通过细分一个20面体,得到一个近似的球形模型,具体的细分算法是把一个三角形细分成四个三角形(取每条边的中点),如下图所示:

生成SkyDome模型的代码如下:
//通过一个20面体细分,近似得到一个球体
void SkyDomeModelClass::BuildGeoSphere( int numSubdivisions, float radius, VertexList& vertices, IndexList& indices)
{
// 最小的细分数量.
numSubdivisions = min(numSubdivisions, 5);
const float X = 0.525731f;
const float Z = 0.850651f;
D3DXVECTOR3 pos[12] =
{
D3DXVECTOR3(-X, 0.0f, Z), D3DXVECTOR3(X, 0.0f, Z),
D3DXVECTOR3(-X, 0.0f, -Z), D3DXVECTOR3(X, 0.0f, -Z),
D3DXVECTOR3(0.0f, Z, X), D3DXVECTOR3(0.0f, Z, -X),
D3DXVECTOR3(0.0f, -Z, X), D3DXVECTOR3(0.0f, -Z, -X),
D3DXVECTOR3(Z, X, 0.0f), D3DXVECTOR3(-Z, X, 0.0f),
D3DXVECTOR3(Z, -X, 0.0f), D3DXVECTOR3(-Z, -X, 0.0f)
};
DWORD k[60] =
{
1,4,0, 4,9,0, 4,5,9, 8,5,4, 1,8,4,
1,10,8, 10,3,8, 8,3,5, 3,2,5, 3,7,2,
3,10,7, 10,6,7, 6,11,7, 6,0,11, 6,1,0,
10,1,6, 11,0,9, 2,11,9, 5,2,9, 11,2,7
};
vertices.resize(12);
indices.resize(60);
for(int i = 0; i < 12; ++i)
vertices[i] = pos[i];
for(int i = 0; i < 60; ++i)
indices[i] = k[i];
for(int i = 0; i < numSubdivisions; ++i)
Subdivide(vertices, indices);
//投影顶点到球面上,然后缩放顶点到球心的距离
for(int i = 0; i < vertices.size(); ++i)
{
D3DXVec3Normalize(&vertices[i], &vertices[i]);
vertices[i] *= radius;
}
}
//细分输入三角形,为四个面积相等的三角形
void SkyDomeModelClass::Subdivide(VertexList& vertices, IndexList& indices)
{
VertexList vin = vertices;
IndexList iin = indices;
vertices.resize(0);
indices.resize(0);
int numTris = (int)iin.size()/3;
for(int i = 0; i < numTris; ++i)
{
D3DXVECTOR3 v0 = vin[ iin[i*3+0] ];
D3DXVECTOR3 v1 = vin[ iin[i*3+1] ];
D3DXVECTOR3 v2 = vin[ iin[i*3+2] ];
D3DXVECTOR3 m0 = 0.5f*(v0 + v1);
D3DXVECTOR3 m1 = 0.5f*(v1 + v2);
D3DXVECTOR3 m2 = 0.5f*(v0 + v2);
vertices.push_back(v0); // 0
vertices.push_back(v1); // 1
vertices.push_back(v2); // 2
vertices.push_back(m0); // 3
vertices.push_back(m1); // 4
vertices.push_back(m2); // 5
//索引出四个三角形
indices.push_back(i*6+0);
…
indices.push_back(i*6+4);
}
}
另外我们新建一个SkyDomeShaderClass, 用来渲染SkyDome,它调用的vs,ps shader文件为cubetex.vs, cubetex.ps
在cubeTex.vs中,我们要注意两点:
1、设置skydome顶点的世界坐标系z=w,这样,skydome总会在远裁剪平面上。
2、用顶点local坐标做为cubemap的纹理坐标。
// set z = w so that z/w = 1 (i.e., skydome always on far plane).
//设置z=w
output.position = output.position.xyww;
//用local坐标做为cubemap查询向量.
output.tex = input.position;
cubeTex.ps中,我们通过cube sample函数得到cube纹理:
TextureCube gCubeMap;
…
float4 CubePixelShader(PixelInputType input) : SV_TARGET
{
return gCubeMap.Sample(SampleType, input.tex);
}
在GraphicsClass类中渲染SkyDome时,我们要关掉cull,再就是建立一个skydome专用depthstencil状态,在该状态中深度比较函数是: D3D11_COMPARISON_LESS_EQUAL
//skyome 顶点和索引数据放入缓冲区,准备渲染
m_SkydomeModel->Render(m_D3D->GetDeviceContext());
m_D3D->EnableCubeDepthStencil();
m_D3D->ChangeNoCullMode(true);
result = m_SkydomeShader->Render(m_D3D->GetDeviceContext(), m_SkydomeModel->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix,
m_TexManager->createCubeTex(m_D3D->GetDevice(),string("grassenvmap1024.dds")));
if(!result)
{
return false;
}
m_D3D->ChangeNoCullMode(false);
m_D3D->EnableDefaultDepthStencil();
另外,在textureManagerClass类中,我们增加了函数createCubeTex,专门用来读入cube texture
ID3D11ShaderResourceView* TexManagerClass::createCubeTex(ID3D11Device* device,string filename)
{
// 如果纹理资源已经存在,则返回,否则创建
for(int i = 0; i < m_TextureRVs.size(); ++i)
if(! m_TextureNames[i].compare(filename) )
return m_TextureRVs[i];
HRESULT result;
D3DX11_IMAGE_LOAD_INFO loadInfo;
loadInfo.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
ID3D11Texture2D* tex = 0;
result = D3DX11CreateTextureFromFile(device, stringToLPCWSTR(filename), &loadInfo, 0, (ID3D11Resource**)&tex, 0) ;
…
D3D11_TEXTURE2D_DESC texDesc;
tex->GetDesc(&texDesc);
D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc;
viewDesc.Format = texDesc.Format;
viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
viewDesc.TextureCube.MipLevels = texDesc.MipLevels;
viewDesc.TextureCube.MostDetailedMip = 0;
ID3D11ShaderResourceView* rv = 0;
result = device->CreateShaderResourceView(tex, &viewDesc, &rv);
if(FAILED(result))
{
HR(result);
return false;
}
m_TextureNames.push_back(filename);
m_TextureRVs.push_back(rv);
return rv;
}
程序执行后界面如下,你可以旋转摄像机,看看能不能超过skydome的包围: 
完整的代码请参考:
工程文件myTutorialD3D11_51
代码下载:
http://files.cnblogs.com/mikewolf2002/d3d1150-58.zip
http://files.cnblogs.com/mikewolf2002/pictures.zip
Directx11教程(56) 建立一个skydome的更多相关文章
- Directx11教程(10) 画一个简易坐标轴
原文:Directx11教程(10) 画一个简易坐标轴 本篇教程中,我们将在三维场景中,画一个简易的坐标轴,分别用红.绿.蓝三种颜色表示x,y,z轴的正向坐标轴. 为此,我们要先建立一个A ...
- Directx11教程(5) 画一个简单的三角形(1)
原文:Directx11教程(5) 画一个简单的三角形(1) 在本篇教程中,我们将通过D3D11画一个简单的三角形.在D3D11中,GPU的渲染主要通过shader来操作(当然还有一些操作 ...
- Directx11教程(55) 建立球形和锥形物体
原文:Directx11教程(55) 建立球形和锥形物体 本教程中,我们新建2个model class,SphereModelClass以及CylinderModelClass,分别用来表示球形和锥形 ...
- Directx11教程(19) 画一个简单的地形
原文:Directx11教程(19) 画一个简单的地形 通常我们在xz平面定义一个二维的网格,然后y的值根据一定的函数计算得到,比如正弦.余弦函数的组合等等,可以得到一个看似不错的地形或者 ...
- Directx11教程(11) 增加一个debug宏
原文:Directx11教程(11) 增加一个debug宏 现在我们在common.h中增加一个debug的宏,在每个d3d11函数后调用,如果d3d函数出错,它能够给出程序中错误的代码行 ...
- Directx11教程(9) 增加一个TimerClass类
原文:Directx11教程(9) 增加一个TimerClass类 在上篇教程代码的基础上,我们增加一个TimerClass类,这个类的功能很简单,就是可以计算相邻2帧的时间差.利用这个时间 ...
- Directx11教程(7) 画一个颜色立方体
原文:Directx11教程(7) 画一个颜色立方体 前面教程我们通过D3D11画了一个三角形,本章我们将画一个颜色立方体,它的立体感更强.主要的变动是ModelClass类,在Model ...
- Directx11教程(6) 画一个简单的三角形(2)
原文:Directx11教程(6) 画一个简单的三角形(2) 在上篇教程中,我们实现了在D3D11中画一个简单的三角形,但是,当我们改变窗口大小时候,三角形形状却随着窗口高宽比例改变而改变, ...
- 使用 MVC 5 的 EF6 Code First 入门 系列:建立一个EF数据模型
这是微软官方SignalR 2.0教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第一篇:建立一个E ...
随机推荐
- 苹果系统 IOS 12 的H5 BUG :键盘把页面顶上去了,底下留有一块空白区域
苹果以往的系统是没问题的,一般情况下,点击input唤起键盘后是会自动显示到输入框的地方,然后收起键盘页面就会恢复到底部. 但是如果苹果是已经更新到最新的IOS12的话就会发生一个BUG ,就是键盘唤 ...
- Joining Byte Blocks(哈希+带花树)
题目链接 Problem Statement As you are probably aware, the Internet protocols specify a canonical byte or ...
- 抓取摩拜单车API数据,并做可视化分析
抓取摩拜单车API数据,并做可视化分析 纵聊天下 百家号|04-19 15:16 关注 警告:此篇文章仅作为学习研究参考用途,请不要用于非法目的. 摩拜是最早进入成都的共享单车,每天我从地铁站下来的时 ...
- PAT甲级——A1013 Battle Over Cities
It is vitally important to have all the cities connected by highways in a war. If a city is occupied ...
- matlab保存数据
一:存txt文件,用dlmwrite()dlmwrite 将一个矩阵写到由分隔符分割的文件中. 在保存整数到文件时使用save存为ascii文件时,常常是文件里都是实型格式的数据(有小数点,和后面很多 ...
- 提交代码出现 Push to origin/master was rejected 错误解决方法
转至博客:http://www.xtyos.cn/archives/qt-1-index 为什么会出现这样的问题 一般发生在 GitHub 或 码云 刚刚创建仓库第一次pull的时候,两个仓库的差别非 ...
- IO流17 --- 对象流操作自定义对象 --- 技术搬运工(尚硅谷)
序列化 @Test public void test14() throws IOException { ObjectOutputStream oos = new ObjectOutputStream( ...
- [转]C#接收邮件
最近由于工作需要,接触到了邮件服务器.以前我用CF写过,感觉没有什么,我想用C#来实现会更简单,但是万万没想到C#没有提供邮件接收的方法,令我很不解.通过我在网上查找,发现了一个国外公司写好的接收邮件 ...
- 901. Online Stock Span [短于线性的时间统计单个元素的Span ]
Span 指这个元素之前连续的小于这个元素的值有多少个 原理: 维护递减栈 这个栈内的元素是递减的序列 新到一个元素x 依次出栈比x小的(也就是这个元素的Span) 这种问题的关键在于 新来的元素如果 ...
- hbase过滤器(1)
最近在公司做hbase就打算复习下它的过滤器以便不时之需,RowFilter根据行键(rowkey)筛选数据 public void filter() throws IOException { Fil ...