前言 cocos2d-x 中相关部分代码介绍 背景知识介绍

参考http://www.3dkingdoms.com/weekly/weekly.php?a=4

  1. 简单3d 模型支持

    第一步实现对3d 模型的简单支持,完成一个CCSprite3D 类
    参考CCSprite 类 以及 CCGLProgram 代码 主要修改 draw 方法。
    添加了定点数组pos 绘制索引数组index. 以及若干 3d 变换相关的成员方法 平移 旋转 缩放。
    对draw方法的改造,首先增加对opengl 矩阵进行计算的stdTransform, 将3d空间中的变换 矩阵乘到 MV 矩阵上面。
    注意cocos2d 使用了两种投影方式,一种是 平行投影,一种是3d 透视投影,默认使用的3d 投影, 这种方式下MV矩阵也被修改,将坐标原点移动到了屏幕的右下角。
    3d绘制需要 开启depth_test 深度测试。
    之后只需要将顶点数组传入vertexAttribPointer 中 将索引数组传给glDrawElements 中即可.
    测试时可以手动写一个 正方体的 顶点数组数据。
    注意要对模型进行一定的缩放,否则屏幕上面会看不到。
    使用 画 line 以及 画顶点 方式 绘制 可以用来调试。

  2. 3d 模型 基本变换支持

    http://en.wikipedia.org/wiki/Transformation_matrix
    cocos2dx 中使用kazmath 这个数学库,这个库中的矩阵kmMat4使用列优先的方式存储,即0 1 2 3 存储的是 矩阵第一列数据。
    对应平移变换只需要修改 最后一列的 前3行数据即可,分别对应x y z 平移。
    http://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
    对于旋转,一般使用四元式进行计算,因此对于旋转可以先按照四元式进行计算,最后转化成一个矩阵即可。
    四元式 通过对旋转轴和相对于轴的旋转角度来表示(通过一些sin cos计算),两个四元式之间的插值使用圆插值(slerp).
    基本的 x y z 轴的旋转矩阵 可以参考2维度旋转矩阵的构造方法。
    http://onlinemca.com/mca_course/kurukshetra_university/semester5/computergraphics/three_d_transformation.php
    对于缩放直接修改矩阵主对角线上面的值即可。
    下面这些矩阵对顶点的计算 都是按照 行优先矩阵 左乘以 顶点列向量, 相当于列优先矩阵 右乘以 顶点行向量 来计算结果的。在给opengl 传入矩阵的时候 是以 列优先的方式传入数据的。 http://www.opengl.org/archives/resources/faq/technical/transformations.htm
    在kazmath 这个库里面 矩阵是以列优先方式存储,矩阵相乘的时候第一个矩阵的第一行 乘以 第二个矩阵的第一列 得到第一个元素,即是第一个矩阵左乘第二个矩阵。
    http://www.mindcontrol.org/~hplus/graphics/matrix-layout.html
    貌似在direct3d中传入的矩阵数据是按照行优先的顺序的,因此在direct中如果要做两个矩阵的乘法,第一个矩阵左乘第二个矩阵,则选取的矩阵元素和opengl是不同的。
    当然这里使用的左乘 是数学上面的左乘的概念,即用一个矩阵的 第一行 乘以 第二个矩阵的第一列 得到第一个元素。
    注意矩阵乘法是不可以交换了,因此对于这三种变换,不同的乘顺序会产生不同的结果。举例如下:
    旋转矩阵 左乘 平移矩阵,则旋转矩阵中的值将会影响到平移的值,而平移的数值不会影响结果中的旋转部分的值,结果就是在世界空间中 对象是旋转一定角度之后,沿着这个角度进行平移的。
    如果平移矩阵 左乘 旋转矩阵, 平移的值不会影响结果中的旋转部分, 旋转也不会影响平移。结果就是物体现在本地旋转,但是之后 还是沿着世界坐标中原来的轴进行平移。
    先考虑 缩放矩阵 左乘 旋转矩阵 结果就是 相当于在原来的旋转矩阵上面 同一行 乘以一个相同的系数, 这个矩阵在作用到顶点的时候 这个 系数可以提取出来,则相当于 对顶点先进行了旋转 接着 还是按照原来的 世界坐标的方向 进行缩放, 这样得到的是一个 切拉伸的效果,这样物体会变形。
    那么 旋转矩阵 左乘 缩放 矩阵 得到的结果就和上面不同,因此应该是物体旋转之后,相应的缩放轴也跟着旋转了,因此缩放 不是沿着世界轴进行的,而是沿着物体本身的本地坐标轴进行的,这样物体不会边形。
    接着考虑缩放左乘 平移矩阵 , 结果中的缩放部分没有变化,但是平移部分会被改变,结果就是 物体还是按照原来的比例缩放,但是 新的平移位移的时候相对于 原来的平移位移会产生一定比例的缩放。
    如果是 平移 左乘 缩放矩阵 结果中平移部分 和 缩放部分都不会发生改变, 结果就是 物体在世界坐标空间中 按照原来的比例 缩放 , 接着平移正常的位移。
    可以总结出来 平移 左乘 另外的矩阵 不过影响原来矩阵的结果, 而别的两种矩阵左乘平移矩阵 会影响平移的结果。缩放 左乘 旋转 会产生物体的边形, 而旋转左乘缩放 会保持物体的形状和单纯的进行缩放的物体形状是一致的。
    因此如果你的目标是保证世界空间中平移不变则用平移矩阵左乘其它矩阵,如果是要保证物体的缩放形状不变则 用 旋转矩阵左乘 缩放矩阵。
    如果要保持这两个不变性的话 矩阵乘法的顺序就是 平移 * 旋转 * 缩放
    http://gamedev.stackexchange.com/questions/16719/what-is-the-correct-order-to-multiply-scale-rotation-and-translation-matrices-f
    当然要根据自身想要的结果来调整矩阵的乘法顺序,并且可以通过增加父子变换的关系来控制矩阵乘法顺序。
    一般情况是 父亲矩阵 左乘于 孩子矩阵 则结果就是 父亲在世界空间的平移 得到保持, 父亲缩放会导致旋转的孩子发生变形,父亲的旋转 会影响孩子自身的平移方向。
    按照专业的说法叫做 孩子的变换矩阵作用在孩子节点自身的局部空间 而 父亲矩阵 会对孩子局部空间进行变换。

  3. 骨骼动画
    本质上骨骼只是一个矩阵变换,如果要查看一个骨骼则观察其对应的矩阵即可。但是这种方式非常不直观,如何通过图像直观表现这种矩阵变换呢?
    这里首先要明确表现和本质之间的区别,矩阵变换及其对应的3d骨骼对象不能等价。骨骼的作用只是施展一种变换到一个对象身上,至于这个对象当前的状态是什么并不关心,因此需要手动的对对象设定一个初始化的状态,而后续的状态通过这种骨骼矩阵变换来生成。
    因此问题分成两个部分,如何描述这种初始化状态,如何描述这个矩阵变换。
    骨骼分成两个端点 头和 尾部,之间有一定长度连接,并且这两个点之间有一定的旋转方向。
    矩阵变换比较简单,可以通过一个四元式和一个3维向量, 以及一个骨骼长度来描述。
    将骨骼的一端做为原点,这里我设定骨骼沿着x 轴方向,而骨骼长度就是lenght,offset表示孩子骨骼相对于父亲骨骼的 结束端点的 位置偏移,通常这个偏移量为0.
    这样矩阵变换的计算方法就是
    外部矩阵 * 父亲骨骼平移矩阵 * 父亲骨骼旋转矩阵 * x轴方向 length长度 平移矩阵 * 孩子骨骼的平移矩阵 * 孩子骨骼旋转矩阵
    上面就能计算到 一个孩子骨骼的变换矩阵了。抽取其中的元素,可以得到每根骨骼的变换矩阵的计算方法是
    外部矩阵 * 平移矩阵 * 旋转矩阵
    而传入给孩子骨骼的 矩阵就是 外部矩阵 * 平移矩阵 * 旋转矩阵 * x轴方向长度length 平移矩阵
    这里的length存在的主要目的是为了方便的描述旋转,因为对于长度0的骨骼来讲 就没办法指定其旋转方向了。
    解决了矩阵变换的描述方法 接着需要描述骨骼初始状态以及和mesh绑定时的初始状态
    首先建模的时候会把骨骼的父子关系确定每个骨骼初始的旋转 长度 平移这些参数确定, 这里我们假设骨骼动画只有旋转, 这样的话 长度可以存储到上面提到的length里面。
    这时候如何存储初始的旋转矩阵呢? 我们为每一个骨骼分配了一个额外的矩阵,这个矩阵里面存放 骨骼初始 骨骼的初始平移矩阵 左乘 旋转矩阵 的逆 矩阵。
    这样在对一个骨骼上面的定点进行变换的时候,如果骨骼在初始状态,那么对定点的变换就是 单位矩阵,通过上面计算的逆矩阵 * 骨骼当前的变换矩阵 就会得到一个单位矩阵 而这个单位矩阵 再作用到 定点上 定点就不会运动了。这样就保证了 在骨骼的初始状态下 骨骼上绑定的 顶点 也是在初始状态, 不会运动。
    上面计算的骨骼逆矩阵是骨骼局部空间的逆矩阵 因此是不够用的,对于子骨骼来讲 其变换矩阵是经过父亲层层传递的, 因此一个子骨骼的逆矩阵实际的逆向矩阵是 子局部逆向矩阵 * 父亲逆向矩阵
    这时候 对于 计算孩子骨骼的实际变换矩阵就是 子局部逆向矩阵 * 父亲逆向矩阵 * 父亲传到的变换矩阵 * 子局部变化矩阵
    这样如果 父亲 孩子骨骼都在bind位置时候,孩子上面的mesh 定点也是在bind位置的。
    因此对于一个骨骼描述的数据就包括
    rotate 四元式骨骼局部旋转
    offset 3维度向量 骨骼局部偏移
    length 长度 骨骼长度
    reverse kmMat4 局部逆矩阵
    mat 计算的实际变换矩阵 用于给mesh定点使用
    child 骨骼的孩子骨骼
    parent 骨骼的父亲
    id 骨骼的编号
    name 骨骼的名字

  4. blender中的骨骼动画导出 并在cocos2dx 中导入
    blender 支持python脚本有比较好的扩展性 因此尝试导出blender中的模型和动画数据 在游戏引擎中使用
    需要导出的数据包括
    定点数据 包括 定点位置 定点绑定的骨骼编号 以及权重
    面数据 每个三角面 由哪几个编号的定点构成
    骨骼数据 骨骼在bind姿势下面 每个骨骼的旋转值, 偏移值, 长度信息, 骨骼的父亲信息
    动画信息 不同frame下 每个骨骼的 旋转 长度 偏移 信息


xuhong
210 声望2 粉丝

引用和评论

0 条评论