Cocos2d-x 3D模型渲染

声明:本文使用的是cocos2d-x-3.17的代码

文章中的提到的测试代码下载地址https://gitee.com/Kyle12/Cocos2dRenderStudy

3D模型

Cocos2d支持渲染*.fbx和*.obj两种3D模型,*.obj模型可以直接渲染,*.fbx模型需要先使用官方的转换程序fbx-conv.exe转换成*.c3t或者*.c3b类型才能渲染。*.c3t是Json格式的文本文件可以使用记事本打开,*.c3b是二进制文件更省空间,处理速度也更快。这几种文件中只有*.c3t文件是文本文件,这里结合*.c3t文件分析一下3D模型。

以下是一个*.c3t文件,这里下载,可以先把文件的扩展名改成json,用notepad++打开:

文件由version、id、meshes、materials、nodes、animations组成。Meshes包含了顶点数据索引数据,materials包含了绘制使用的纹理,nodes包含了模型各个模块和骨架 ,animations是骨骼动画。Animations另外有文章讨论,先看meshes、materials、nodes。

对于一个3D模型可以包含很多模块,如下图是win10上“画图3D”程序制作3D模型,(注意“画图3D”程序生成的*.fbx模型,使用fbx-conv.exe程序转换时有bug,转换后的模型有时显示不正常

这里面有3个模块,这3个模块构成了一个大的3D模型,每个模块我们可以调整位置和大小,为了更加方便的控制,每个模块都可以有自己的局部坐标系,模型会有一个大的全局坐标系。

Meshes

Meshes包含了模型的所有顶点数据vertices,顶点数据的属性(attributes)和各个模块(parts)的索引。以上面的3D绘图为例,vertices中会包含3个模块所有的点,parts中包含3组索引,代表三个模块。

materials

materials包含了模型中用到的纹理,只记录了纹理的文件名。这里面也会包含物体的光照材质、反光率、透明度等等,但Cocos2d-x 3.17中并没有使用这些参数。

nodes

nodes记录了整个模型的结构,如果使用了骨骼也会包含骨骼信息。以上面的3D绘图为例,首先有一个node节点,代表整个大的模型,它会有3个子node,代表3个模块。每个node里面都会有一个4*4矩阵用于坐标系变换

模型加载

模型的加载需要用到Bundle3D类,*.c3t文件加载后meshes对应meshdatas对象,materials对应MaterialDatas对象、nodes对应NodeDatas对象、animations对应Animation3Ddata对象,Animation3Ddata另外有文章讨论。另外三个对象数据结构如下:

其他类型的3D模型加载后也对应的是这三个对象,加载函数为Sprite3D::loadFromFile。

Mesh初始化

对于3D模型的绘制使用的是Sprite3D对象,Sprite3D对象包含了多个Mesh对象,最终模型的绘制由Mesh对象完成。加载后的模型数据meshdatas、MaterialDatas、NodeDatas可以用来创建Mesh对象,Mesh对象实现了3D模型的绘制。模型中每个模块需要创建一个Mesh,即NodeData中的每个modelNodeData都会创建一个Mesh。

顶点缓存和索引缓存

首先需要根据meshdatas创建顶点缓存MeshVertexData和索引缓存MeshIndexData,顶点缓存MeshVertexData中会包含多个索引缓存MeshIndexData,结构如下:

Mesh结构

Mesh中有一个对象Mesh:: _material对象中这个对象并不是MaterialDatas初始而来。MaterialDatas中只存储了纹理,也就是Mesh:: _textures。Mesh:: _material是一个Material类包含了所有mesh绘制的数据。

Material类内部包含了Technique数组,Technique中包含Pass数组,Pass里面有VertexAttribBinding,真正存储绘制数据的是VertexAttribBinding。VertexAttribBinding包含了绘制用的着色器程序GLProgramState,顶点索引缓存MeshVertexData。

Material类设计的很乱,很难从Technique类和Pass类看出类设计的目的,原本以为这两者是为了实现3D模型“换装”用的,但官方给的“换装”例子并没有使用这两个类实现。

模型绘制

3D模型绘制使用的是MeshCommand命令,MeshCommand命令可以直接使用顶点缓存索引缓存绘制,也可以使用Material类绘制。直接使用缓存绘制的方法请参考“Cocos2d-x 渲染器Renderer——MeshCommand”,这里主要是使用Material类绘制。

Material绘制3D模型

Material类包含了所有mesh绘制要使用的数据,绘制的时候只需一个Material对象就可以绘制,以下是MeshCommand中使用Material绘制的代码:

for(const auto& pass: _material->_currentTechnique->_passes)

{

pass->bind(_mv);

glDrawElements(_primitive, (GLsizei)_indexCount, _indexFormat, 0);

CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, _indexCount);

pass->unbind();

}

