使用Managed DirectX创建三维地形

[原创]Managed DirectX 基础使用教程(零) hangtao_me@yahoo.com.cn        由于下学期的毕业设计,最近开始研究起了.NET平台下的DirectX.本来是不打算用DirectX的,因为我不会DirectX - -||。但是DEMO出来后,发现用C#的GDI+绘图的效率是如此低下。最后不得不得硬着头皮上DirectX。而且是资料相对较少的Managed DirectX(托管版DX)。很多都是直接 阅读详情

使用Managed DirectX创建三维地形

 

作者:clayman
clayman_joe@yahoo.com.cn
转载请注明作者,商业使用请联系我^_^

使用Height Map作为输入

 

         首先,什么是高度图(Height Map)呢?所谓高度图实际上就是一个2维数组。创建地形为什么需要高度图呢?我们这样考虑,地形实际上就是一系列高度不同的网格而已,这样数组中每个元素的索引值刚好可以用来定位不用的网格(x,y),而所储存的值就是网格的高度(z)。正是由于这个简单的映射关系,最常见的地形生成方法都使用高度图作为输入数据。同时,为了减小数组的尺寸,通常使用Byte类型来保存高度值,因此,地形中最低点将用0表示,而最高点使用255表示(当然,这样做可能会出现一些问题,比如,地形中大部分区域的高度差别都不大,但是有少数地方高度差特别大时,不过大多数情况下这个系统都能运行的很好)。使用2D Byte数组的另一个好处就是我们高度图刚好可以用一张灰度位图(grayscale bitmap) 来表示。对于位图中的每个像素来说,同样使用0~~255之间的值来表示一个灰度。这样,我们又能把不同的灰度映射为高度,并且用像素索引表示不同网格。

         那么如何来创建高度图呢?有两种方法:直接使用程序创建2D数组或者使用其他绘图软件创建灰度位图。先来看看两种方法的优缺点。直接创建数组,通过特定算法填充每个元素的值(只为每个元素赋随即值是不可行,这样会导致你的地面看起来极度不真实,不连续的高度值可能创建出很扭曲的地形。),你不需要任何额外的工具就能创建地形。但是,通过这种方法创建的地形基本是随机的,虽然可以通过调节算法的参数控制大概的地形形状,却不能精确控制每个点应该凹下还是凸起。而使用灰度图,你不必掌握复杂的地形生成算法,可以把3维软件建好的地形模型渲染为灰度图,也可以使用通过卫星采样的图片作为灰度图。我们的示例程序将使用后一种方法,不过首先,我们还是来看看完全使用程序生成地形的算法。

