我的编程空间,编程开发者的网络收藏夹
学习永远不晚

教你在Cocos2D-X中用3D Toolkit for Cocos2D-X制作3D图形

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

教你在Cocos2D-X中用3D Toolkit for Cocos2D-X制作3D图形

  看到文章标题,估计很多同学就有疑问了,Cocos2d-x不是2D游戏的开发引擎么,怎么做出3D效果?本篇教程将为你揭晓如何在Cocos2D-X中用3D Toolkit for Cocos2D-X制作3D图形。

  当前ToolKit中几个最重要的类是:Cc3dScene,Cc3dNode,Cc3dSprite。基本上这三个类引出所有的类。

教你在Cocos2D-X中用3D Toolkit for Cocos2D-X制作3D图形_Cocos2D-X_Cocos2D-X教程_Cocos2D-X 3D_编程学习网

  一,Cc3dNode:

  Cc3dNode是3D节点类,相当于cocos2d-x的CCNode。

  Cc3dNode继承自CCNode,增加了以下数据成员:

  Cc3dMatrix4*m_mat

  Cc3dcamera*m_camera

  m_mat是为4x4的3d变换矩阵,记录本节点相对于父节点的变换。

  在CCNode中是用m_fRotation、m_fScale、m_fSkew、m_obAnchorPoint、m_obPosition等量来记录相对于父节点变换的(需要的时候再合成为矩阵)。当然,你也可以通过对上述成员作扩充使其能够描述3D变换,不过出于简单我没有利用它们而是另外添加了m_mat来记录相对于父节点的3D变换。

  m_camera为本节点所使用的相机的引用。

  Cc3dCamera继承自CCCamera,由于CCCamera本身就是3D摄像机,我们直接继承它的set/getEyeXYZ()、set/getCenterXYZ()、set/getUpXYZ()和locate()等方法,便可实现正确的视图变换。

  但还是进行了一些扩展,把投影变换的功能也加入到Cc3dCamera中(在cocos2d-x中投影变换的功能放在了CCDirector中)。而且像cocos2d-x一样,我们支持透视投影模式(perspective mode)和正交投影模式(orthographic mode)。在test1 demo中可以切换这两种模式来观察效果。

  在cocos2d-x中每个节点都有自己的相机,可以对单个节点实现摄像机动画。不过对于3D而言,这种需求不存在, 同一个场景只需要一个世界像机,因此Cc3dNode的m_camera成员只是世界相机的引用。正常情况下我们用3D场景根节点的像机作为此3D场景的世界像机。

  重载visit函数。

  Cc3dNode对CCNode的visit函数进行了重载。Cc3dNode::visit的逻辑与CCNode::visit基本一样,只是将this->transform()换成了this->transform3D()。transform()中是实现由m_fRotation、m_fScale、m_fSkew、m_obAnchorPoint、m_obPosition等计算变换矩阵并将其乘到模型视图堆栈的栈顶。类似地transform3D()实现将m_mat乘到模型视图堆栈的栈顶。

  实现若干3D变换方法。

  类似于CCNode的setPosition()、setRotation()等方法,Cc3dNode中实现了如setPosition3D(),translate3D(),translateRelativeToParent3D(); setRotation3D(),rotate3D(),rotateRelativeToParent3D()等(rotate3D的第一个参数是旋转轴的单位方向向量)。值得注意的是XXX3D()和XXXRelativeToParent3D()之间的差别。举个例子:假设定义了一个太阳节点,一个地球节点,并将地球节点添加为太阳节点的子节点,假设已经通过setPosition3D()设定好了地球相对于太阳的偏移,每帧调用earth->rotation3D(...)你会发现地球开始自转,每帧调用earth->rotateRelativeToParent3D(...)你会发现地球开始围绕太阳公转。(demo test1中对此有演示)。

  类似于CCNode的nodeToWorldTransform(),worldToNodeTransform(),convertToWorldSpace(),convertToNodeSpace()等方法,Cc3dNode中实现了nodeToWorldTransform3D(),worldToNodeTransform3D(),convertToWorldSpace3D(),convertToNodeSpace3D()等方法。

  3D变换不如2D变换直观,不熟悉矩阵变换的同学可以多做实验以摸清其行为。

  isSceneNode3D方法。

  nodeToWorldTransform3D()函数需要计算当前节点至3D场景根节点路径上所有节点的变换矩阵之积,我们需要使用Cc3dNode::isSceneNode方法来判断是否已到达3D场景根节点。

  Cc3dNode::isSceneNode3D()方法默认返回false,对于3D场景根节点,我们重载使它返回true。

  二,Cc3dScene:

  Cc3dScene是3D场景根节点类,相当于cocos2d-x中的CCScene。

  Cc3dScene继承自Cc3dNode。

  重载isSceneNode3D方法。

  前面已经提到了,3D场景根节点重载isSceneNode3D()使其返回true。

  重载visit方法。

  根节点的visit函数要比普通节点多做一些设置初始状态的工作,首先保存投影堆栈和模型视图堆栈的状态,然后使用本节点的像机的投影矩阵和视图矩阵重设矩阵堆栈栈顶。注意我们在切换到模型视图堆栈后先loadIdentity(),这表示我们不关心祖先节点的变换,强制回到世界原点,此后我们的3D场景从世界原点开始生长。

  完成以上初始化工作后再调用父类同名函数执行与普通节点相同visit代码。

  3D场景的组织。

  推荐的做法是将Cc3dScene节点加到一个CCLayer上,然后再在上面添加Cc3dNode、Cc3dSprite等节点。可以参考demo test1的Cscene3DLayer::init() 中的内容(在scene3DLayer.cpp文件中),那里我们创建了一个Cc3dScene和3个Cc3dSprite,并将它们组装成场景。

  三,Cc3dSprite:

  Cc3dSprite是3D精灵类,相当于cocos2d-x中的CCSprite。

  Cc3dSprite继承自Cc3dNode。主要增加了以下数据成员:

  CCTexture2D* m_texture;

  Cc3dMaterial* m_material;

  Cc3dMesh* m_mesh;

  Cc3dIndexVBO3d* m_indexVBO;

  Cc3dProgram* m_program;

  Cc3dUniformPassor* m_uniformPassor;

  Cc3dLightSource* m_lightSource;

  以上各指针成员指向的对象都可更换并遵从引用计数。

  以上这些指针成员都有相应的get/set函数,表示这些指针成员所指向的对象是可更换的(例如更换mesh、更换shader program),这种可装配的特点用起来非常灵活。另外这些对象都是CCObject的子类,遵从cocos2d-x的引用计数规则,因此是可共享的(例如一个mesh或shader program可以同时用于多个sprite)。

  在Cc3dSprite::init()中为这些指针成员创建了默认对象。

  m_texture就是cocos2d-x中的CCTexture2D。

  像CCSprite一样,我们令一个Cc3dSprite只包含一个texture。对于多texture的3d模型,可以使用多个Cc3dSprite,或者事先将纹理图片合并。

  m_mesh提供顶点数据。

  Cc3dMesh是一个索引三角网,数据成员如下:

  vector m_positionList;

  vector m_texCoordList;

  vector m_normalList;

  vector m_colorList;

  vector m_IDtriList;

  其中m_positionList,m_texCoordList,m_normalList,m_colorList为顶点数据,给出3D模型的顶点信息。m_IDtriList为索引数据,描述3D模型的拓扑结构。

  在c3dDefaultMeshes.h中实现了若干基本几何体mesh的创建函数。

  m_program继承自CCGLProgram,但在其基础上做了扩展。

  Cc3dProgram在CCGLProgram基础上添加了一个uniform映射表,可以通过Cc3dProgram::attachUniform实现将GLint型的uniform句柄与uniform变量名绑定在一起。然后便可以通过相应的passUnifo函数实现按变量名为uniform变量传值,使用起来很方便。

  和cocos2d-x的CCGLProgram一样,Cc3dProgram可以通过传入vertex shader和fragment shader的文件名来创建,而且与CCGLProgram一样,可以通过CCShaderCache::sharedShaderCache()->addProgram(program,key)将Cc3dProgram对象添加到CCShaderCache中。

  目前我在c3dDefaultPrograms.h/.cpp中创建了两个默认的shader program,一个是texOnly,一个是classicLighting,前者没有光照效果只有纹理颜色,cocos2d-x中用的最多的便是这一款;后者是经典的光照shader,可以在橙宝书上找到。

  对于不了解shader的同学,橙宝书是不错的入门教材。

  m_indexVBO3d实现向显卡提交顶点和索引数据及发送绘制命令。

  Cc3dIndexVBO3d成员包含以下几个buffer句柄:

  GLuint m_posBuffer;

  GLuint m_texCoordBuffer;

  GLuint m_normalBuffer;

  GLuint m_colorBuffer;

  GLuint m_indexBuffer;

  通过相应的submit函数可以将顶点数据或索引数据提交到相应的buffer中。

  而Cc3dIndexVBO3d::draw()函数用来发送绘制命令。一旦调用此函数,buffer中的数据便传入shader program进行绘制。

  顶点数据又称为attribute数据,因为顶点数据最终要传给shader中的attribute变量,cocos2d-x 2.2中定义了一些内置的attributte变量,它们是a_position,a_color,a_texCoord。我们发现没有a_normal,然而对于3D绘图来说法线属性是非常重要的,计算光照必须用到法线。所以我们必须自己把这个属性加进去。添加方法可以在项目中搜kCCAttributeNameNormal和kCCVertexAttrib_Normals来查看相关代码。

  另外注意在cocos2d-x中position是2维向量,而在3D中我们要用4维向量,因此在设置属性数据格式时我们要用glVertexAttribPointer(kCCVertexAttrib_Position, 4,…)而非glVertexAttribPointer(kCCVertexAttrib_Position, 2,…)。至于shader中属性变量a_position的类型cocos2d-x用的本来就是vec4。

  m_texture,m_material,m_lightSource,以及从Cc3dNode继承过来的m_camera提供uniform值。

  我们知道shader program需要传入两类数据,一类是attribute数据(顶点数据),一类是uniform数据。顶点数据从VBO buffer中来(最终是从mesh中来),而uniform数据则从texture,material,lightSource,camera等对象中提取。这也正是我们要将lightSource和camera这样的外部对象引入到Cc3dSprite之中的原因--这样我们便可以从Cc3dSprite中提取到所有所需的uniform数据。

  Cc3dMaterial是材质对象,继承自CCObject,包含如下数据成员:

  Cc3dVector4 m_ambient;//环境颜色

  Cc3dVector4 m_diffuse;//散射颜色

  Cc3dVector4 m_specular;//镜面颜色

  float m_shininess;//镜面指数

  emission(自发光颜色)由于尚未用到,暂时还没有添加进来。

  Cc3dLightSource是光源对象,继承自Cc3dNode,增加了如下数据成员:

  Cc3dVector4 m_ambient;//环境颜色

  Cc3dVector4 m_diffuse;//散射颜色

  Cc3dVector4 m_specular;//镜面颜色

  将Cc3dLightSource定义为Cc3dNode的子类,想法是将光源也看作一个节点挂到场景树上。

  目前光源的实现还过于简单,将来如果要实现不同类型的光源,还需要更多属性。

  m_uniformPassor实现uniform传值逻辑。

  Cc3dUniformPassor的数据成员是一个函数指针void (*m_callback)(Cc3dNode*, Cc3dProgram*)。成员函数是setCallback和executeCallback两个方法。这样设计的目的是可以让用户自己去实现uniform的传值逻辑。因为对于ToolKit框架而言,用户所使用的shader program有哪些uniform变量不可预知,用户的sprite有哪些成员数据不可预知(因为用户使用的可能是Cc3dSprite的子类,有新增的数据成员),所以uniform传值逻辑不可能通过ToolKit框架来解决,只能留给用户自己去实现。用户写一个满足void (*m_callback)(Cc3dNode*, Cc3dProgram*)形式的回调函数实现uniform传值逻辑,然后将此回调函数set给uniformPassor,则uniformPassor便会在合适的时候调用executeCallback完成传值。

  回调函数有Cc3dNode*node和Cc3dProgram*program两个参数,意即从node中提取uniform值传给program的相应uniform变量,node可以通过类型转化转成用户所使用的Cc3dNode子类,从而访问其数据。

  uniform传值回调函数的实例可以参考c3dDefaultPassUniformCallback.h/.cpp。

  通常是一个(sprite,program)组合对应一个callback,当为sprite更换shader program时应相应地更换callback(或uniformPassor)。

  重载draw函数。

  Cc3dSprite::draw()中大致完成以下动作:

  使用shader program:program->use().

  为cocos2d-x的build-in uniform传值:program->setUniformsForBuiltins()

  (注:cocos2d-x 2.2中的build-in uniform有:

  CC_PMatrix

  CC_MVMatrix

  CC_MVPMatrix

  CC_Time

  CC_SinTime

  CC_CosTime

  CC_Random01

  CC_Texture0

  CC_Alpha_value)

  执行用户自定义的uniform传值回调: uniformPassor->executeCallback(...)

  绑定纹理

  indexVBO发送绘图命令: indexVBO->draw(…)。

  于是,一个3D sprite就显示出来了。

  ----补充:

  矩阵堆栈。

  矩阵堆栈是OpenGL 1.0中内置的设施,但在OpenGL 2.0中去掉了,OpenGL 2.0因使用可编程管线使API得以大幅度简化,以前固定管线的一些API和基础设施便被删掉除了,这是非常合理的,因为核心越小越容易维护,外围的东西完全可以由用户自己去实现。cocos2d-x中实现了矩阵堆栈,有三个堆栈:projection matrix stack, modelview matrix stack, texture matrix stack,其中最常用的是前两个。

  有若干操纵函数,其中较重要的有:

  kmGLMatrixMode:切换堆栈

  kmGLPushMatrix:复制当前栈顶矩阵并压入栈

  kmGLPopMatrix:移除当前栈顶矩阵

  kmGLLoadIdentity:将当前栈顶矩阵设成单位阵

  kmGLLoadMatrix:将当前栈顶矩阵设成指定矩阵

  kmGLGetMatrix:获得当前栈顶矩阵

  我们知道递归本质上就是栈的操作,所以在递归渲染场景树过程中使用矩阵堆栈来记录变换状态是非常好用的。

  ToolKit中的矩阵和向量类。

  矩阵类只提供4x4矩阵c3dMatrix4;向量类只提供2维和4维向量c3dVector2和c3dVector4。

  对于3D而言,只使用4维向量和4x4矩阵就好(w=0的4维向量表示空间向量,w=1的4维向量空间表示点,4x4矩阵描述空间变换),相信图形程序员都习惯于这种方式,所以我不打算定义3维向量和3x3矩阵了,那只会增加混乱。

  ToolKit原则。

  不修改cocos2d-x代码。

  尽可能符合cocos2d-x的思维和风格。

  可读性>>性能。

  好了,本篇教程到这里结束了,希望对大家有所帮助。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