Material类中有多个Technique,绘制时只会使用_currentTechnique,会对_currentTechnique中所有的pass类都执行一次绘制。

从代码中可以看出Cocos2d希望每个3D模块绘制都由一个Material对象控制,因为获取Material对象的函数Sprite3DMaterial::createBuiltInMaterial返回值是缓存Material对象的克隆值。Material对象克隆时会克隆所有的Technique对象,pass对象,pass对象中的的_glProgramState也会被克隆,这也可以说明pass对象中的的_glProgramState不是共享的,是对象单独所有的。pass->bind(_mv)使用的Pass::_glProgramState,而不是VertexAttribBinding::_glProgramState。

因为pass对象中的的_glProgramState是单独所有的,所以我们可以在生成MeshCommand命令时设置pass的_glProgramState中的uniform值,不会影响其他对象的绘制。

实际代码中Cocos2d-x 3.17中对这一块的代码设计的很糟糕,原因有以下几点:

  1. pass绘制的顶点缓存和索引缓存来至VertexAttribBinding,所以绘制原本应该使用VertexAttribBinding:: _glProgramState,但实际上使用的是Pass::_glProgramState
  2. Pass::_glProgramStateVertexAttribBinding:: _glProgramState都是指针,他们指向的是同一个对象,这个一致是因为Mesh::setMaterial中的以下代码:
    auto vertexAttribBinding = VertexAttribBinding::create(_meshIndexData, pass->getGLProgramState());
    pass->setVertexAttribBinding(vertexAttribBinding);

    这种方式并不能完全保证Pass::_glProgramStateVertexAttribBinding:: _glProgramState完全都是指向同一个对象。
  3. Pass::_glProgramState是可以通过函数Pass::setGLProgramState随意设置的,而设置的_glProgramState并不能保证没有被共享。毕竟程序中还专门设计了GLProgramStateCache类共享GLProgramState

系统预创建Material类型

Cocos2d-x定义了以下几种Material类型,由枚举MaterialType表示:
NLIT:不使用光照,使用纹理
UNLIT_NOTEX:不使用光照,也不使用纹理
DIFFUS:使用光照和纹理
DIFFUSE_NOTEX:使用光照,不使用纹理
BUMPED_DIFFUSE:使用光照,纹理,和凹凸纹理
CUSTOM:自定义类型
其中UNLIT、DIFFUSE、BUMPED_DIFFUSE还分是否使用蒙皮骨骼。

函数Sprite3DMaterial* Sprite3DMaterial::createBuiltInMaterial(MaterialType type, bool skinned)可以获取Material,type为Material类型,skinned为是否使用骨骼。

Material绘制步骤

  1. 创建顶点缓存和索引缓存,可使用模型加载的MeshDatas数据创建
  2. 创建Material对象,可以使用Sprite3DMaterial::createBuiltInMaterial函数创建
  3. 使用索引缓存创建VertexAttribBinding,并设置Material:: _techniques:: _passes的_vertexAttribBinding、
  4. 使用Material对象初始MeshCommand绘制命令
  5. 设置Material:: _techniques:: _passes中_glProgramState的Uniform值。
  6. 绘制出图形。

Material绘制完整例子:DrawModelScene.cpp/DrawModelScene.h,对应程序菜单“Test Draw 3D”->“Draw Model”

光照

以下是有光照和无光照对比

光照作用是使得3D物体看上去更加立体,光照也可以通过光的颜色改变物体颜色。

Cocos2d-x支持四种光源,四种光源以及对应的类是,环境光AmbientLight、方向光DirectionLight、点光源PointLight、聚光源SpotLight。

使用光照也很简单,光源类继承了Node类,只需要创建相应的光源对象,然后用AddChild加入到场景树的任意节点,绘制Sprite3D精灵时会自动使用场景树的节点。

执行的顺序如下图:

完整例子: SpriteLightScene.cpp/SpriteLightScene.h,对应程序菜单“Test Draw 3D”->“Sprite3D with Light”


动画部分的讲解:Cocos2d骨骼动画

