0
点赞
收藏
分享

微信扫一扫

Cocos2d-x之3D


Cocos2d-x是一个二维(2D)游戏引擎。但在Cocos2d-x版本3开始,3D特性被加了进来并进行了改进。3D游戏市场非常巨大, Cocos2d-x正在添加3D开发所需的所有功能 。

3D技术

  • Mesh(网格): 顶点,用于构造要渲染的形状和纹理。
  • Model( 模型 ): 可以渲染的对象 ,它是风格的集合。在Cocos2d-x中就是Sprite3D对象。
  • Texture( 纹理 ):三维模型的所有曲面和顶点都可以映射到纹理。大多数情况, 每个模型有多个纹理 , 在纹理图集中展开。
  • Camera(照相机): 因为三维世界不是平面的,你需要设置一个相机来观察它。 使用不同的相机参数可以得到不同的场景。
  • Light(灯光): 应用灯光使场景看起来逼真。 为了使一个物体看起来真实,颜色应该根据光线变化。 当你面对光明时,它是光明的,相反的是黑暗的。 照亮物体意味着根据光线计算物体的颜色。

Sprite3D对象

就像2D游戏,3D游戏也有Sprite对象。Sprite对象是任何游戏的核心基础。Sprite3D和Sprite唯一不同的是Sprite3D对象有3个轴,可以定位在x、y和z轴上。 Sprite3D在很多方面都像普通的Sprite一样工作。 加载并展示Sprite3D对象:

auto sprite = Sprite3D::create("boss.c3b"); //c3b file, created with the FBX-converter
sprite->setScale(5.f); //sets the object scale in float
sprite->setPosition(Vec2(200,200)); //sets sprite position
scene->addChild(sprite,1); //adds sprite to scene, z-index: 1

​.c3b​​3D文件可以用FBX-converter工具来创建

现在,让我们来循环旋转模型。为此,我们将创建一个操作并运行它:

//rotate around the X axis
auto rotation = RotateBy::create(15, Vec3(0, 360, 0));
//our sprite object runs the action
sprite->runAction(RepeatForever::create(rotation));

要在Sprite或Sprite3D上设置定位点,请使用:

sprite->setAnchorPoint(Point(0.0f,0.0f));

将三维模型附加到Sprite3D对象

请记住,三维模型是网格的集合。可以将三维模型附加到其他三维模型以创建丰富的效果。如添加武器到主角上,为了做到这一点,你需要找到武器将被添加的附着点。可以调用getAttachNode(attachment_point_name)函数来找到。 然后我们用addChild()将新模型作为子模型添加到附件点 。

auto sp = Sprite3D::create("axe.c3b");
sprite->getAttachNode("Bip001 R Hand")->addChild(sp);

交换三维模型

当做3D开发时,你可能要对你的模型做些动态改变 。这种改变可能是换装、 视觉线索 、通知用户关于模型的状态变化。 如果三维模型由网格组成,则可以使用​​getMeshByIndex()​​​和​​getMeshByName()​​访问网格数据。 . 使用这些功能,可以实现像用武器或衣服交换角色一样的效果。

auto sprite = Sprite3D::create("ReskinGirl.c3b");

// display the first coat
auto girlTop0 = sprite->getMeshByName("Girl_UpperBody01");
girlTop0->setVisible(true);

auto girlTop1 = sprite->getMeshByName("Girl_UpperBody02");
girlTop1->setVisible(false);

// swap to the second coat
girlTop0->setVisible(false);
girlTop1->setVisible(true);

Animation动画

创建​​Animate3D​​对象,使用动画。

// the animation is contained in the .c3b file
auto animation = Animation3D::create("orc.c3b");

// creates the Action with Animation object
auto animate = Animate3D::create(animation);

// runs the animation
sprite->runAction(RepeatForever::create(animate));

多动画
同时运行多个动画。

auto animation = Animation3D::create(fileName);

auto runAnimate = Animate3D::create(animation, 0, 2);
sprite->runAction(runAnimate);

auto attackAnimate = Animate3D::create(animation, 3, 5);
sprite->runAction(attackAnimate);

上面这个例子有两个动画。第一个立刻执行,持续2秒,第二个在3秒后执行,持续5秒。

Animation speed动画速度
向前的动画速度是一个正整数,负速度则是相反的方向。

