内容简介:Cocos2d-x 3.x 图形学渲染系列二十五
笔者介绍: 姜雪伟 , IT 公司技术合伙人, IT 高级讲师, CSDN 社区专家,特邀编辑,畅销书作者,国家专利发明人 ; 已出版书籍:《手把手教你 架构 3D 游戏引擎》电子工业出版社 和《 Unity3D 实战核心技术详解》电子工业出版社等。
CSDN视频网址: http://edu.csdn.net/lecturer/144
大年三十,还是忍不住,再写一篇文章吧,作为在猴年的最后一篇文章。。。。
游戏中的角色包括玩家、NPC、怪物等都具有骨骼动画,对于成熟的引擎也是需要支持骨骼动画的,骨骼动画是在模型的基础上经过max工具调试出来的,在程序中只需要提供播放骨骼动画的接口即可,掌握骨骼动画的原理对于评判一个 程序员 是否精通引擎的必要条件,也是一个重要的评判指标。本章重点给读者介绍Cocos2d-x的骨骼动画,虽然该引擎对骨骼动画的处理不是很成熟,但简单的骨骼动画还是可以实现的。本章会把Cocos2d-x引擎中关于动画的处理的缺点和优点都会给读者分享。
骨骼动画是通过蒙皮实现的,Cocos2d-x引擎有自己的demo实现,如果开发者掌握骨骼动画,必须自己要尝试着做一下。Cocos2d-x引擎还是有很多坑的,尤其是在播放动画方面。后面会给读者介绍到,下面就以自己做的带有骨骼动画的宇航员模型为例给读者介绍。因为c3t是json文件格式,相对读者能够看到其文件内容,更有助于读者理解。在这里只截取了骨骼动画数据展示如下所示:
"animations": [ { "id": "Take 001", "length": 0.666000, "bones": [ { "boneId": "Box002", "keyframes": [ { "keytime": 0.000000, "rotation": [-0.707107, -0.000000, 0.000000, 0.707107], "scale": [ 1.000000, 1.000000, 1.000000], "translation": [ 0.000000, 36.417324, -74.842522] }, { "keytime": 0.050050, "rotation": [-0.696203, -0.000000, 0.000000, 0.717845] }, { "keytime": 0.100100, "rotation": [-0.665764, -0.000000, 0.000000, 0.746162] }, { "keytime": 0.150150, "rotation": [-0.617108, -0.000000, 0.000000, 0.786878] }, { "keytime": 0.200200, "rotation": [-0.556456, -0.000000, 0.000000, 0.830877] }, { "keytime": 0.250250, "rotation": [-0.486915, -0.000000, 0.000000, 0.873449] }, { "keytime": 0.300300, "rotation": [-0.414019, -0.000000, 0.000000, 0.910268] }, { "keytime": 0.350350, "rotation": [-0.342342, -0.000000, 0.000000, 0.939575] }, { "keytime": 0.400400, "rotation": [-0.283450, -0.000000, 0.000000, 0.958987] }, { "keytime": 0.450450, "rotation": [-0.241560, -0.000000, 0.000000, 0.970386] }, { "keytime": 0.500501, "rotation": [-0.226392, -0.000000, 0.000000, 0.974036] }, { "keytime": 0.550551, "rotation": [-0.240981, -0.000000, 0.000000, 0.970530] }, { "keytime": 0.600601, "rotation": [-0.282420, -0.000000, 0.000000, 0.959291] }, { "keytime": 0.650651, "rotation": [-0.341023, -0.000000, 0.000000, 0.940055] }, { "keytime": 0.700701, "rotation": [-0.410364, -0.000000, 0.000000, 0.911922] }, { "keytime": 0.750751, "rotation": [-0.485455, -0.000000, 0.000000, 0.874261] }, { "keytime": 0.800801, "rotation": [-0.555123, -0.000000, 0.000000, 0.831769] }, { "keytime": 0.850851, "rotation": [-0.616001, -0.000000, 0.000000, 0.787745] }, { "keytime": 0.900901, "rotation": [-0.664969, -0.000000, 0.000000, 0.746871] }, { "keytime": 0.950951, "rotation": [-0.695771, -0.000000, 0.000000, 0.718264] }, { "keytime": 1.000000, "rotation": [-0.707102, -0.000000, 0.000000, 0.707111], "scale": [ 1.000000, 1.000000, 1.000000], "translation": [ 0.000000, 36.417324, -74.842522] } ] } ] } ]
在上述文件中id 表示的是骨骼动画的名字,bone表示的是动作,在bone项里面包含keytime帧动画时间,rotation旋转的角度,scale缩放大小,translation表示的骨骼转换的起始位置和最终转换的位置。程序的作用就是加载读取该模型文件,程序加载模型文件,需要在程序中定义相应的结构体,定义结构体主要是用于存放模型信息对应项,Cocos2d-x引擎已定义的结构体给介绍如下:
//模型顶点属性 struct MeshVertexAttrib { //描述该属性所需要的元素个数,比如描述一个Vec3的位置信息,需要x、y、z 3个变量 GLint size; //元素的类型如GL_FLOAT GLenum type; //描述该属性类型的值,使用的值是GLProgram类的枚举,例如 GLProgram::VERTEX_ATTRIB_POSITION int vertexAttrib; //存储该属性所需要的字节数,等于size * sizeof(type) int attribSizeBytes; }; 再介绍一下模型蒙皮数据结构体如下所示: struct MeshData { //模型的顶点索引数组 typedefstd::vector<unsignedshort> IndexArray; //模型的顶点数组 std::vector<float> vertex; //模型的顶点数量 int vertexSizeInFloat; //子模型的索引 std::vector<IndexArray> subMeshIndices; //子模型的名字ID std::vector<std::string> subMeshIds; //子网格名字 (从版本3.3开始) //模型的碰撞盒 std::vector<AABB> subMeshAABB; int numIndex; //模型的顶点属性信息 std::vector<MeshVertexAttrib> attribs; int attribCount; } 以c3t文件为例,先介绍模型相关文件属性: "version": "0.7", "id": "", "meshes": [ { "attributes": [{ "size": 3, "type": "GL_FLOAT", "attribute": "VERTEX_ATTRIB_POSITION" }, { "size": 3, "type": "GL_FLOAT", "attribute": "VERTEX_ATTRIB_NORMAL" }, { "size": 3, "type": "GL_FLOAT", "attribute": "VERTEX_ATTRIB_TANGENT" }, { "size": 3, "type": "GL_FLOAT", "attribute": "VERTEX_ATTRIB_BINORMAL" }, { "size": 2, "type": "GL_FLOAT", "attribute": "VERTEX_ATTRIB_TEX_COORD" }],
attribs 属性数组分别代表:
(1)顶点在模型坐标系下的位置信息(VERTEX_ATTRIB_POSITION)
(2)顶点的法线(VERTEX_ATTRIB_NORMAL)
(3)顶点的纹理坐标(VERTEX_ATTRIB_TEX_COOD)
(4)作用于该顶点的某骨骼,对该顶点最终位置的权重(VERTEX_ATTRIB_BLEND_WEIGHT)
(5)影响该顶点的骨骼在骨骼数组中的索引(VERTEX_ATTRIB_BLEND_INDEX)
接下来介绍模型的蒙皮骨骼信息,引擎提供了结构体如下所示:
struct SkinData { //影响到模型蒙皮的骨骼名字数组,skinBone的数组 std::vector<std::string> skinBoneNames; //未影响到模型蒙皮的骨骼名字数组,nodeBone的数组 std::vector<std::string> nodeBoneNames; //从对应的skinBone坐标系到模型坐标系变换的逆变换,可实现将该骨骼影响的蒙 皮顶点从模型坐标系的坐标,转换至骨骼坐标系的坐标 std::vector<Mat4> inverseBindPoseMatrices; //skinBone到其父骨骼坐标的初始矩阵 std::vector<Mat4> skinBoneOriginMatrices; //nodeBone到其父骨骼坐标的初始矩阵 std::vector<Mat4> nodeBoneOriginMatrices; //所有骨骼与其子骨骼索引的map,值得说明的是这个索引是对skinBoneNames 和nodeBoneNames两个数组而言的。 std::map<int, std::vector<int>> boneChild; //根骨骼索引,同样是相对两个数组而言的 introotBoneIndex; } //材质数据结构体 struct MaterialData { std::map<int, std::string> texturePaths; //子网格id和纹理路径 void resetData() { texturePaths.clear(); } };
骨骼动画的运动是通过矩阵之间的变换实现的,在Bone3D
类中实现了骨骼矩阵的更新,函数如下所示:
void Bone3D::updateJointMatrix(Vec4* matrixPalette) { { static Mat4 t; //得到需要的矩阵 Mat4::multiply(_world, getInverseBindPose(), &t); //将矩阵最后一行去掉,得到4 * 3的Vec4向量数组 matrixPalette[0].set(t.m[0], t.m[4], t.m[8], t.m[12]); matrixPalette[1].set(t.m[1], t.m[5], t.m[9], t.m[13]); matrixPalette[2].set(t.m[2], t.m[6], t.m[10], t.m[14]); } }
updateJointMatrix函数主要是告诉读者,模型的骨骼在运动时都是通过矩阵运算得到的,骨骼数量越多,它们消耗CPU越多,因此为了优化骨骼运算,现在的处理方式都是将运算移到GPU中计算。
下面构建渲染指令,传递矩阵数据变换到GPU中进行计算,把骨骼动画处理的Shader文件给读者展示如下:
vec4 getPosition() { //对该顶点产生作用的第一块骨骼所占比重 float blendWeight = a_blendWeight[0]; int matrixIndex = int (a_blendIndex[0]) * 3; //对传递进来的matrixPalette矩阵乘以骨骼的权重 vec4 matrixPalette1 = u_matrixPalette[matrixIndex] * blendWeight; vec4 matrixPalette2 = u_matrixPalette[matrixIndex + 1] * blendWeight; vec4 matrixPalette3 = u_matrixPalette[matrixIndex + 2] * blendWeight; blendWeight = a_blendWeight[1]; if (blendWeight >0.0) { //若还有别的骨骼对该顶点产生影响,则进行混合 matrixIndex = int(a_blendIndex[1]) * 3; matrixPalette1 += u_matrixPalette[matrixIndex] * blendWeight; matrixPalette2 += u_matrixPalette[matrixIndex + 1] * blendWeight; matrixPalette3 += u_matrixPalette[matrixIndex + 2] * blendWeight; blendWeight = a_blendWeight[2]; if (blendWeight >0.0) { matrixIndex = int(a_blendIndex[2]) * 3; matrixPalette1 += u_matrixPalette[matrixIndex] * blendWeight; matrixPalette2 += u_matrixPalette[matrixIndex + 1] * blendWeight; matrixPalette3 += u_matrixPalette[matrixIndex + 2] * blendWeight; blendWeight = a_blendWeight[3]; if (blendWeight >0.0) { matrixIndex = int(a_blendIndex[3]) * 3; matrixPalette1 += u_matrixPalette[matrixIndex] * blendWeight; matrixPalette2 += u_matrixPalette[matrixIndex + 1] * blendWeight; matrixPalette3 += u_matrixPalette[matrixIndex + 2] * blendWeight; } } } vec4 _skinnedPosition; vec4 postion = vec4(a_position, 1.0); //使用这个混合后的矩阵,对顶点进行变换,得到该顶点在模型坐标系下的坐标 _skinnedPosition.x = dot(postion, matrixPalette1); _skinnedPosition.y = dot(postion, matrixPalette2); _skinnedPosition.z = dot(postion, matrixPalette3); _skinnedPosition.w = postion.w; return _skinnedPosition; } void main() { //得到顶点在模型坐标系下的坐标 vec4 position = getPosition(); //使用MVP矩阵进行变换的到最终坐标 gl_Position = CC_MVPMatrix * position; TextureCoordOut = a_texCoord; TextureCoordOut.y = 1.0 - TextureCoordOut.y; }
骨骼动画在GPU中根据骨骼的权重进行矩阵运算,这样也可以保证骨骼动画与动画之间的过渡是平滑的,保证了不同的骨骼动画之间的切换是顺畅的。在美术制作模型时,要按照一定的规则制作,从而保证导出的模型可以在Cocos2d-x引擎中加载,接下来给读者介绍关于模型的制作。
以上所述就是小编给大家介绍的《Cocos2d-x 3.x 图形学渲染系列二十五》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- CubeEngine v0.3B 更新,图形渲染引擎
- TensorFlow也可以做图形渲染了:当神经网络遇上计算机图形学
- Basemark推出Rocksolid图形渲染解决方案
- Ogre 1.12.3 发布,三维图形渲染引擎
- Rust 移动端跨平台复杂图形渲染项目开发系列总结(目录)
- Cocos2d-x 3.x 图形学渲染系列总结
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。