Cocos2d-x 3D模型渲染相关推荐

  1. html3d模型渲染,【SVG】纯clip-path打造的3D模型渲染器

    几天之前, 一个species-in-pieces的网站把我震到了(如下图), 出于一个优秀前端的敏锐嗅觉和原始本能, 我立刻祭出了看家法宝--Chrome开发者工具开始偷窥这个网站. 简单推敲之后, ...

  2. XNA:2D图元与3D模型共存时的渲染问题

    本文为Rumon863原创翻译,此处为收藏,转载请按如下方式显式标明原创作者以及文章出处,以示尊重!! 翻译:Rumon863 文章出处:http://blog.csdn.net/rumon863/a ...

  3. 【Unity】3D模型或粒子渲染在UI上层

    方法有很多,我只介绍一种我认为最简单有效的方法,用非常取巧的手段,使用RenderTexture + Camera 在ScrollView里展示3D模型. 效果: 1.首先创建一个RenderText ...

  4. 用最少的代码渲染3D模型

    Github:https://github.com/xosg/model-view Model View 基于 Zero Overhead 原则的草量级 3D 模型渲染组件,在线演示:https:// ...

  5. uniapp小程序展示3D模型

    小程序展示3D模型-使用three.js 进行渲染 在开发的期间查阅了大量的资料.案例,大多都是无稽之谈-经过摸索-终于开发出来了适合本项目的3D模型案例 为了帮助有需要的同学 少走弯路 特地记录了一 ...

  6. Cocos技术派 | 3d人物渲染详细教程

    文章目录 前言 3d系统基础 FBX模型导入 配置模型参数 相机分组 2D相机设置 添加UI节点 添加3D节点 设置灯光 设置平台接收阴影 设置3D相机 3D场景编辑器 设置模型材质 设置模型产生阴影 ...

  7. Unity之在UI界面上显示3D模型

    1.创建一个3D摄像机,渲染3D模型.(我为了方便就把模型放到了Camera的下面,你可以不这样) 2.在2D里面指定一个TopLeft和BottomRight,用来表示模型渲染到UI上面的区域. 3 ...

  8. 3D API,快速展示模型,实现3D模型在线可视化展示,还能进行各种测量视图等操作......

    如果你是一名从事3D建模相关的代码开发者,是否经常会遇到以下问题: 渲染效果不及预期 模型展示不够直观 测量视图等操作难以实现 ..... 由此带来的结局大多是: 造成客户对模型理解不清晰,增加双方的 ...

  9. PCB 3D模型与渲染

    如何制作一张印刷电路板(PCB)的3D渲染效果图? - 况琪的回答 - 知乎             不错 从AltiumDesigner导出电路完美3D模型至Solidworks的方法        ...

最新文章

  1. sqlserver中将行数据转为Xml文件格式
  2. CxImage的使用及基本用法
  3. python正则表达式——regex模块
  4. E. 存储过程(procedure)
  5. QT-Linux开发环境的搭建
  6. antd如何获取表单的值_JavaScript多个表单序列化获取值
  7. java calendar 时分秒_Java中Calendar类的常用方法(对时间进行计算的类)
  8. Grunt的配置及使用(压缩合并js/css)
  9. 日语输入法电脑版_哪个日语输入法比较好用,日语输入法下载及使用教程
  10. C# 缓存学习第一天
  11. 【外文文献检索与下载方法】
  12. token干什么用_什么是TOKEN?Token小号的理解运用,拼多多,知乎,快手,抖音的Token是什么意思...
  13. 来料加工企业使用ERP系统作用有哪些
  14. 简单的python爬虫爬豆瓣图书TOP250
  15. 《Data Algorithm》读书笔记七 — 购物篮分析
  16. 解决 cp: omitting directory ‘./dist’ ( 拷贝失败 )
  17. Qt之实现图片轮播效果
  18. ChatGPT大封号,注册功能关闭!亚洲成重灾区,网友喊话:不要登录,不要登录...
  19. Linux命令-samba服务器和防火墙
  20. Java LTS版本——Java 11新特性

热门文章

  1. Glide加载圆角图片不显示问题
  2. CUMT2022算法设计与分析A上机考试
  3. 男朋友转行 Java 失败,找不到工作
  4. 最近成了叨客 - 注册了饭否,叽歪网
  5. 《在路上 …》 42区介绍演讲- 在家的排练的MP3
  6. Silverlight技术是什么
  7. 搭建简单的Netty开发环境
  8. 你知道小黑鱼APP是干嘛的吗?
  9. 儿童磁铁玩具,磁性积木片CPC认证,ASTM F963、CPSIA测试
  10. 网页三剑客:HTML+CSS+JavaScript 之CSS概述