読者です 読者をやめる 読者になる 読者になる

WonderPlanet DEVELOPER BLOG

ワンダープラネットの開発者ブログです。モバイルゲーム開発情報を発信。

cocos2d-xで3Dプログラミング〜導入編〜

Cocos2d-x

はじめまして、今回のエンジニアブログ担当の安藤です。

cocos2d-xはv2.0からOpenGL ES 1.x 系はサポートから外されており、固定パイプラインシェーダを使用することが出来なくなっております。
今回はcocos2d-xのUtilityを効率よく使ってプログラマブルシェーダを実装してみようと思います。

まず、新規プロジェクトを用意してください。
新規作成
cocos2d-xの開発環境の設定〜プロジェクト作成まで(iOS篇)

最初はこのような構成になっているのかと思います。
構成
HelloWorldSceneに追記していきます。
追記したソースコードの解説です。

cocos2d-xの描画インタフェースとテクスチャを追記します。

HelloWorldScene.h

public:  
 ...  
 vartual void draw();  
 cocos2d::CCTexture2D* m_pTexture;  

今回は導入編ということで自前でシェーダーは書かず、cocos2d-xのシェーダーを利用します。
同時にポリゴンに貼るテクスチャも生成します。HelloWorld.pngはプロジェクト生成時に自動でリソースに追加されます。

HelloWorldScene.cpp

bool HelloWorld::init(){  
...  
  // シェーダーをセット  
  CCGLProgram* pProgram = CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTexture);  
  this->setShaderProgram(pProgram);  
  
  // テクスチャ生成  
  m_pTexture = CCTextureCache::sharedTextureCache()->addImage("HelloWorld.png");  
  return true;  
 }  

描画の設定をします。
cocos2d-xの描画パイプラインを利用するので、ポリゴン描画の処理のみ記述します。
シェーダーとのデータのやりとりはccGLEnableVertexAttribs / glVertexAttribPointerで頂点単位情報を
glGetUniformLocation / setUniformLocationWithMatrix4fv で全ての頂点に対して一律に処理される情報を送信しています。
シェーダーオブジェクトはgetShaderProgramで呼び出して使うことができます。

HelloWorldScene.cpp

 // cocos2d-x 描画インタフェース  
 void HelloWorldScene::draw(){  
   // 深度テスト有効  
   CCDirector::sharedDirector()->setDepthTest(true);  
   // 頂点に座標とテクスチャUVのindex指定  
   ccGLEnableVertexAttribs(kCCVertexAttribFlag_Position | kCCVertexAttribFlag_TexCoords );  
   // 設定したシェーダーを使用する  
   this->getShaderProgram()->use();  
  
    // 行列の作成  
    static float yaw = 0;  
    yaw += 0.01f;  
    kmMat4 matProjection;  
    kmMat4 matView;  
    kmMat4 matWVP;  
    kmMat4 matTrans,matScale,matRota,matWorld;  
    kmGLGetMatrix(KM_GL_PROJECTION, &matProjection ); // 射影行列を取得  
    kmGLGetMatrix(KM_GL_MODELVIEW, &matView ); // ビュー行列の取得  
    kmMat4RotationPitchYawRoll(&matRota, 0, yaw, 0);  
    kmMat4Translation(&matTrans, 250, 150, 100);  
    kmMat4Scaling(&matScale, 1,1,1);  
    kmMat4Multiply(&matWVP, &matProjection, &matView);  
    kmMat4Multiply(&matWorld, &matTrans, &matRota);  
    kmMat4Multiply(&matWorld, &matWorld, &matScale);  
    kmMat4Multiply(&matWVP, &matWVP, &matWorld);  
  
    // 作成した行列をシェーダーに送る  
    GLuint matrixId = glGetUniformLocation(this->getShaderProgram()->getProgram(), kCCUniformMVPMatrix_s);  
    this->getShaderProgram()->setUniformLocationWithMatrix4fv(matrixId, matWVP.mat, 1);  
  
    // テクスチャのバインド  
    ccGLBindTexture2D( m_pTexture->getName() );  
  
    // 頂点をセット  
    const float x=50,y=50;  
    kmVec3 pos[4];  
    kmVec2 uv[4];  
    glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, 0, pos);  
    glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, uv);  
  
    // 描画  
    kmVec3Fill(&pos[0], -x, -y, 0);  
    kmVec3Fill(&pos[1], -x, y, 0);  
    kmVec3Fill(&pos[2], x, -y, 0);  
    kmVec3Fill(&pos[3], x, y, 0);  
    kmVec2Fill(&uv[0], 0, 1);  
    kmVec2Fill(&uv[1], 0, 0);  
    kmVec2Fill(&uv[2], 1, 1);  
    kmVec2Fill(&uv[3], 1, 0);  
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);  
 }  

実行結果
完成1 完成2
しっかり3Dになってます。
今回はソースコードが冗長になるので板ポリゴンにしましたが、
頂点を増やして箱型にすると、より3Dっぽさが際立つのではないでしょうか。

次回はcocos2d-xならではの実践テクニックを紹介したいと思います。