Animation blending动画混合
当使用多个动画时, 在每个动画之间自动应用混合。混合的目的是在效果之间创建平滑过渡。 给定两个动画A和B,动画A的最后几帧和动画B的前几帧重叠以使动画中的更改看起来自然。 默认的过渡时间是0.1秒。可以使用Animate3D::setTransitionTime修改过渡时间。
Cocos2d-x只支持关键帧之间的线性插值。 这将填充曲线中的间隙以确保路径平滑。如果在模型制作中使用其他插值方法,我们的内置工具fbx-conv将生成额外的关键帧以进行补偿。

Camera照相机

3D世界不是平的,我们需要使用照相机观察它。因此Camera对象在3D开发中是非常重要的。Camera对象继承Node,因此支持大多数相同的动作对象。有两类Camera对象: perspective camera (透视照相机)和orthographic camera(正交相机)。

  • 透视照相机用于观看具有近到远效果的对象 。透视照相机的效果:在近处的物体会比较大,在远处的物体会比较小。
  • 正交摄影机可以将其理解为将三维世界转换为二维表示。使用正交摄影机可以看到,无论对象距摄影机对象有多远,它们的大小都是相同的。游戏中的小地图通常使用正交摄影机渲染。另一个例子是自上而下的视图,可能是在地牢风格的游戏中。

每一个​​Scene​​自动地创建一个默认的camera, 基于Director对象的投影特性 。如果你要更多的camera,可以用如下代码创建:

auto s = Director::getInstance()->getWinSize();
auto camera = Camera::createPerspective(60, (GLfloat)s.width/s.height, 1, 1000);

// set parameters for camera
camera->setPosition3D(Vec3(0, 100, 100));
camera->lookAt(Vec3(0, 0, 0), Vec3(0, 1, 0));

addChild(camera); //add camera to the scene

创建正交照相机
默认的Camera是透视照相机,如果要创建正交照相机,可以用如下代码来创建:

Camera::createOrthographic();

如:

auto s = Director::getInstance()->getWinSize();
auto camera = Camera::createOrthographic(s.width, s.height, 1, 1000);

从相机中隐藏对象
有时,我们并不想在照相机里显示所有对象。从一个照相机里隐藏一个对象其实也很简单,通过在Node(节点)上调用setCameraMask(CameraFlag)和在Camera上调用setCameraFlag(CameraFlag)即可:

//Camera
camera->setCameraFlag(CameraFlag::USER1);

//Node
node->setCameraMask(CameraFlag::USER1);

Cubemap Texture立方纹理

立方体贴图纹理是六个单独的正方形纹理的集合,这些纹理放置在虚拟立方体的面上。它们通常用于在对象上显示无限远的反射,类似于“天空盒”在背景中显示遥远景物的方式。展开的立方体映射的外观 :

Cocos2d-x之3D_2d


Cocos2d-x创建立方纹理的方法:

// create a textureCube object with six texture assets
auto textureCube = TextureCube::create("skybox/left.jpg", "skybox/right.jpg", "skybox/top.jpg", "skybox/bottom.jpg", "skybox/front.jpg", "skybox/back.jpg");

// set cube map texture parameters
Texture2D::TexParams tRepeatParams;
tRepeatParams.magFilter = GL_NEAREST;
tRepeatParams.minFilter = GL_NEAREST;
tRepeatParams.wrapS = GL_MIRRORED_REPEAT;
tRepeatParams.wrapT = GL_MIRRORED_REPEAT;
textureCube->setTexParameters(tRepeatParams);

// create and set our custom shader
auto shader = GLProgram::createWithFilenames("cube_map.vert", "cube_map.frag");
auto _state = GLProgramState::create(shader);

// bind cube map texture to uniform
state->setUniformTexture("u_cubeTex", textureCube);

Skybox

Skybox是你整个场景的包装,它显示了你的几何之外的世界。你可以用一个天空盒来模拟无限的天空、山脉和其他现象。 创建Skybox:

// create a Skybox object
auto box = Skybox::create();

// set textureCube for Skybox
box->setTexture(_textureCube);

// attached to scene
_scene->addChild(box);

Light光线

光线对于营造游戏的气氛和氛围非常重要。目前支持4种照明技术。

  • Ambient Light(环绕光): AmbientLight对象将均匀地为场景中的所有对象应用灯光。效果就像办公室里的照明。灯光在头顶,当你看到办公室周围的物体时,你会看到它们在同一个灯光下。

auto light = AmbientLight::create (Color3B::RED);
addChild(light);

  • Directional Light(平行光、定向光): 平行光通常用于模拟光源,如阳光。当使用方向光时,请记住它的密度是相同的,不管你和它的关系是什么。当你直视太阳时,它是一种强烈的光,即使你朝任何方向移动几步 。

