Directx11学习笔记【十三】 实现一个简单地形
本文由zhangbaochong原创,转载请注明出处http://www.cnblogs.com/zhangbaochong/p/5510294.html
上一个教程我们实现了渲染一个会旋转的立方体,这次我们来实现一个简单地形。
先来看看最终实现效果吧(蓝色是背景色,地形的不同高度分别渲染了不同颜色)
实现原理其实很简单,我们现在xz平面定义一个二维网格,然后y值可以根据一定的函数得到,比如正余弦函数组成等等,便可以得到一个看似不错的地形
或者水面效果。
1.创建二维网格
首先我们在GeometryGenerator中定义了一个只有XMFLOAT3 Position一个变量的结构Vertex用来描述顶点信息,MeshData中保存了整个grid所有
顶点的顶点信息和索引信息。CreateGrid函数负责创建grid。
#pragma once #include<vector>
#include "Dx11DemoBase.h" class GeometryGenerator
{
public:
struct Vertex
{
Vertex(){}
Vertex(const XMFLOAT3& p)
: Position(p){}
Vertex(float px, float py, float pz): Position(px, py, pz){}
XMFLOAT3 Position;
}; struct MeshData
{
std::vector<Vertex> vertices;
std::vector<UINT> indices;
}; void CreateGrid(float width, float depth, UINT m, UINT n, MeshData& meshData);
};
顶点索引的计算关键是推导出一个用于求构成第i行,第j列的顶点处右下方两个三角形的顶点索引的通用公式。
对顶点缓存中的任意一点A,如果该点位于地形中的第i行、第j列的话,那么该点在顶点缓存中所对应的位置应该就是i*m+j(m为每行的顶点数)。如果A点在索引缓存中的位置为k的话,那么A点为起始点构成的三角形ABC中,B、C顶点在顶点缓存中的位置就为(i+1)x m+j和i x m+(j+1)。且B点索引值为k+1,C点索引值为k+2.这样。这样,公式就可以推导为如下:
三角形ABC=【i*每行顶点数+j,i*每行顶点数+(j+1),(i+1)*行顶点数+j】
三角形CBD=【(i+1)*每行顶点数+j,i*每行顶点数+(j+1),(i+1)*行顶点数+(j+1)】
#include "GeometryGenerator.h" void GeometryGenerator::CreateGrid(float width, float depth, UINT m, UINT n, MeshData& meshData)
{
UINT vertexCount = m*n;
UINT faceCount = (m - )*(n - ) * ; // Create the vertices. float halfWidth = 0.5f*width;
float halfDepth = 0.5f*depth; float dx = width / (n - );
float dz = depth / (m - ); meshData.vertices.resize(vertexCount);
for (UINT i = ; i < m; ++i)
{
float z = halfDepth - i*dz;
for (UINT j = ; j < n; ++j)
{
float x = -halfWidth + j*dx;
meshData.vertices[i*n + j].Position = XMFLOAT3(x, 0.0f, z);
}
} // Create the indices. meshData.indices.resize(faceCount * ); // 3 indices per face UINT k = ;
for (UINT i = ; i < m - ; ++i)
{
for (UINT j = ; j < n - ; ++j)
{
meshData.indices[k] = i*n + j;
meshData.indices[k + ] = i*n + j + ;
meshData.indices[k + ] = (i + )*n + j; meshData.indices[k + ] = (i + )*n + j;
meshData.indices[k + ] = i*n + j + ;
meshData.indices[k + ] = (i + )*n + j + ; k += ; // next quad
}
}
}
2.鼠标拖动控制视角
为了方便观察最后创建出的地形,我们采用鼠标拖动旋转的方式,通过鼠标拖动改变相应的视图矩阵,因此我们Dx11DemoBase中添加了三个函数
//鼠标事件
virtual void OnMouseDown(WPARAM btnState, int x, int y){}
virtual void OnMouseUp(WPARAM btnState, int x, int y){}
virtual void OnMouseMove(WPARAM btnState, int x, int y){}
main函数消息循环中添加
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
demo->OnMouseDown(wParam, GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));
return ;
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
demo->OnMouseUp(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
return ;
case WM_MOUSEMOVE:
demo->OnMouseMove(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
具体函数的实现在下面给出
3.顶点缓冲和索引缓冲的创建
GeometryGenerator::MeshData grid;
GeometryGenerator geoGen;
geoGen.CreateGrid(160.0f, 160.0f, , , grid);
m_gridIndexCount = grid.indices.size(); std::vector<Vertex> vertices(grid.vertices.size(),Vertex(XMFLOAT3(,,),XMFLOAT4(,,,)));
for (UINT i = ; i < grid.vertices.size(); ++i)
{
XMFLOAT3 p = grid.vertices[i].Position;
p.y = GetHeight(p.x, p.z); vertices[i].pos = p; //渲染顶点根据高度给出不同颜色
if (p.y < -10.0f)
{
//sandy beach color
vertices[i].color = XMFLOAT4(1.0f, 0.96f, 0.62f, 1.0f);
}
else if (p.y < 5.0f)
{
//light yellow-green color
vertices[i].color = XMFLOAT4(0.48f, 0.77f, 0.46f, 1.0f);
}
else if (p.y < 12.0f)
{
//dark yellow-green color
vertices[i].color = XMFLOAT4(0.1f, 0.48f, 0.19f, 1.0f);
}
else if (p.y < .f)
{
//dark brown color
vertices[i].color = XMFLOAT4(0.45f, 0.39f, 0.34f, 1.0f);
}
else
{
//white snow color
vertices[i].color = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
}
} D3D11_BUFFER_DESC vertexDesc;
ZeroMemory(&vertexDesc, sizeof(vertexDesc));
vertexDesc.Usage = D3D11_USAGE_IMMUTABLE;
vertexDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexDesc.ByteWidth = sizeof(Vertex)* grid.vertices.size();
D3D11_SUBRESOURCE_DATA resourceData;
ZeroMemory(&resourceData, sizeof(resourceData));
resourceData.pSysMem = &vertices[];
result = m_pd3dDevice->CreateBuffer(&vertexDesc, &resourceData, &m_pVertexBuffer);
if (FAILED(result))
{
return false;
} D3D11_BUFFER_DESC indexDesc;
ZeroMemory(&indexDesc, sizeof(indexDesc));
indexDesc.Usage = D3D11_USAGE_IMMUTABLE;
indexDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexDesc.ByteWidth = sizeof(UINT)* m_gridIndexCount; D3D11_SUBRESOURCE_DATA indexData;
ZeroMemory(&indexData, sizeof(indexData));
indexData.pSysMem = &grid.indices[];
result = m_pd3dDevice->CreateBuffer(&indexDesc, &indexData, &m_pIndexBuffer);
if (FAILED(result))
{
return false;
}
加载shader代码与之前相同,故不再给出
GetHeight函数根据xz的值得到y的值
float HillsDemo::GetHeight(float x, float z) const
{
return 0.3f*(z*sinf(0.1f*x) + x*cosf(0.1f*z));
}
下面给出鼠标控制的具体做法:
定义4了个变量
float m_theta;
float m_phi;
float m_radius;
POINT m_lastMousePos;
其中m_lastMousePos意思明确很容易理解,那么其他三个分别代表什么意思呢?看下面的图就明白了
m_radius为半径,m_phi和m_theta为两个角度。
初始化半径和角度
HillsDemo::HillsDemo()//其他变量初始化省略了
: m_theta(1.5f*XM_PI), m_phi(0.1f*XM_PI), m_radius(200.0f){}
在Update函数中,给world,view,proj矩阵赋值
void HillsDemo::Update(float dt)
{
float x = m_radius*sinf(m_phi)*cosf(m_theta);
float z = m_radius*sinf(m_phi)*sinf(m_theta);
float y = m_radius*cosf(m_phi); XMVECTOR pos = XMVectorSet(x, y, z, 1.0f);
XMVECTOR target = XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f);
XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f); XMMATRIX V = XMMatrixLookAtLH(pos, target, up);
XMStoreFloat4x4(&m_view, V);
XMMATRIX T = XMMatrixPerspectiveFovLH(XM_PIDIV4, m_width / static_cast<float>(m_height),
1.0f, 1000.0f);
XMStoreFloat4x4(&m_proj, T); }
拖动鼠标时适当改变半径和角度的值,便可以间接改变view矩阵,从而改变视角
void HillsDemo::OnMouseDown(WPARAM btnState, int x, int y)
{
m_lastMousePos.x = x;
m_lastMousePos.y = y;
SetCapture(m_hWnd);
} void HillsDemo::OnMouseUp(WPARAM btnState, int x, int y)
{
ReleaseCapture();
} //限定数值范围
template<typename T>
static T Clamp(const T& x, const T& low, const T& high)
{
return x < low ? low : (x > high ? high : x);
} void HillsDemo::OnMouseMove(WPARAM btnState, int x, int y)
{
if ((btnState & MK_LBUTTON) != )
{
// Make each pixel correspond to a quarter of a degree.
float dx = XMConvertToRadians(0.25f*static_cast<float>(x - m_lastMousePos.x));
float dy = XMConvertToRadians(0.25f*static_cast<float>(y - m_lastMousePos.y)); // Update angles based on input to orbit camera around box.
m_theta += dx;
m_phi += dy; // Restrict the angle mPhi.
m_phi = Clamp(m_phi, 0.1f, XM_PI - 0.1f);
}
else if ((btnState & MK_RBUTTON) != )
{
// Make each pixel correspond to 0.2 unit in the scene.
float dx = 0.2f*static_cast<float>(x - m_lastMousePos.x);
float dy = 0.2f*static_cast<float>(y - m_lastMousePos.y); // Update the camera radius based on input.
m_radius += dx - dy; // Restrict the radius.
m_radius = Clamp(m_radius, 50.0f, 500.0f);
} m_lastMousePos.x = x;
m_lastMousePos.y = y;
}
4.渲染Render()函数
因为同之前教程中代码并没有什么改变,所以不再给出了。
这样运行程序就能得到之前给出图片所示的效果了,是不是很简单呢?
Directx11学习笔记【十三】 实现一个简单地形的更多相关文章
- Linux系统学习笔记之 1 一个简单的shell程序
不看笔记,长时间不用自己都忘了,还是得经常看看笔记啊. 一个简单的shell程序 shell结构 1.#!指定执行脚本的shell 2.#注释行 3.命令和控制结构 创建shell程序的步骤 第一步: ...
- 【opencv学习笔记五】一个简单程序:图像读取与显示
今天我们来学习一个最简单的程序,即从文件读取图像并且创建窗口显示该图像. 目录 [imread]图像读取 [namedWindow]创建window窗口 [imshow]图像显示 [imwrite]图 ...
- Django 学习笔记之六 建立一个简单的博客应用程序
最近在学习django时建立了一个简单的博客应用程序,现在把简单的步骤说一下.本人的用的版本是python 2.7.3和django 1.10.3,Windows10系统 1.首先通过命令建立项目和a ...
- [原创]java WEB学习笔记12:一个简单的serlet连接数据库实验
本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...
- UNP学习笔记2——从一个简单的ECHO程序分析TCP客户/服务器之间的通信
1 概述 编写一个简单的ECHO(回复)程序来分析TCP客户和服务器之间的通信流程,要求如下: 客户从标准输入读入一行文本,并发送给服务器 服务器从网络输入读取这个文本,并回复给客户 客户从网络输入读 ...
- Ruby学习笔记2 : 一个简单的Ruby网站,搭建ruby环境
Ruby on Rails website 的基础是 请求-返回 循环. 首先是浏览器请求服务器, 第二步,Second, in our Rails application, the route ta ...
- 【Python学习笔记三】一个简单的python爬虫
这里写爬虫用的requests插件 1.一般那3.x版本的python安装后都带有相应的安装文件,目录在python安装目录的Scripts中,如下: 2.将scripts的目录配置到环境变量pa ...
- DirectX11 学习笔记3 - 创建一个立方体 和 轴
该方案将在进一步的程序 面向对象. 独立的模型类.更像是一个框架. 其中以超过遇到了一个非常有趣的问题,.获得一晚.我读了好几遍,以找到其他的列子.必须放在某些功能Render里面实时更新,而不是仅仅 ...
- DuiLib学习笔记2——写一个简单的程序
我们要独立出来自己创建一个项目,在我们自己的项目上加皮肤这才是初衷.我的新建项目名为:duilibTest 在duilib根目录下面有个 Duilib入门文档.doc 我们就按这个教程开始入门 首先新 ...
- avalonjs学习笔记之实现一个简单的查询页
官网地址:http://avalonjs.coding.me/ 因为是为了学习js,所以对样式没什么要求,先放效果图: 步骤为:初始页面-------条件查询-------编辑员工1-------保存 ...
随机推荐
- Oschat IM 开源即时通讯项目介绍 - FengJ的个人页面 - 开源中国社区
Oschat IM 开源即时通讯项目介绍 - FengJ的个人页面 - 开源中国社区 Oschat IM 开源即时通讯项目介绍 255人收藏此文章, 我要收藏 发表于5天前(2013-08-28 ...
- HDOJ/HDU 2717 Catch That Cow 一维广度优先搜索 so easy..............
看题:http://acm.hdu.edu.cn/showproblem.php?pid=2717 思路:相当于每次有三个方向,加1,减1,乘2,要注意边界条件,减1不能小于0,乘2不能超过最大值. ...
- linux进程通信之信号
本节主要学习信号和与信号相关的处理函数,兴许还会更新. http://blog.csdn.net/xiaoliangsky/article/details/40264151 一 信号 信号是UNIX和 ...
- hdu4612(双连通缩点+树的直径)
传送门:Warm up 题意:询问如何加一条边,使得剩下的桥的数目最少,输出数目. 分析:tarjan缩点后,重新建图得到一棵树,树上所有边都为桥,那么找出树的直径两个端点连上,必定减少的桥数量最多, ...
- hbase总结(二)-hbase安装
本篇介绍两种HBase的安装方式:本地安装方式和伪分布式安装方式. 安装的前提条件是已经安装成功了hadoop,并且hadoop的版本号要和hbase的版本号相匹配. 我将要安装的hbase是hbas ...
- dblink如果很慢可以用这种方式优化
发现dblink如果很慢可以用这种方式加个/*+driving_site(xxx)*/,查询很快 DBLINK HINT /*+ driving_site(org) */ 优化策略和思路 使用DBLI ...
- hdu4586(概率、期望)
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=4586 题意:有一个色子,n面,每面有个分值a[i],其中有m面比较特殊,当该面出现时,可以再投一次.求 ...
- 西南民大oj(两园交求面积)
西南民大oj:http://www.swunacm.com/acmhome/welcome.do?method=index 我的几何不可能那么可爱 时间限制(普通/Java) : 1000 MS/ 3 ...
- hdu2102(bfs)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2102 分析:bfs求最短时间到达'P'点,不过本题有好几个trick,我都踩到了,自己还是太嫩了... ...
- SE 2014年4月29日
交换网络中有vlan1 到20个vlan,要求使用MSTP技术实现vlan的负载分担. SW2为实例1(vlan1-vlan10)的主根,SW3为备根 SW3为实例2(vlan11-vlan20)的主 ...