打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
[2.1.0]Cocos2d
 本帖最后由 火星熊猫 于 2013-3-7 08:07 编辑

说明:按照FireDragon的建议,我把缩放原理这部分发在新帖里(顺便再骗一个精华
),但是本帖内容与上一贴讲到的部分内容有联系,所以请自行参考 http://bbs.firedragonpzy.com.cn/ ... d=54&extra=page%3D1


二、节点缩放原理
引擎对节点的平移(T)、缩放(S)、旋转(R)等操作,最终都是通过修改OpenGL的模型视图矩阵(MV)和投影矩阵(P)来实现的。
在cocos2dx\kazmath\src\GL\matrix.c中维护了三个栈结构用来保存绘制流程中每个节点的矩阵
  1. km_mat4_stack modelview_matrix_stack; //模型视图矩阵栈
  2. km_mat4_stack projection_matrix_stack; //投影矩阵栈
  3. km_mat4_stack texture_matrix_stack; //引擎中目前未使用
复制代码
三个栈会在第一次访问时被初始化,初始化后栈中都有一个4*4的对角线是1的单位矩阵。
  1. void lazyInitialize()
  2. {

  3.     if (!initialized) { //若尚未初始化则进入,下面的各种接口都会频繁的进入这里
  4.         kmMat4 identity; //Temporary identity matrix

  5.         //Initialize all 3 stacks
  6.         //modelview_matrix_stack = (km_mat4_stack*) malloc(sizeof(km_mat4_stack));
  7.         km_mat4_stack_initialize(&modelview_matrix_stack); //MV栈初始化

  8.         //projection_matrix_stack = (km_mat4_stack*) malloc(sizeof(km_mat4_stack));
  9.         km_mat4_stack_initialize(&projection_matrix_stack); //P栈初始化

  10.         //texture_matrix_stack = (km_mat4_stack*) malloc(sizeof(km_mat4_stack));
  11.         km_mat4_stack_initialize(&texture_matrix_stack);

  12.         current_stack = &modelview_matrix_stack; //设定MV矩阵栈为当前栈
  13.         initialized = 1; //初始化标志设为true

  14.         kmMat4Identity(&identity); //创建单位矩阵

  15.         //Make sure that each stack has the identity matrix
  16.         km_mat4_stack_push(&modelview_matrix_stack, &identity); //单位矩阵压入MV
  17.         km_mat4_stack_push(&projection_matrix_stack, &identity); //单位矩阵压入P
  18.         km_mat4_stack_push(&texture_matrix_stack, &identity);
  19.     }
  20. }
复制代码
用kmGLMatrixMode(kmGLEnum mode)函数来切换当前操作的栈
  1. void kmGLMatrixMode(kmGLEnum mode)
  2. {
  3.     lazyInitialize(); //判定是否需要初始化以及初始化

  4.     switch(mode)
  5.     {
  6.         case KM_GL_MODELVIEW:
  7.             current_stack = &modelview_matrix_stack; //切换为模型视图矩阵栈
  8.         break;
  9.         case KM_GL_PROJECTION:
  10.             current_stack = &projection_matrix_stack; //切换为投影矩阵栈
  11.         break;
  12.         case KM_GL_TEXTURE:
  13.             current_stack = &texture_matrix_stack;
  14.         break;
  15.         default:
  16.             assert(0 && "Invalid matrix mode specified"); //TODO: Proper error handling
  17.         break;
  18.     }
  19. }
复制代码
用kmGLPushMatrix()函数将当前栈的栈顶矩阵复制后压栈,kmGLPopMatrix()用于出栈,kmGLLoadIdentity()用于将栈顶矩阵初始化成单位矩阵。
  1. void kmGLPushMatrix(void)
  2. {
  3.     kmMat4 top;

  4.     lazyInitialize(); //Initialize the stacks if they haven't been already //判定是否需要初始化以及初始化

  5.     //Duplicate the top of the stack (i.e the current matrix)
  6.     kmMat4Assign(&top, current_stack->top); //将栈顶矩阵复制到top
  7.     km_mat4_stack_push(current_stack, &top); //将top压栈
  8. }

  9. void kmGLPopMatrix(void)
  10. {
  11.     assert(initialized && "Cannot Pop empty matrix stack");
  12.     //No need to lazy initialize, you shouldn't be popping first anyway!
  13.     km_mat4_stack_pop(current_stack, NULL); //当前栈 弹出一个矩阵
  14. }

  15. void kmGLLoadIdentity()
  16. {
  17.     lazyInitialize();  //判定是否需要初始化以及初始化

  18.     kmMat4Identity(current_stack->top); //Replace the top matrix with the identity matrix //将当前栈的栈顶元素覆盖为单位矩阵
  19. }