教你在Cocos2D-X中用3D Toolkit for Cocos2D-X制作3D图形

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

教你在Cocos2D-X中用3D Toolkit for Cocos2D-X制作3D图形

编程学习网:看到文章标题,估计很多同学就有疑问了,Cocos2D-X不是2D游戏的开发引擎么,怎么做出3D效果?本篇教程将为你揭晓如何在Cocos2D-X中用3D Toolkit for Cocos2D-X制作3D图形。
教你在Cocos2D-X中用3D Toolkit for Cocos2D-X制作3D图形

教你怎样在Cocos2D-X中制作人物动作

编程学习网:我们在玩游戏的时候,人物角色总会有各种各样的动作,本篇教程为你揭秘怎样在Cocos2D-X中制作人物动作。
教你怎样在Cocos2D-X中制作人物动作

编程热搜

  • 教你如何为你的Cocos2D-X资源加密解密
    编程学习网:我们平时在用Cocos2D-X时都不想让别人看到自己的文件,本篇教程将教你如何为你的Cocos2D-X资源加密解密。
    教你如何为你的Cocos2D-X资源加密解密
  • 教你如何用Cocos2D实现3d效果
    编程学习网:经过了之前几篇教程,相信各位同学已经对Cocos2D不陌生了,本篇教程将教你如何用Cocos2D实现3d效果。
    教你如何用Cocos2D实现3d效果
  • 零基础学习之教你如何用Cocos2D实现画中画效果
    编程学习网:没学过Cocos2D?没关系,多看看我们的教程,一步步成为游戏开发大神。本篇教程将教你如何用Cocos2D实现画中画效果。
    零基础学习之教你如何用Cocos2D实现画中画效果
  • 解析安卓Cocos2D-X2转移项目到Cocos2D-X3过程
    编程学习网:相信有一部分同学都经历过将项目从Cocos2D-X2转到Cocos2D-X3的麻烦,本篇教程将解析将项目从Cocos2D-X2转到Cocos2D-X3的过程。
    解析安卓Cocos2D-X2转移项目到Cocos2D-X3过程
  • 游戏脚本设计基础教程
    编程学习网:类游戏编程,特别是RPG脚本起着驱动整个游戏进程的作用。事件的运作建立在脚本的基础上,而脚本的设计建立在引擎的基础上,所以设计脚本之前因该想一想引擎,好的脚本对剧情的描述具有简单、准确的性质。
    游戏脚本设计基础教程
  • 成为游戏设计师必须具备的条件
    编程学习网:很多人喜欢玩游戏,他们总在不停感叹:这款游戏实在太精彩了,我要是能设计出这样的游戏就!而在他们行动之前,还有许多需要解答的问题罗列在前,我要如何成为游戏设计师?我需要具备哪些技能和素质?游戏设计师究竟在做什么工作?那么,游戏设计师有哪些任务呢?我们将讨论这个问题。
    成为游戏设计师必须具备的条件
  • 贪食蛇小游戏开发设计基础教程
    编程学习网: 贪吃蛇是家喻户晓的益智类小游戏,大家小时候应该都有玩过,编程学习网这里就不多介绍了,本教程将教你用MicrosoftVisualC++来制作它。
    贪食蛇小游戏开发设计基础教程
  • 成为优秀的Cocos2D程序员需要的十个品质
    编程学习网:相信各位同学都在为了成为优秀的Cocos2D程序员而奋斗着,那么一个优秀的Cocos2D程序员需要具备什么品质呢?本篇教程为你揭秘成为一个优秀的Cocos2D程序员所需要的十个品质。
    成为优秀的Cocos2D程序员需要的十个品质
  • Unity3d脚本基础
    最好用游戏引擎开发游戏,推荐Unity3D引擎,该引擎学习更简单,更易上手。游戏引擎可以编辑你的游戏场景、角色和游戏需要的东西。还有,学习编写脚本。编写脚本实际上就是编程。例如,你要在游戏中按键盘方向键来控制角色行走,这必须通过编写脚本。因此,脚本是游戏的逻辑。小编推荐你去学C编程,Unity3D通常是用C语言编写的。
    Unity3d脚本基础
  • 实例教程解析制作flash小游戏
    编程学习网:flash小游戏在游戏开发中是属于比较简单的类型,本例为FlashAS3.0实例教程,介绍射击类游戏的制作,主要分游戏界面的制作和类的编写两部分,从简单的开始做起,跟着教程动手做做看吧。
    实例教程解析制作flash小游戏

目录