转自http://cocos2d.cocoachina.com/bbs/forum.php?mod=viewthread&tid=3527&extra=page%3D1
内容重点: Hello World 3D, 简单的3D 渲染 + MD2 模型
English version:
http://jameshui.com/?p=10
cocos2d-x 是一个2D 遊戏引擎, 当然主要是拿来写2D遊戏, 但有时候我们会想加些简单的3D 物件做效果或一些特殊的用途, 那该怎麽理呢?
OpenGL ES 2.0 开始, 一切的渲染操作都是用 shader 了, 我首先尝试的是用一些网上找到的 shader, 放到一个 CCLayer 里, 然後在 CCLayer 的 draw() 里画一个3D 的盒子, 可惜试了几个不同的 shader 都没成功.
後来再研究了一下 CCSprite 的 draw(), 发现它用的坐标竟然就是3D的(x, y, z). 所以老话说什麽来著, 寻寻觅觅费劲跑到老远去找你爱的人没找著, 其实她就在你身边只是你没发现...
有了这个发现, 接下来就变得容易了, 基本上可以用 cocos2d-x 本来已有的 shader 就可以渲染简单的3D物件.
(注意: 当测试时, 不要用 cocos2d-x 自带的 HelloWorld, 这个例子的 VC 工程少了一些 lib 的设定, 一定要建立一个新的项目)
在这次的例子里, 我建立了一个叫 Layer3D 的 CCLayer 专门用作渲染3D 物件, 这个 layer 用的 shader 在 init() 里设定为"ShaderPositionTexture":
- CCGLProgram* program = CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTexture);
- setShaderProgram(program);
复制代码
我们想渲染一个有贴图的盒子, 所以同时也载入一张贴图:
- mTexture = CCTextureCache::sharedTextureCache()->addImage("HelloWorld.png");
复制代码
接下来我们就可以在 Layer3D 的 draw() 里利用 glDrawArrays 来画3D物件了:
- CCDirector::sharedDirector()->setDepthTest(true); // 1
-
- ccGLEnableVertexAttribs(kCCVertexAttribFlag_Position | kCCVertexAttribFlag_TexCoords ); // 2
-
- getShaderProgram()->use(); // 3
-
- ccGLBindTexture2D( mTexture->getName() ); // 4
复制代码
1) 要开了 Depth Test, 不然会分不清盒面的前後次序
2) 告诉系统我们会用上顶点列表和贴图座标列表
3) 调用之前设定的 shader
4) 启用盒子的贴图
- ccVertex3F vertices[4];
- ccVertex2F uv[4];
-
- glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, 0, vertices);
- glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, uv);
-
-
- float x = 0;
- float y = 0;
- float len = 8;
-
- /// front
- vertices[0] = vertex3(x-len,y-len,len);
- vertices[1] = vertex3(x-len,y+len,len);
- vertices[2] = vertex3(x+len,y-len,len);
- vertices[3] = vertex3(x+len,y+len,len);
-
- uv[0] = vertex2(0, 1);
- uv[1] = vertex2(0, 0);
- uv[2] = vertex2(1, 1);
- uv[3] = vertex2(1, 0);
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
- // right
- vertices[0] = vertex3(x+len,y-len,len);
- vertices[1] = vertex3(x+len,y+len,len);
- vertices[2] = vertex3(x+len,y-len,-len);
- vertices[3] = vertex3(x+len,y+len,-len);
-
- uv[0] = vertex2(0, 1);
- uv[1] = vertex2(0, 0);
- uv[2] = vertex2(1, 1);
- uv[3] = vertex2(1, 0);
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
- / back
- vertices[0] = vertex3(x+len,y-len,-len);
- vertices[1] = vertex3(x+len,y+len,-len);
- vertices[2] = vertex3(x-len,y-len,-len);
- vertices[3] = vertex3(x-len,y+len,-len);
-
- uv[0] = vertex2(0, 1);
- uv[1] = vertex2(0, 0);
- uv[2] = vertex2(1, 1);
- uv[3] = vertex2(1, 0);
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
- // left
- vertices[0] = vertex3(x-len,y-len,len);
- vertices[1] = vertex3(x-len,y+len,len);
- vertices[2] = vertex3(x-len,y-len,-len);
- vertices[3] = vertex3(x-len,y+len,-len);
-
- uv[0] = vertex2(0, 1);
- uv[1] = vertex2(0, 0);
- uv[2] = vertex2(1, 1);
- uv[3] = vertex2(1, 0);
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
-
- / top
- vertices[0] = vertex3(x+len,y+len,len);
- vertices[1] = vertex3(x-len,y+len,len);
- vertices[2] = vertex3(x+len,y+len,-len);
- vertices[3] = vertex3(x-len,y+len,-len);
-
- uv[0] = vertex2(0, 0);
- uv[1] = vertex2(1, 0);
- uv[2] = vertex2(0, 1);
- uv[3] = vertex2(1, 1);
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
- / bottom
- vertices[0] = vertex3(x+len,y-len,len);
- vertices[1] = vertex3(x-len,y-len,len);
- vertices[2] = vertex3(x+len,y-len,-len);
- vertices[3] = vertex3(x-len,y-len,-len);
-
- uv[0] = vertex2(0, 0);
- uv[1] = vertex2(1, 0);
- uv[2] = vertex2(0, 1);
- uv[3] = vertex2(1, 1);
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
复制代码
座标 (0,0,0) 是萤幕的左下角, 如果我们想把盒子移到其他位置, 就要利用 shader 的 matrix 了, 自己设定 matrix 的内容可是一件头痛的事, 还好 cocos2d-x 里自带了 kazmath 库, 我们可以好好的利用一下:
- kmMat4 matrixP;
- kmMat4 matrixMV;
- kmMat4 matrixMVP;
-
- kmGLGetMatrix(KM_GL_PROJECTION, &matrixP );
- kmGLGetMatrix(KM_GL_MODELVIEW, &matrixMV );
-
- kmQuaternion quat;
- kmQuaternionRotationYawPitchRoll(&quat, mYaw, mPitch, mRoll); // 1
-
- kmMat3 rotation;
- kmMat3RotationQuaternion(&rotation, &quat); // 2
-
- kmVec3 translation;
- kmVec3Fill(&translation, 240, 150, 220); // 3
-
- kmMat4 rotationAndMove;
- kmMat4RotationTranslation(&rotationAndMove, &rotation, &translation); // 4
-
- kmMat4Multiply(&matrixMVP, &matrixP, &matrixMV);
- kmMat4Multiply(&matrixMVP, &matrixMVP, &rotationAndMove); // 5
-
- GLuint matrixId = glGetUniformLocation(getShaderProgram()->getProgram(), "u_MVPMatrix");
- getShaderProgram()->setUniformLocationwithMatrix4fv(matrixId, matrixMVP.mat, 1); // 6
复制代码
1) 设定一个带有 X, Y, Z 叁个轴的旋转资料的 quaternion
2) 把 quaternion 换成 3x3 的 matrix
3) 设定移动的数据
4) 设定一个带有旋转和位移的 4x4 matrix
5) 把上边的 matrix 加入到用作更新 shader 的 matrix 里
6) 更新 shader 的 matrix
当然, 如果可以画盒子, 我们自然也可以画3D 模型, 这里就怀旧一下, 我把以前弄的一个 MD2 (Quake2) 模型类移植了过来:
这里我只是随便的弄了一下, 大家可以继续研究研究怎样处理 camera 或是加入 lighting