【转】游戏开发高度图有关资料与Balder中的相关支持
2019独角兽企业重金招聘Python工程师标准>>>
资料一:使用Managed DirectX创建三维地形
来源:GameRes网站
内容:
使用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轴方向的权重就为:
Sx = 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处理。下一次,我们将仔细讨论这些问题。
下载配套例程
资料二:如何由Height Map生成Normal Map
来源:cnblogs(http://www.cnblogs.com/cxrs/archive/2009/11/01/1594155.html)
内容:
- Nvidia和ATI都有相应的工具把Heightmap转成NormalMap,有了NormalMap,我们就可以用NormalMapping技术进行Per Pixel Lighting计算了。那么HeightMap是怎么转化成NormalMap的呢? 其实并不难, 在《3D 游戏与计算机图形学方法》中,提供了一种由高度图生成法向图的方法。其思想是根据高度图中的象素与其周围象素的高度差,在切空间构造S 向量和T 向量,由SXT 得到法线向量。
用shader 来实现也很简单,VS 和PS 代码如下,上边左图为HeightMap,右图为由下面shader生成的NormalMap,这个方法生成的NormalMap并不够好,在 RenderMonkey中有一个叫NormalmapFilter的Sample,会生成更高质理的NormalMap,有兴趣的朋友可以参考。 VS_OUTPUT main(float4 Pos: POSITION){ VS_OUTPUT Out; // Clean up inaccuracies Pos.xy = sign(Pos.xy); Out.Pos = float4(Pos.xy, 0, 1); // Image-space Out.texCoord.x = 0.5 * (1 + Pos.x); Out.texCoord.y = 0.5 * (1 - Pos.y); return Out; } float4 main(float2 texCoord: TEXCOORD) : COLOR { float2 ff = 1.0 / HeightMapSize; float Scale = 1; // Sample teh neighbor float s0 = tex2D(Heightmap, texCoord + float2(-off.x,0)).r; float s1 = tex2D(Heightmap, texCoord + float2( off.x,0)).r; float s2 = tex2D(Heightmap, texCoord + float2( 0,-off.y)).r; float s3 = tex2D(Heightmap, texCoord + float2(0,off.y)).r; float3 U = float3(1,0,s1 - s0); float3 V = float3(0,1,s3 - s2); float3 normal = normalize(Scale * cross(U,V)); // Pack [-1, 1] into [0, 1] return float4(normal * 0.5 + 0.5,1); } 资料三:Ogre 天龙八部地形 Heightmap(高度图)+GridInfo(地表信息)初步结果
刚研究出的天龙八部的地形高度和GridInfo,正确的载入了高度图和地表信息,可以看出场景的大致样子了:)
第一张图是我自己载入的 明教的光明殿地形,第二张是天龙八部游戏中的场景,可以看出差距啊:)
场景里没有放入mesh,所以不大容易看出来,不过载入mesh就简单了,我将尽快放出新版本截图!
TerrainLiquid是用来做湖水,海水,熔岩之类效果的。最多有两层,一层放贴图动画,另一层作alpah值。
天龙八部TerrainLiquid的实现实在有点那个-3-
对Model的支持只是最简单的载入mesh,还没有加入动画:)
WCollision是天龙八部游戏中用来实现“碰撞”的。下图粉红色区域即为WCollision信息
桥的下面是熔浆,不允许行走的,但是可以从桥上通过,而天龙里不是根据桥这个mesh,来实时检测玩家所应该处的高度,而是通过WCollision里所记录的信息来判断的。
这一点很容易验证,我们把.scene文件里所有的mesh删掉,玩家依然站在了正确的高度,如下图所示
如果把WCollision文件删掉,玩家就会站到熔浆里了:)
因为地表法线算的有问题,一直没察觉,所以灯光这部分绕了点弯路,呵呵!现在场景可漂亮多了:)
这是明教光明殿,灯光比较多
因为还没有加入lightmap所以跟天龙八部游戏本身的效果,还是有点差别:)
.Frame文件是配合.Model文件一起使用,来实现场景节点动画的(NodeAnimationTrack)。比如鸟儿的飞行轨迹,鱼群的活动路线,随风摇曳的灯笼等等。
是否分析天龙八部的Frame文件格式,我酝酿了两三天,因为推测该文件是3DSMAX或Maya的导出插件导出的(后面会给出推测的理由),其格式应该是公开的,但是Google了数次未果,只好手动分析了。
Frame文件开头部分有明显的[Serializer_v1.10]标记,显然是使用Ogre的Serializer类保存的,且存在很多重复的模型,比如天龙寺的铃铛,完全可以使用1个mesh就可以了,但是资源里共有4个mesh,分别命名为铃铛1,铃铛2,铃铛3,铃铛4,大理派主殿就更多了,有十几个铃铛。-3- 其实所有的铃铛,样子都一样,只是相对于中心点(0,0,0点)的偏移不同。这就证明了上面的推测,如果不是直接从MAX或Maya导出的Frame动画,而是自己做一个编辑器来制作动画。那么使用1个放在0,0,0点的铃铛就够了。而且Frame文件中保存的帧的信息不可以直接使用,还是要经过计算 -3-,计算的方法完全是为了将就多个不同偏移的mesh。
这个插件估计跟OFusion类似,应该提供两种版本的Frame文件,一种是xml文本结构的,一种是Serializer二进制结构的。(纯属猜测)
下面是截图(动画用截图不好表现啊- -)
天龙寺的铃铛
飞翔的小鸟
遨游的鱼群
TerrainLiquid加入法线可以说是最简单的了,所有法线一律平行y轴,连算都不用算。现在水面看起来跟游戏里一个德行了-3-
天龙八部的粒子特效做得非常漂亮,漂亮的背后一定有着复杂的实现:),以下是我总结的天龙对Ogre粒子系统的改动内容:
添加发射器1个
PolarEmitter
添加影响器6个
ColourFading
MeshAnimationAffector
MeshRotator
Movement
Revolution
ScaleInterpolator
添加的Renderer 2个
mesh
texcoord_billboard
因为我要实现场景中所使用的粒子效果,对于其它的,比如技能里所使用的粒子则暂时不予考虑。
所以对273个场景文件所使用粒子做了统计如下:
粒子名,使用个数
a_y_dali_05, 1
baofu, 18
caihong, 16
cangying, 11
chuansongdian_01, 1
chuansongdian_03, 11
chuansongdian_04, 10
chuansongdian_05, 1
chuansongmen_03, 14
cj_ba01_big, 10
cj_ba03, 2
cj_denglong_020, 13
cj_ghuochong, 4
cj_huo, 37
cj_kvk01, 1
cj_kvk02, 1
cj_kvk_03, 1
cj_kvkfeng01, 7
cj_xing, 11
foguang, 7
guihun_01, 5
huahuo, 35
huangsha, 110
huoba01, 2624
huoba01_big, 555
huoba02, 615
huoba03, 7
huoba04, 5
huohua_011, 9
huoxing, 1886
huoxing7mi, 6
jian01, 18
jiujiaozhengqi, 93
jiujiaozhengqi_01, 48
kuangdui01, 37
kuangdui02, 354
kuangdui03, 7
kuangdui05, 1
kuangdui_hong, 249
kuangdui_hong01, 259
kuangdui_lan, 270
kuangdui_lan01, 365
kuangdui_lv, 154
kuangdui_lv01, 68
langhua, 86
langhua01, 119
langhua02, 76
penhuo, 120
penhuo_01, 41
penjianyanjiang, 6
pubushuihua_02, 1420
pubushuihua_03, 582
rain, 23
rain_xiao, 6
shandong_01, 18
shedengguangzhu, 93
shedengguangzhu_01, 64
shedengguangzhu_02, 56
shedengguangzhu_03, 17
shedengguangzhu_04, 5
shedengguangzhu_05, 1
shidenglong_01, 167
shidenglong_010, 124
shidenglong_02, 86
shidenglong_020, 19
shuidi, 44
shuidi_01, 158
shuidi_lv_01, 1
shuimianlianyi, 156
snow, 12
sunny01, 23
sunny02, 17
sunny03, 6
sunny04, 45
taohua, 528
taohua_big, 83
wenquan, 95
wenquanzhengqi, 73
xiangluyan, 77
xiangluyan_01, 51
yan01, 96
yan01_da, 4
yanjiangshuihua_001, 46
yanjiangwuqi, 211
yanjiangzhengqi, 34
yezi, 136
yezi01, 186
yezi01_, 2
yezi01__big, 6
yezi01_big, 179
yezi01_i02, 2
yezi01_ihua_02, 20
yezi01_ngdian_04, 12
yezi_big, 24
yezihuyang01, 7
yinghuochong, 1689
yun, 717
yun001, 65
yun002, 103
yun02, 6
yun_01, 17
yun_02, 1
ziti01, 40
然后是对这102种粒子详细信息的统计:
发射器类型,使用个数
Box, 64
Point, 3
PolarEmitter, 4
Ring, 21
影响器类型,使用个数
ColourFading, 83
MeshAnimationAffector, 1
Movement, 39
Revolution, 11
Rotator, 71
ScaleInterpolator, 69
Render类型,使用个数
billboard, 87
mesh, 1
texcoord_billboard, 4
并不存在的粒子
cj_ba01_big
cj_ba03
cj_denglong_020
cj_ghuochong
cj_huo
cj_xing
yezi01_
yezi01__big
yezi01_i02
yezi01_ihua_02
yezi01_ngdian_04
发现场景中使用的这些粒子几乎将天龙添加的那部分全部使用了-3-,本来还想偷懒一下呢,看来全部得实现啊!其中huoba01使用的最多,我就以这个为例子,做出来的效果和游戏中的差不多,如下图:
游戏中的效果
btw:馊狐在09年3月3号左右已经将all.material,all.particle等文件加密了,真囧~~~
从TLSceneViewer中导出,然后用TLSceneImporter导入就可以了,哈哈,效果如下:
如果版本号大于0x00100002则后面跟一个bool型数据,否则不存在这个bool型数据
bool bLarge GridInfo是否为7字节类型
BYTE IndexOrder 对格子的三角形的操作,可能取值如下
0正常三角形索引
1不同于正常的三角形索引
如下图,该值主要用在水池啊一类的地方,如果三角形索引不变的话,水池四个角中的两个角就不对了。
本文出自www.MobileGameBase.com
[GridInfo 7字节版本]
short nFirstLayer
读取后需交换高8位与低8位的值,需做如下操作
nFirstLayer = (nFirstLayer<<8)|(nFirstLayer>>8)
BYTE nFirstLayerOp
short nSecondLayer
同nFirstLayer,需交换高8位与低8位的值
BYTE nSecondLayerOp
BYTE IndexOrder
Ogre场景编辑器基本功能已经完成:)
1.类似于3DSMAX的摄像机,非常方便美术操作
2.动态刷新素材资源
3.支持所有操作任意步撤消
4.可以导入并编辑天龙八部的地表(通过TLSceneViewer导出)
5.可以轻松绘制特定图案,绘制时自动融合
6.可以对地表应用特定格式(Word的格式画刷)
7.多种方式调整地表高度,制作斜坡,悬崖等非常方便
8.支持动态光源,以及光源的特定变化
9.可以很方便的摆放物体,通过快捷键和鼠标既可以直观的调整物体位置、旋转、缩放,也可以在场景组件界面里做特定调整
10.可以自动很快速的生成非常优化的导航网格(Navigation Mesh, Navmesh)
生成的导航网格与天龙八部类似,已经非常优化了。
天龙八部很早就已经放弃Region的方式寻路,改用了.path文件,也就是导航网格,同时继续沿用WCollision,但是WCollision仅只是用来调整人物高度的,并没有参与寻路。
仅做了一点修改:导入后自动显示贴图。3DSMAX SDK比我想象中的麻烦,所以耽搁了两天:)
OgreImporter使用实例将《天龙八部Online》的模型导入到3DS MAX中就可以自由编辑了。
注:OgreImporter不是仅可以导入天龙八部的模型,任何Ogre的mesh文件都可以导入。
龙八部场景导入插件(TLSceneImporter)可以将天龙八部的场景导入到3DSMAX中。导入后场景就可以自由编辑了:)
正文:Balder 3D引擎中的HeightMap支持
下面这张图来自于引擎在线示例快照:
下图来自于我添加材质贴图后生成的一个快照:
相关代码如下:
XAML:
<UserControl x:Class="Balder_tut_Programatic.HeightMapTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Execution="clr-namespace:Balder.Execution;assembly=Balder"
xmlns:Geometries="clr-namespace:Balder.Objects.Geometries;assembly=Balder"
xmlns:View="clr-namespace:Balder.View;assembly=Balder"
xmlns:Materials="clr-namespace:Balder.Materials;assembly=Balder"
xmlns:Lighting="clr-namespace:Balder.Lighting;assembly=Balder" >
<Grid x:Name="LayoutRoot">
<Execution:Game Width="640" Height="480">
<Execution:Game.Camera>
<View:Camera Position="0,50,-40" Target="0,0,0"/>
</Execution:Game.Camera>
<Lighting:OmniLight Position="-100,100,0" Ambient="White" Strength="0.7"/>
<Geometries:Heightmap x:Name="HeightMap"
Dimension="128,64"
LengthSegments="16"
HeightSegments="16"
HeightInput="Heightmap_HeightInput"
InteractionEnabled="True">
<Geometries:Heightmap.Material>
<Materials:Material Ambient="White" ReflectionMap="/Balder_tut_Programatic;component/Assets/cloudless.png" Specular="White" Shade="Gouraud" />
</Geometries:Heightmap.Material>
</Geometries:Heightmap>
</Execution:Game>
</Grid>
</UserControl>
后台代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Balder.Objects.Geometries;
using Balder.Rendering;//DetailLevel
namespace Balder_tut_Programatic
{
public partial class HeightMapTest : UserControl
{
private double _sin;
private double _movement;
public HeightMapTest()
{
InitializeComponent();
}
private void Heightmap_HeightInput(object sender, HeightmapEventArgs e)
{
var height = System.Math.Sin(_sin + _movement) * 2;
//var height=1.2;
e.Height = (float)height;
var highlight = (byte)((height * 16f) + 32f);
e.Color = Color.FromArgb(0xff, highlight, highlight, highlight);
_sin += 0.03;
if (e.GridX == HeightMap.LengthSegments &&
e.GridY == HeightMap.HeightSegments)
{
_sin = 0;
_movement += 0.05;
}
}
}
}
文章转自:http://space.itpub.net/14466241/viewspace-671625
转载于:https://my.oschina.net/u/1446855/blog/643524
【转】游戏开发高度图有关资料与Balder中的相关支持相关推荐
- 游戏开发高度图有关资料与Balder中的相关支持
资料一:使用Managed DirectX创建三维地形 来源:GameRes网站 内容: 使用Height Map作为输入 首先,什么是高度图(Height Map)呢?所谓高度图实际上就是一个2维数 ...
- 陈力:传智播客古代 珍宝币 泡泡龙游戏开发第44讲:PHP程序设计中的COOKIE
陈力:传智播客古代 珍宝币 泡泡龙游戏开发第44讲:PHP程序设计中的COOKIE 服务器在客户端保存用户的信息,就要用到cookie.Cookie在客户机(浏览器)就是一个字符串,可以通过setco ...
- 陈力:传智播客古代 珍宝币 泡泡龙游戏开发第47讲:PHP程序设计中的文件操作
陈力:传智播客古代 珍宝币 泡泡龙游戏开发第47讲:PHP程序设计中的文件操作 描述:PHP程序设计语言中文件操作的函数很多,如何获取文件的信息,如果打开文件,如果关闭文件,读取文件,写入文件,复制文 ...
- 陈力:传智播客古代 珍宝币 泡泡龙游戏开发第46讲:PHP程序设计中的session应用实例
陈力:传智播客古代 珍宝币 泡泡龙游戏开发第46讲:PHP程序设计中的session应用实例 Session是服务器端技术, 可以把各自的数据放在各自的session中.本文实现了用Session实现 ...
- 陈力:传智播客古代 珍宝币 泡泡龙游戏开发第45讲:PHP程序设计中的session技术
陈力:传智播客古代 珍宝币 泡泡龙游戏开发第45讲:PHP程序设计中的session技术 Session是服务器端技术,可以把各自的数据放在各自的session中.通过session_start(); ...
- 陈力:传智播客古代 珍宝币 泡泡龙游戏开发第43讲:PHP程序设计中的MVC模式
陈力:传智播客古代 珍宝币 泡泡龙游戏开发第43讲:PHP程序设计中的MVC模式 模型(M)-视图(V)-控制器(C)mvc是一种软件设计模式,通过MVC思想进行实例开发和程序设计. 在前面的 ...
- 【游戏开发实战】教你在Unity中实现模型消融化为灰烬飘散的效果(ShaderGraph | 消融 | 粒子系统 | 特效)
文章目录 一.前言 二.ShaderGraph环境准备 三.模型准备:原神角色模型 四.实现思路 1.效果一的实现思路 2.效果二的实现思路 五.ShaderGraph具体实现 1.效果一 1.1.创 ...
- 【iOS-cocos2d-X 游戏开发之十四】cocos2dx(c++)中访问object函数
Cocos2dx系列博文的上一篇详细介绍了如何在Xcode中利用jni调用Android的Java层代码,还没有看过的童鞋,请移步到如下博文: [iOS-cocos2d-X 游戏开发之十三]详细讲解在 ...
- 《Python和Pygame游戏开发指南》——1.12 图书中的文本折行
本节书摘来自异步社区<Python和Pygame游戏开发指南>一书中的第1章,第1.12节,作者[美]Al Sweigart(斯维加特), 李强 译,更多章节内容可以访问云栖社区" ...
最新文章
- centos7 下搭建git服务器
- epoll的使用实例
- 征名公布|Qtum量子链企业版—Unita 中文名征集圆满落幕
- java 日期处理工具类_Java日期处理工具类DateUtils详解
- pythonjava有什么区别_Python与JAVA有何区别?
- Android下写一个永远不会被KILL掉的进程/服务
- Orleans 3.0 发布,微软下一代云计算编程模式
- 大数据分析双剑合璧:Apache Kylin 和 Superset
- 数据结构学习(1):单链表
- mysql 动态游标 , 实际上不要使用, 因为两个人同时执行会删除了其中一个人的数据...
- C++沉思录中文版pdf
- oracle查询备份存储路径,Oracle数据库备份和恢复(导出,导入)
- 解决黑苹果睡眠唤醒后立马死机(AppleHDAHDMI_DPDriver)
- Hadoop HA架构
- logout 注销登录
- [配置文件]_start.wzd
- 基于SqlServer的DML(数据查询)实验,掌握select查询语句的使用、掌握有无条件查询、结果排序与分组、掌握视图用法
- 2020/09-2020/11 读书摘录
- vue element el-date-picker绑定对象 v-model=‘xx.xx‘清空后数据还是显示
- 机器学习案例——客户流失预测