- 本帖最后由 dr_watson 于 2012-8-16 22:30 编辑 *
内容重点: 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
- 要开了 Depth Test, 不然会分不清盒面的前後次序
- 告诉系统我们会用上顶点列表和贴图座标列表
- 调用之前设定的 shader
- 启用盒子的贴图
ccVertex3F vertices;
ccVertex2F uv;
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 = vertex3(x-len,y-len,len);
vertices = vertex3(x-len,y+len,len);
vertices = vertex3(x+len,y-len,len);
vertices = vertex3(x+len,y+len,len);
uv = vertex2(0, 1);
uv = vertex2(0, 0);
uv = vertex2(1, 1);
uv = vertex2(1, 0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
////////// right
vertices = vertex3(x+len,y-len,len);
vertices = vertex3(x+len,y+len,len);
vertices = vertex3(x+len,y-len,-len);
vertices = vertex3(x+len,y+len,-len);
uv = vertex2(0, 1);
uv = vertex2(0, 0);
uv = vertex2(1, 1);
uv = vertex2(1, 0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
///////// back
vertices = vertex3(x+len,y-len,-len);
vertices = vertex3(x+len,y+len,-len);
vertices = vertex3(x-len,y-len,-len);
vertices = vertex3(x-len,y+len,-len);
uv = vertex2(0, 1);
uv = vertex2(0, 0);
uv = vertex2(1, 1);
uv = vertex2(1, 0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
////////// left
vertices = vertex3(x-len,y-len,len);
vertices = vertex3(x-len,y+len,len);
vertices = vertex3(x-len,y-len,-len);
vertices = vertex3(x-len,y+len,-len);
uv = vertex2(0, 1);
uv = vertex2(0, 0);
uv = vertex2(1, 1);
uv = vertex2(1, 0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
///////// top
vertices = vertex3(x+len,y+len,len);
vertices = vertex3(x-len,y+len,len);
vertices = vertex3(x+len,y+len,-len);
vertices = vertex3(x-len,y+len,-len);
uv = vertex2(0, 0);
uv = vertex2(1, 0);
uv = vertex2(0, 1);
uv = vertex2(1, 1);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
///////// bottom
vertices = vertex3(x+len,y-len,len);
vertices = vertex3(x-len,y-len,len);
vertices = vertex3(x+len,y-len,-len);
vertices = vertex3(x-len,y-len,-len);
uv = vertex2(0, 0);
uv = vertex2(1, 0);
uv = vertex2(0, 1);
uv = 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
- 设定一个带有 X, Y, Z 叁个轴的旋转资料的 quaternion
- 把 quaternion 换成 3x3 的 matrix
- 设定移动的数据
- 设定一个带有旋转和位移的 4x4 matrix
- 把上边的 matrix 加入到用作更新 shader 的 matrix 里
- 更新 shader 的 matrix
440
当然, 如果可以画盒子, 我们自然也可以画3D 模型, 这里就怀旧一下, 我把以前弄的一个 MD2 (Quake2) 模型类移植了过来:
442441
444
443
这里我只是随便的弄了一下, 大家可以继续研究研究怎样处理 camera 或是加入 lighting 
445446
449
450
448