auto light = DirectionLight::create(Vec3(-1.0f, -1.0f, 0.0f), Color3B::RED);
addChild(light);

  • Point Light点光源:点光源通常用于模拟灯泡、灯或火炬的效果。 点光源的方向是 从发光位置到点光源 。离光源近则亮,远则暗。

auto light = PointLight::create(Vec3(0.0f, 0.0f, 0.0f), Color3B::RED, 10000.0f);
addChild(light);

  • Spot Light聚光源:聚光源通常用于模拟手电筒。:

auto spotLight = SpotLight::create(Vec3(-1.0f, -1.0f, 0.0f), Vec3(0.0f, 0.0f, 0.0f),
Color3B::RED, 0.0, 0.5, 10000.0f) ;
addChild(spotLight);

遮光

在节点上使用照明遮罩仅对其应用特定的光源。例如,如果场景中有多个灯光,则节点只能由其中一个灯光而不是全部三个灯光照亮。可以使用setLightFlag(LightFlag)控制灯光影响哪些节点对象。需要注意的是,所有光源都是在一个过程中渲染的。由于移动平台性能问题,不建议使用多个光源。默认最大值为1。如果要打开多个光源,必须在info.plist中定义以下键:

<key> cocos2d.x.3d.max_dir_light_in_shader </key>
<integer> 1 </integer>
<key> cocos2d.x.3d.max_point_light_in_shader </key>
<integer> 1 </integer>
<key> cocos2d.x.3d.max_spot_light_in_shader </key>
<integer> 1 </integer>

Terrain地形

纹理用于表示高度图。最多可以使用4种纹理来混合地形、草地、道路等的细节。

HeightMap高度图

高度图对象是地形图的核心。与普通图像不同,高度图表示顶点的高度。它决定了地形的几何形状。

DetailMap详细图

DetailMap对象是确定地形细节的纹理列表,最多可以使用四个纹理。

AlphaMap字母表

AlphaMap对象是一个图像,其数据是细节贴图的混合权重。混合结果是最终地形的外观。

LOD(Level Of Detail)政策

地形使用一种称为细节层次(Level Of Detail,LOD)的优化技术。这是一种渲染技术,当对象与摄影机的距离增加时,它可以减少对其进行渲染的垂直(或三角形)数量。用户可以通过调用Terrain::setLODDistance(float lod1, float lod2, float lod3) 方法来设置到相机的距离。
相邻的地形对象块,其LOD不同,可能会产生裂纹伪影。地形提供了两个功能来避免它们:

Terrain::CrackFixedType::SKIRT
Terrain::CrackFixedType::INCREASE_LOWER

Terrain::CrackFixedType::SKIRT: 将在块的每个边上生成四个类似裙子的网格。
Terrain::CrackFixedType::INCREASE_LOWER: 将动态调整每个块索引以无缝连接它们。

创建Terrain地形
以下代码片段创建一个玩家并将其放置在地形图上:

player->setScale(0.08);
player->setPositionY(terrain->getHeight(player->getPositionX(),player->getPositionZ()));

创建所有DetailMap对象,最多4个。我们需要将DetailMap对象传递给Terrain::DetailMap结构体:

Terrain::DetailMap r("dirt.dds");
Terrain::DetailMap g("grass.dds");
Terrain::DetailMap b("road.dds");
Terrain::DetailMap a("greenSkin.jpg");

用DetailMap对象创建TerrainData变量,并指定高度图文件路径和字母图文件路径:

Terrain::TerrainData data("chapter9/heightmap16.jpg","TerrainTest/alphamap.png", r, g, b, a);

传递​​TerrainData​​​对象给​​Terrain::create​​,最后一个参数决定LOD政策:

_terrain = Terrain::create(data, Terrain::CrackFixedType::SKIRT);

如果你设置​​Terrain​​​对象照相机遮罩(camera mask)并添加到一个节点Node或一个场景Scene。这时要当心,​​Terrain​​被添加到节点或场景后,就不能再用transform(translate,scale)。另外,如果在addChild方法之后做以上操作,可能会导致一个错误。

使用方法Terrain::getHeight(float x, float z, Vec3 * normal= nullptr)可以获得指定位置的高度。当你想把一个Sprite3D对象或任意节点放在地形图上时,这个方法特别有用。

射线地形交会试验将会计算指定位置的交点。
​​​Terrain::CrackFixedType::SKIRT​​​会产生四个 每一块边缘都有裙状网格 。
​​​Terrain::CrackFixedType::INCREASE_LOWER​​将会动态调整每个块索引以无缝连接它们。


举报

相关推荐

0 条评论