使用Midpoint Displacement方法生成高度图

        这里我们介绍一种比较常用,也比较简单的地形生成算法,称为Midpoint Displacement中点偏移算法。使用这个方法,我们先创建一张平坦的高度图,然后再来升高或降低不同的网格创建随机地形。为了避免生成的值是完全没有规则的,我们先把整个平面分为4个正方形区域,接下来重复对这四个正方形进行同样的分割,同时,调整每个正方形顶点的高度。随着细份层次的增加,相应减少顶点高度调整的幅度。

 

 

       使用[0,255]之间的浮点值来进行调整,以保证最后能用8位的灰度值来表示所有高度。每一步,都在一个确定范围内产生一个随机值来作为顶点偏移值。对于第一步来说,随机值将在[-128,128]之间(为了方便说明,我们把这个随机值范围记为[-delta,delta]产生,并且赋给上图左边的A,B,C,D四个顶点。接下来,用虚线把它分为4个小区域,这将创建5个新的顶点。计算每个新顶点所在边两个顶点高度的平均值作为这个点的基准值(比如 把点A和B的高度平均值作为点1的基准值),其中,点5的基准值是由四个顶点A,B,C,D的平均值来决定的。再计算[-delta,delta]之间的一个随机值,对基准值进行偏移,作为这个点的最终值。5个点的值都计算完毕之后,我就调到下一阶段,使用同样的方法,计算个顶点值,如上图右边所示。

         为了引导地形的产生,再把delta和一个缩放因子相乘。我们把这个因子称为roughness,它是一个1~0之间的值,这样,每个阶段都会减小delta的值。

delta = delta * roughness

         roughness的值越大,地形起伏就越明显,而越小,相应的地形也就越平坦。

使用Perlin Noise生成高度图

         任何没有讨论噪声函数的程序地形算法都是不完整的。最重要的噪声函数就是Perlin Noise。他几乎是现代图形软件包生成各种火焰,云彩,奇形怪状的岩石,以及树木和大理石表面等许多应用的基础。这里不对Perlin Noise的理论做详细介绍,我们主要看看如何使用它为我们的地形添加噪声。

         Perlin噪声可以适用于任何维度的空间,但这里我们只讨论二维的情况。本质上,2D Perlin噪音就是对每个网格顶点法线的一种插值,来仔细看看这个技术吧。

 

    首先,使用网格把整个图片划分为几个不同部分。如上图所示,我们使用了一个4X4的网格来划分整个图片。这里,网格的多少控制着噪声的复杂性。网格越多,噪声越密集(tiger),类似于电视没有信号时显示出的雪花点;而网格越少,噪声的波形就越明显,类似于云朵的效果。

       对于每个网格顶点我们都分配一个随机法线(normal vector)。这些法线实际上就是一些指向不同方向的单位矢量而已。这里,常见的方法是创建一张有256个指向不同方向(形成一个圆周)的向量查找表。然后为每个网格随机分配一个向量,如上图所示。

       对于图片中的每个像素来说,我们先找到包含它的网格单元。然后,再创建4个从网格顶点指向所要计算的像素的方向矢量,如下图所示。现在,每个网格顶点有2个向量:一个随机的单位向量以及一个指向像素的方向向量。计算每对向量的点积,把它作为每个网格顶点的梯度高度值(scalar height value)。接下来,混合这4个值决定所计算像素的高度。这里,不同的混合方法可以产生不同效果,最常见的就方法就是通过目标像素与每个顶点位置的权重来计算。

 

    我们将执行3次混合操作。首先需要计算混合权重。使用如下公式:

W = 6t^5 – 15t^4 + 10t^3 (^符号表示幂运算)

         其中w表示权重,t根据需要替换为x或y值。这个方法与最早Perlin提出的公式(w = 3t^2 – 2t^3)有些区别。它虽然计算起来比较慢,但效果要好得多。

首先,计算x方向上的权重,使用公式:

V = Ca(w) + Cb(1-w)

          混合网格上边的两个顶点。其中Ca和Cb分别为上面两个顶点的梯度高度值,w是上一个公式计算出的权重值。然后,使用同样的方法混合下面两个顶点。最后,使用前两部混合的结果,以及y方向上的权重再进行一次混合。最后为这个像素计算出的高度值位于[0,1]之间,我们再把它缩放为相应的灰度值。
         举个例子,假如网格上边两个顶点的坐标分别为Ca[2,0]和Cb[8,0],梯度高度值分别为h0和h1,所求像素位置为[4,2],那么两个顶点指向这个像素的矢量就是

Vector2 d0(4 -2,2-0)

Vector2 d1(4-8,2-0);

         X轴方向的权重就为

S = 6*d0.x^5 – 15d0.x^4 + 10d0.x^3

         相应的插值就为

avgX0 = h0*Sx + h1(1 –Sx)

         如果下面两个顶点的插值为avgX1,则最后的插值就是:

Result = avgX0 * Sy + avg2(1- Sy)

         通常情况下,为了获得真实的地形,会选取不同网格粒度,分别对图像进行多次Perlin噪音处理,最后把这些处理过的图加到一起,获得最终结果。

生成地形

         现在来看看如何把高度图转变为为多边形网格。一开始就说过,把高度图中像素的x,y值转换为顶点的x,y值,把像素的颜色值转换为顶点高度。我们可以把这些值缩放为所需要的尺寸。

         每2X2个像素就对应着2X2个顶点,同时可以组成2个三角形。可以把把顶点数据储存为一个简单的(x,y,z)列表,三角形数据储存为三个索引值一组的顶点列表。这两个列表之后就转变为顶点缓冲和索引缓冲。

public class Terrain

{

     private Device device;

     private VertexBuffer vb;

     private IndexBuffer ib;

     private int numVertices, numIndices, numTriangles;

     //保存从高度图中提取的数据

     float[,] heights;

     //地形大小

     private float terrainSize;

     public unsafe Terrain(Device d,float Min, float Max,float terrainSize)

     {

         device = d;

         //加载高度图

         Bitmap heightMap = new Bitmap(@"../../heightmap.bmp");

         //根据位图大小创建数组

         heights = new float[heightMap.Width,heightMap.Height];

         //锁定数据

         BitmapData data = heightMap.LockBits(new Rectangle(0,0,heightMap.Width,heightMap.Height),ImageLockMode.ReadOnly,PixelFormat.Format24bppRgb);

         //获得位图中第一个像素的地址

         byte* p = (byte*) data.Scan0;

         //遍历位图,获得最高和最低点的灰度值

         byte lowest = 255;

         byte hightest = 0;

         for(int i=0;i<heightMap.Width;i++)

         {

              for(int j=0;j<heightMap.Height;j++)

              {

                   if ( *p < lowest)

                       lowest = *p;

                   if( *p > hightest)

                       hightest = *p;

                   //由于每个像素是24位,而指针是8位,所以+3指向下一个像素

                   p += 3;

              }

         }

         //填充数组,max表示地形最高点的位置,min标志最低点。

         p = (byte*) data.Scan0;

         for(int i=0;i< heightMap.Width;i++)

         {

              for(int j=0; j< heightMap.Height; j++)

              {

                   heights[i,j] = (float)(*p - lowest) / (float)(hightest - lowest) * (Max - Min) + Min;

                   p += 3;

              }

         }

        heightMap.UnlockBits(data);

         //计算顶点,索引,三角形数量

         numVertices = heightMap.Width * heightMap.Height;

         numIndices = 6 * (heightMap.Width - 1) * (heightMap.Height - 1);

         numTriangles = 2 * (heightMap.Width - 1) * (heightMap.Height - 1);

         //创建顶点数组

         Vector3[] verts = new Vector3[numVertices];

         int[] index = new int[numIndices];

         int x = 0;

         int n = 0;

         float dx = terrainSize / (float) heightMap.Height;

         float dy = terrainSize / (float) heightMap.Width;

         //填充顶点数组

         for ( int i = 0; i < heightMap.Height; i ++)

         {

              for ( int j = 0; j < heightMap.Width; j ++)

              {                     

                   verts[i*heightMap.Width+j] = new Vector3((float)j*dx -terrainSize/2f,heights[j,i],(float)i*dy -terrainSize/2f);                          

              }

         }

         //填充索引数组

         for ( int i = 0; i < heightMap.Width-1; i ++)

        {

              for ( int j = 0; j < heightMap.Height-1; j ++)

              {

                  x = i * heightMap.Width + j;

                   index[n++] = x;

                   index[n++] = x+1;

                   index[n++] = x+heightMap.Width+1;

                   index[n++] = x;

                   index[n++] = x+heightMap.Width;

                   index[n++] = x+heightMap.Width+1;

              }

         }

         //设置顶点以及索引缓冲

         vb = new VertexBuffer(typeof(Vector3),numVertices,device,Usage.None,VertexFormats.Position,Pool.Default);

         vb.SetData(verts,0,0);

         ib = new IndexBuffer(typeof(int),numIndices,device,Usage.None,Pool.Default);

         ib.SetData(index,0,0);

     }

     public void DrawTerrain()

     {

         device.VertexFormat = VertexFormats.Position;

         device.SetStreamSource(0,vb,0);

         device.Indices = ib;

         device.Transform.World = Matrix.Translation(0,0,0);

         device.DrawIndexedPrimitives(PrimitiveType.TriangleList,0,0,numVertices,0,numTriangles);

     }

}

 

    

     好了,看看我们的工作成果吧,还不错把。源码中我们使用了一张位图作为高度图。

     当然,这只是初级的地形技术而已,我们没有为地形贴纹理,顶点没有法线信息,以至于不能使用灯光照亮他,另外,也没有进行任何LOD处理。下一次,我们将仔细讨论这些问题。

 

[原创]Managed DirectX 基础使用教程(一) hangtao_me@yahoo.com.cn 本次内容:如何用Managed  DirectX 显示图片(2D),以及一些变形.上回我们已经学习了MDX的一些基本知识,现在我们就要来使用它了.准备:创建一个Windows 窗体应用程序然后在引用中右键添加引用然后选择需要的.NET项目然后在代码中写上相应的using .......现在准备工作已经做好了,那么在 阅读详情

相关推荐

使用Managed DirectX编写游戏(四) 使用Managed DirectX创建三维地形

作者:clayman使用Height Map作为输入  首先,什么是高度图(Height Map)呢?所谓高度图实际上就是一个2维数组。创建地形为什么需要高度图呢?我们这样考虑,地形实际上就是一系列高度不同的网格而已,这样数组中每个元素的索引值刚好可以用来定位不用的网格(x,y),而所储存的值就是网格的高度(z)。正是由于这个简单的映射关系,最常见的地形生成方法都使用高度图作为输入数据。同时,为了

csdn_gamedev的专栏 8824

使用C# + Managed DIrectX 制作 2D 游戏使用C# + Managed DIrectX 制作 2D 游戏

主旨:虽然目前Microsoft已经发布了XNA Game Studio,让游戏编程员能够更轻松的在Windows(XB360不在本文考虑范围内)制作游戏,不过这个XNAGS有个缺点,它要求系统必须有至少能够支援Shader Model 1.1的显示卡。这表示TNT,GeForce 1, 2, 和 GeForce 3 & 4 MX 系列的显示卡,以及较早期的 integrated display

shaily读书学习 6785

用Managed DirectX 9和C#实现3D地形可视化

介绍:GIS(Geographical Information System) 是一种通过计算机系统实现地图显示的系统。可以让用户进入,显示并分析地理数据。由于大多数人不了解GIS,所以经常在IT市场上有几家大企业垄断该行业比如Intergraph, Bentley, MapInfo, Autodesk 和 ESRI,如今全球IT垄断企业微软,Google 和 Oracle 正在竞争建立一个虚拟地

CD的专栏 1155

Direct3D 地形

高程图生成三角形网格 3D场景的模拟离不开地形,因此3D地形的模拟在3D程序设计中很重要。在3D场景中,地形的表面都是通过三角形网来表示,即通过高度图(也称影像图)上的对应坐标值来表示规则网格顶点坐标值,从而形成规则的矩阵分布三角网。具体的实现就是通过读取高度图上象元的值来获取该坐标位置的高程,然后根据这些高程值及坐标值形成一个规则的三角网。由于在三角网中相邻三角形都具有公共点,所以为了节约内存

面目全非,胸有丘壑 | Json Lee 1475

C# WinForms/WPF集成DirectX三维渲染控件:从架构到性能优化

在C#桌面应用开发中,当需要实现复杂的三维数据可视化时,传统的GDI+往往难以满足性能与交互需求。DirectX作为Windows平台原生的图形API,提供了底层硬件加速能力,是实现高性能3D渲染的关键技术。通过托管封装库(如Managed DirectX或SharpDX),开发者可以在.NET生态中调用DirectX接口,构建可深度集成的三维显示组件。这种技术方案特别适用于工业软件、上位机监控、GIS地形展示等需要实时交互与复杂模型渲染的应用场景。本文以Managed DirectX为基础,详细解析了三维

weixin_33850890的博客 409

Direct-X学习笔记--天空盒

学习了三维的地形系统之后,该抬头看看天空啦。下面学习一下传说中的三维天空系统。其实三维天空的实现比地形系统简单得多,我们只需要用一点点小伎俩,就可以蒙混大多数人。 一.简介 我们在玩游戏的时候,尤其是野外的大场景中,要想更逼真离不开三维天空系统。要用程序来模拟无边无际的天空是不可能的,而聪明的前辈们自然也想到了用“天圆地方”这种古代人们对于天空的认识来作为三维天空设计思想。即用一个足够

puppet_master的专栏 3831

使用Managed DirectX编写游戏

转自 http://dev.gameres.com/Program/Visual/DirectX/ManagedDirectX9Game_01.htm http://dev.gameres.com/Program/Visual/DirectX/ManagedDirectX9Game_02.htm http://dev.gameres.com/Program/Vis...

weixin_30270561的博客 186

Managed DirectX Shader技术实战指南

本文还有配套的精品资源,点击获取 简介:Managed DirectX是微软开发的一套API,允许开发者利用.NET环境控制硬件图形处理。本主题探讨了在.NET中使用Managed DirectX实现复杂图形渲染效果的Shader技术,特别是顶点Shader和像素Shader的应用。Managed DirectX支持高级图形效果的Shader Model 2.0及以上版本。...

weixin_42284380的博客 1220

加载地形的类

#ifndef __TerrainH__ #define __TerrainH__ #include <d3d9.h> #include <d3dx9.h> #include <vector> using namespace std; struct TERRAIN_CUSTOMVERTEX { FLOAT x, y...

weixin_30449239的博客 138

托管DirectX教程:Direct3D设备初始化指南

本文还有配套的精品资源,点击获取 简介:本教程旨在指导初学者在C#环境下使用.NET Framework的Managed DirectX库来初始化Direct3D设备。Direct3D是Microsoft的图形API,用于创建高性能3D图形应用。教程将逐步介绍创建设备、选择适配器、设置呈现目标和模式、创建交换链、设置渲染状态、创建顶点和索引缓冲以及加载纹理等关键步骤,为创建...

weixin_35732273的博客 1240

DirectX 9 地形渲染基础

节选自《DirectX 9 3D游戏设计入门》,全书非常适合3D游戏初学者,吐血推荐。

Direct9学习之--------------------------地形

一. 地形的作用:              地形,不言而喻是用来描绘游戏中高山 盆地 平原等地貌,用于确定对象在游戏世界的空间高度。 二. 地形基本原理:        地形的实现是用地形网格(N行M列矩形网格)和高度图实现,地形网格用于绘制地表,高度图信息则用来表示地形高度。通过高度图的不同高度使地形形成高山盆地等。(高度图中保存了地形高度信息)    

新撰组 1176

3D场景中加载地形 高度图 directx

3D场景中加载地形 高度图 directx

【翻译】managed DirectX (第三章)

使用简单的渲染技术翻译:clayman    至今为止,我们的渲染工作效率都很低。每次渲染场景时,都要分配新的顶点列表,并且所有东西存储在系统内存里。现代显卡集成了足够显存,把顶点数据存放在显存可以获得大幅的新能提升:存放在系统内存里的数据,渲染每一帧时都要拷贝到显卡,这会带来极大的损失。只有移除每帧时的这种分配才能帮助我们提高性能。  使用顶点缓冲(Using Vertex Buffers)Di

Clayman的专栏 5027

海光(hygon)CPU ESXI vSphere 紫屏问题修复记录

海光服务器ESXI 紫屏问题修复

edison202302的博客 3552

从C-EPS到DP-EPS:手把手图解不同EPS类型怎么选,对你的车操控感影响有多大?

方向盘转动时那股细腻的阻尼感,高速过弯时令人安心的沉稳,或是狭窄车位里一根手指就能拨动的轻盈——这些看似微妙的差异,其实都源自转向系统电机那几厘米的安装位置变化。当我们谈论驾驶乐趣时,发动机参数和悬架调校总是占据C位,但真正连接驾驶者与车辆的神经末梢,恰恰是那套默默工作的电动助力转向系统(EPS)。不同EPS类型带来的操控特性差异,可能比马力数字更能定义一辆车的性格。

weixin_30920513的博客 531

Managed Direct3D开发经验浅析

注意:本文主要针对Managed DirectX(MDX) 1.1,而不是MDX2.0或XNA Question 1. 如何正确地安装Managed DirectX(MDX)运行库? 对于开发者,先装好vs2005,然后再安装最新的DirectX SDK即可,SDK会自动安装MDX运行库; 对于使用者,并没有必要安装SDK,仅需正确地安装.Net Framework 2.0 Redis...

weixin_34146410的博客 155

从零开始:使用英伟达nx开发板部署目标检测算法的全过程

英伟达nx开发板烧录、依赖安装、开发板联网

weixin_51192760的博客 3761
上一篇: 使用Managed DirectX编写游戏----理解sample framework 之事件处理
下一篇: 【翻译】Managed DirectX(第十二章 下)
soilwork
博客等级 码龄23年 290粉丝 116原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值