文/Cocos2d-X社区版主dr_watson
内容重点: Hello World 3D, 简单的3D 渲染 + MD2 模型
English version 地址点这里
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
这里我只是随便的弄了一下, 大家可以继续研究研究怎样处理 camera 或是加入 lighting
原地址:http://www.cocoachina.com/gamedev/gameengine/2012/0816/4606.html