cocos2d-x 3d 地形教程

在cocos2dX 3.6 中,我们为3D部分增加了一个Terrain——地形类,用以模拟现实生活中诸如草地、荒漠、丘陵地带等自然景观,能够丰富游戏场景。这篇教程将会向大家简单介绍一下Terrain类所涵盖的功能,以及基础的用法。

Terrain:
从编程的角度来说,地形就是通过载入一张高度图和若干张细节纹理,从而在场景中绘制若干高低不平的几何体,用以在游戏场景中模拟自然中的沙漠,草原等景象的一种手段。从地形的资源构成角度来说,包含了下面几个元素:

高度图:
高度图是Terrain类的核心,高度图从性质上来说是简单的一张图片,但是图片中的每一个像素的颜色值并不被当做颜色,而是直接被视为一个高度的值。Terrain类就通过高度图所给的高度信息,加以适当的缩放,而构建出整个地形的Mesh结构。

细节图:
如果说高度确定了整个Terrain的几何形状,那么细节图则确定了Terrain的外观。通过将细节图贴在Terrain上,可以制造非常好的表现效果。由于Terrain在世界坐标系下,尺寸可能会非常的大,如果单纯将细节图平铺在Terrain上,表现会非常模糊,效果很不好,因此Terrain通过制定细节图的平铺尺寸来解决此问题。

Alpha贴图:
Alpha贴图用于控制Terrain上的细节图是在Terrain上何处以及如何绘制的。Alpha贴图从本质而言,也是一张图片,但是它将图片中每个像素中的Red,Green,Blue以及Alpha通道对应四张细节图的Terrain某个位置上,每一张细节图颜色比例是多少。通过这种方式,我们就可以在Terrain上同时出现诸如沙丘,石板路,草地等纹理,使得Terrain的外观不再那么单调,同时Alpha贴图的结构简单,可以使用外部的编辑器就能直接修改。

Terrain入门
下面我们从两个例子出发来看看,如何在Cocos2Dx中使用Terrain。这里分为两个部分,第一部分介绍如何创建一个简单的地形,第二部分介绍如何在Terrain上放置一个角色,并通过触摸(点击)使角色在Terrain中漫游。

创建地形
需要创建地形需要分类两步,第一步是构造出Terrain所需参数的结构体——Terrain::TerrainData,第二步,通过该TerrainData来创造地形。
首先,我们需要构建出地形需要的所有细节图:
细节图以Terrain::DetailMap结构体的形式存储,其构造函数有两个参数,第一个是细节图的路径,第二个是细节图的在地形上的平铺尺寸,第二个参数有默认值,是可选的。

Terrain::DetailMap r(“TerrainTest/dirt.dds”);
Terrain::DetailMap g(“TerrainTest/Grass2.dds”);
Terrain::DetailMap b(“TerrainTest/road.dds”);
Terrain::DetailMap a(“TerrainTest/GreenSkin.jpg”);

然后,我们使用细节图构造出Terrain::TerrainData:
Terrain::TerrainData 的构造函数有若干种形式以满足用户不同的构造地形的需求,在这里我们只给出最常用的一种,构造函数的第一个参数为高度图的路径,第二个参数为Alpha贴图的路径,接下来的四个参数为之前所构造的四个DetailMap结构体。
Terrain::TerrainData data(“TerrainTest/heightmap16.jpg”,“TerrainTest/alphamap.png”,
r,g,b,a);

最后,将构造出来的TerrainData传给Terrain::create即可创建出地形了。
_terrain = Terrain::create(data,Terrain::CrackFixedType::SKIRT);
Terrain::create的第二个参数为地形LOD裂缝的缝补方法,LOD简单来讲就是通过将远处的地形块的Mesh加以简化,从而在不影响视觉效果的情况下,提高性能的方法。但是如果临近的两个地形块的LOD不同时,其边缘可能会出现裂缝,为了游戏效果的美观,我们需要对裂缝进行填补。Terrain提供两种方式对裂缝进行填补,分别是裙边以及补边的方法,在这里我们使用裙边法来对裂缝进行填补。
地形的效果如图所示:

创建角色在地形上行走
首先我们先创建一个角色的Sprite3D,让其播放动画并将其加入场景里:

_player = Sprite3D::create(“Sprite3DTest/girl.c3b”);
_player->setCameraMask(2);
_player->setScale(0.08);
_player->setPositionY(_terrain->getHeight(_player->getPositionX(),_player->getPositionZ())+PLAYER_HEIGHT);
auto animation = Animation3D::create(“Sprite3DTest/girl.c3b”,“Take 001”);
if (animation)
{
auto animate = Animate3D::create(animation);
_player->runAction(RepeatForever::create(animate));
}

接着使相机跟随角色的位置移动:

_camera->setPosition3D(_player->getPosition3D()+camera_offset);
_camera->setRotation3D(Vec3(-45,0,0));

马克一下:13:

能把例子中的 地形和天空盒的的模型发一下吗

我想问一个问题就是我在天空盒的例子中将camera的视角角度变化了一下 在茶壶的斜下方出现的黑色的部分 这是什么 是做模型是没有删除的黑片吗

版主 给回个话呗

版主人呢。。。

看不到图,你把天空盒放大一下试试!!

我需要的地图小,但是可以重复怎么处理?可以复用吗? 就像飞机一样,背景复用,地形能复用吗?

马克一下啦!:2:

关于3D地形,建议改进一下算法。

目前基于top-left为坐标原点,取高程的时候使用(1 - u)*(1 - v)*getImageHeight(i, j)*getScaleY() + (1 - u)vgetImageHeight(i, j + 1)getScaleY() + u(1 - v)getImageHeight(i + 1, j)getScaleY() + uvgetImageHeight(i + 1, j + 1)*getScaleY()。这样会造成一个问题,摆一个圆山的话,只有左上角象限高程是基本正常的,其他三个象限都会遇到人物被埋到地下的问题。

原因也很简单,在其他三个象限,i+1和j+1取得的高程永远低于当前点高程,经过合计以后,最终result就会低于当前实际高程。所以,山形只有左上象限高程不会低于当前点高程,向下下坡,向右下坡都会遇到高程被算低的问题。希望下个版本能搞定这个问题哈,要不然这个terrain还是不实用。

我用判断象限取值,简单改了一下算法,也大致能解决这个问题,但是在山巅最尖尖的时候,高程还是不准,版主应该一看我说的就知道我大概是怎么改的了,这种粗糙改法应该是不顶用的,希望版主和高手们能来说说更好的思路。

另外,resetHeightMap()这个方法貌似是废的,不注释掉free(_data)直接崩溃,注释掉后,GL错误,地图消失

请问有什么好的工具来生成需要的高度图,或者把3d模型导出成高度图