复制代码
kmGLMultMatrix()做矩阵乘法(主要是节点经过变形计算后得到的结果与栈顶矩阵相乘时,以及最终绘制时MV与P相乘时使用)
kmGLGetMatrix ()获得指定栈的栈顶矩阵
  1. void kmGLMultMatrix(const kmMat4* pIn)
  2. {
  3.     lazyInitialize(); //判定是否需要初始化以及初始化
  4.     kmMat4Multiply(current_stack->top, current_stack->top, pIn); //将pIn与当前栈栈顶矩阵相乘,结果存入栈顶矩阵
  5. }

  6. void kmGLGetMatrix(kmGLEnum mode, kmMat4* pOut)
  7. {
  8.     lazyInitialize(); //判定是否需要初始化以及初始化

  9.     switch(mode)
  10.     {
  11.         case KM_GL_MODELVIEW:
  12.             kmMat4Assign(pOut, modelview_matrix_stack.top); //获取MV栈栈顶矩阵
  13.         break;
  14.         case KM_GL_PROJECTION:
  15.             kmMat4Assign(pOut, projection_matrix_stack.top); //获取P栈栈顶矩阵
  16.         break;
  17.         case KM_GL_TEXTURE:
  18.             kmMat4Assign(pOut, texture_matrix_stack.top);
  19.         break;
  20.         default:
  21.             assert(1 && "Invalid matrix mode specified"); //TODO: Proper error handling
  22.         break;
  23.     }
  24. }
复制代码
当每个节点被访问(visit())时,先压栈(默认操作的是MV),再变形
  1. kmGLPushMatrix(); //压栈,压的是MV

  2. this->transform(); //变形计算
复制代码
CCNode::transform()中,CCNode::nodeToParentTransform()负责实际计算,然后将计算结果乘入MV的栈顶矩阵
  1. kmMat4 transfrom4x4;

  2. // Convert 3x3 into 4x4 matrix
  3. CCAffineTransform tmpAffine = this->nodeToParentTransform(); //实际计算
  4. CGAffineToGL(&tmpAffine, transfrom4x4.mat); //将计算结果转换成4*4的矩阵

  5. // Update Z vertex manually
  6. transfrom4x4.mat[14] = m_fVertexZ;

  7. kmGLMultMatrix( &transfrom4x4 ); //做乘法,这里要注意相乘前的MV栈顶矩阵是从父节点的矩阵复制过来的(kmGLPushMatrix有复制的环节),所以如果父节点是缩放过的,那么子节点的矩阵在相乘后也就同样是缩放过的,所以节点缩放为什么具有传递性,原因就在这里
复制代码
CCNode::nodeToParentTransform()并不是每一帧都会计算,只有当需要的时候,即m_bTransformDirty为真时,才会重新计算。除了节点的创建的时候,m_bTransformDirty  为真。运行过程中的扭曲(setSkew)、旋转(setRotation)、缩放(setScale)、平移(setPosition)、设置锚点(setAnchorPoint)、设置内容尺寸(setContentSize)等操作会导致节点的 m_bTransformDirty 设置为真。经过一次变形计算后, m_bTransformDirty 再次赋值成false。
我在这里只贴出了缩放生效的代码,其他操作的计算,请参考 CCNode::nodeToParentTransform()
  1. m_sTransform = CCAffineTransformMake( cy * m_fScaleX, sy * m_fScaleX, -sx * m_fScaleY, cx * m_fScaleY, x, y ); // 这句负责缩放的计算,以及把尺寸信息和坐标信息拼成一个Struct
复制代码
当节点在绘制自身(draw())的时候,以CCLayerColor为例,先将矩阵设置给OpenGL,然后设置颜色数据和顶点数据,指定像素渲染模式,最后绘制顶点
矩阵是在 CC_NODE_DRAW_SETUP 这个宏里设置到OpenGL的
  1. #define CC_NODE_DRAW_SETUP() \
  2. do { \
  3. ccGLEnable(m_eGLServerState); \
  4. CCAssert(getShaderProgram(), "No shader program set for this node"); \
  5. { \
  6. getShaderProgram()->use(); \
  7. getShaderProgram()->setUniformsForBuiltins(); \ //这句负责将矩阵设置给OpenGL
  8. } \
  9. } while(0)
复制代码
CCGLProgram::setUniformsForBuiltins() 中先取得MV和P,然后将MV和P相乘得到名字很酷的MVP,即模型视图投影矩阵,然后调用setUniformLocationWithMatrix4fv 将各个矩阵写入OpenGL中
  1. kmMat4 matrixP;
  2. kmMat4 matrixMV;
  3. kmMat4 matrixMVP;

  4. kmGLGetMatrix(KM_GL_PROJECTION, &matrixP); //获得投影矩阵栈的栈顶矩阵
  5. kmGLGetMatrix(KM_GL_MODELVIEW, &matrixMV);//获得模型视图矩阵栈的栈顶矩阵

  6. kmMat4Multiply(&matrixMVP, &matrixP, &matrixMV);//相乘得到MVP

  7. setUniformLocationWithMatrix4fv(m_uUniforms[kCCUniformPMatrix], matrixP.mat, 1); //设置投影矩阵
  8. setUniformLocationWithMatrix4fv(m_uUniforms[kCCUniformMVMatrix], matrixMV.mat, 1); //设置MV矩阵
  9. setUniformLocationWithMatrix4fv(m_uUniforms[kCCUniformMVPMatrix], matrixMVP.mat, 1); //设置MVP矩阵
复制代码
至于矩阵的说明,我也不懂,所以请参考OpenGL的相关资料。

超出字数限制了,后面的部分放在二楼
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
[OpenGL ES 03]3D变换:模型,视图,投影与Viewport
如何投影一个纹理 (翻译:心蓝 潘李亮) - OpenGL资源 代码 电子书 请到www....
Android OpenGL ES 2.0 完全入门(一):基本概念和 hello world
乱弹OpenGL选择
OpenGL 矩阵变换
三维变换glMatrixMode
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服