Cocos2d-x 3D模型渲染
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中对这一块的代码设计的很糟糕,原因有以下几点:
- pass绘制的顶点缓存和索引缓存来至VertexAttribBinding,所以绘制原本应该使用VertexAttribBinding:: _glProgramState,但实际上使用的是Pass::_glProgramState。
- Pass::_glProgramState和VertexAttribBinding:: _glProgramState都是指针,他们指向的是同一个对象,这个一致是因为Mesh::setMaterial中的以下代码:
auto vertexAttribBinding = VertexAttribBinding::create(_meshIndexData, pass->getGLProgramState());
pass->setVertexAttribBinding(vertexAttribBinding);
这种方式并不能完全保证Pass::_glProgramState和VertexAttribBinding:: _glProgramState完全都是指向同一个对象。 - 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绘制步骤
- 创建顶点缓存和索引缓存,可使用模型加载的MeshDatas数据创建
- 创建Material对象,可以使用Sprite3DMaterial::createBuiltInMaterial函数创建
- 使用索引缓存创建VertexAttribBinding,并设置Material:: _techniques:: _passes的_vertexAttribBinding、
- 使用Material对象初始MeshCommand绘制命令
- 设置Material:: _techniques:: _passes中_glProgramState的Uniform值。
- 绘制出图形。
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模型渲染相关推荐
- html3d模型渲染,【SVG】纯clip-path打造的3D模型渲染器
几天之前, 一个species-in-pieces的网站把我震到了(如下图), 出于一个优秀前端的敏锐嗅觉和原始本能, 我立刻祭出了看家法宝--Chrome开发者工具开始偷窥这个网站. 简单推敲之后, ...
- XNA:2D图元与3D模型共存时的渲染问题
本文为Rumon863原创翻译,此处为收藏,转载请按如下方式显式标明原创作者以及文章出处,以示尊重!! 翻译:Rumon863 文章出处:http://blog.csdn.net/rumon863/a ...
- 【Unity】3D模型或粒子渲染在UI上层
方法有很多,我只介绍一种我认为最简单有效的方法,用非常取巧的手段,使用RenderTexture + Camera 在ScrollView里展示3D模型. 效果: 1.首先创建一个RenderText ...
- 用最少的代码渲染3D模型
Github:https://github.com/xosg/model-view Model View 基于 Zero Overhead 原则的草量级 3D 模型渲染组件,在线演示:https:// ...
- uniapp小程序展示3D模型
小程序展示3D模型-使用three.js 进行渲染 在开发的期间查阅了大量的资料.案例,大多都是无稽之谈-经过摸索-终于开发出来了适合本项目的3D模型案例 为了帮助有需要的同学 少走弯路 特地记录了一 ...
- Cocos技术派 | 3d人物渲染详细教程
文章目录 前言 3d系统基础 FBX模型导入 配置模型参数 相机分组 2D相机设置 添加UI节点 添加3D节点 设置灯光 设置平台接收阴影 设置3D相机 3D场景编辑器 设置模型材质 设置模型产生阴影 ...
- Unity之在UI界面上显示3D模型
1.创建一个3D摄像机,渲染3D模型.(我为了方便就把模型放到了Camera的下面,你可以不这样) 2.在2D里面指定一个TopLeft和BottomRight,用来表示模型渲染到UI上面的区域. 3 ...
- 3D API,快速展示模型,实现3D模型在线可视化展示,还能进行各种测量视图等操作......
如果你是一名从事3D建模相关的代码开发者,是否经常会遇到以下问题: 渲染效果不及预期 模型展示不够直观 测量视图等操作难以实现 ..... 由此带来的结局大多是: 造成客户对模型理解不清晰,增加双方的 ...
- PCB 3D模型与渲染
如何制作一张印刷电路板(PCB)的3D渲染效果图? - 况琪的回答 - 知乎 不错 从AltiumDesigner导出电路完美3D模型至Solidworks的方法 ...
最新文章
- sqlserver中将行数据转为Xml文件格式
- CxImage的使用及基本用法
- python正则表达式——regex模块
- E. 存储过程(procedure)
- QT-Linux开发环境的搭建
- antd如何获取表单的值_JavaScript多个表单序列化获取值
- java calendar 时分秒_Java中Calendar类的常用方法(对时间进行计算的类)
- Grunt的配置及使用(压缩合并js/css)
- 日语输入法电脑版_哪个日语输入法比较好用,日语输入法下载及使用教程
- C# 缓存学习第一天
- 【外文文献检索与下载方法】
- token干什么用_什么是TOKEN?Token小号的理解运用,拼多多,知乎,快手,抖音的Token是什么意思...
- 来料加工企业使用ERP系统作用有哪些
- 简单的python爬虫爬豆瓣图书TOP250
- 《Data Algorithm》读书笔记七 — 购物篮分析
- 解决 cp: omitting directory ‘./dist’ ( 拷贝失败 )
- Qt之实现图片轮播效果
- ChatGPT大封号,注册功能关闭!亚洲成重灾区,网友喊话:不要登录,不要登录...
- Linux命令-samba服务器和防火墙
- Java LTS版本——Java 11新特性