今回のエンジニアブログを担当する大原です。
演出部分を作っていく中で、色付きのキャラクタからグレーに向かってアニメーションさせたいときがあります。
Cocos2d-xで簡単に実装するのであれば、色付きの画像を奥にグレースケールの画像を手前に用意することで、グレースケールを透過させるアニメーションを実装するのが一番簡単な方法です。
しかし、その場合2種類分の画像を用意しなければならず、画面全体を覆うようなテクスチャであれば、容量を圧迫します。
そこで、Shaderによるグレースケールの変換を行いたいと思います。
シェーダーのコーディング
まずは、Shaderコードを書きます。
頂点シェーダーはとても単純なものです。
attribute vec4 a_position; attribute vec2 a_texCoord; attribute vec4 a_color; #ifdef GL_ES varying lowp vec4 v_fragmentColor; varying mediump vec2 v_texCoord; #else varying vec4 v_fragmentColor; varying vec2 v_texCoord; #endif void main() { gl_Position = CC_PMatrix * a_position; v_fragmentColor = a_color; v_texCoord = a_texCoord; }
本番は、フラグメントシェーダからです。
#ifdef GL_ES precision lowp float; #endif // Input varying vec4 v_fragmentColor; varying vec2 v_texCoord; uniform sampler2D u_texture; // Gray const float gRed = 0.298912; const float gGreen = 0.586611; const float gBlue = 0.114478; const vec3 gScale = vec3(gRed, gGreen, gBlue); uniform float u_Rate; void main() { vec4 baseColor = texture2D(u_texture, v_texCoord); float grayColor = dot(baseColor.rgb, gScale); // Changes colors. vec4 ansColor = vec4(vec3(grayColor), baseColor.a); vec4 blendColor = vec4(baseColor.rgb * u_Rate,1.0); ansColor = ansColor * (1.0 - u_Rate); ansColor = ansColor + blendColor; gl_FragColor = ansColor * v_fragmentColor.a; }
作成した
・shaders/glayscale.vsh
・shaders/glayscale.fsh
はResource/shadersに配置してください。
Cocos2d-xのプロジェクト側の対応
いつものプロジェクトファイルを作るときに自動生成される
HelloWorldクラスにシェーダに追加します。
bool HelloWorld::init() { if ( !Layer::init() ) { return false; } Size visibleSize = Director::getInstance()->getVisibleSize(); Vec2 origin = Director::getInstance()->getVisibleOrigin(); ///////////////////////////// // 3. add your codes below... // add a label shows "Hello World" // create and initialize a label auto label = LabelTTF::create("Hello World", "Arial", 24); // position the label on the center of the screen label->setPosition(Vec2(origin.x + visibleSize.width/2, origin.y + visibleSize.height - label->getContentSize().height)); // add the label as a child to this layer this->addChild(label, 1); // add "HelloWorld" splash screen" auto sprite = Sprite::create("HelloWorld.png"); // position the sprite on the center of the screen sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
シェーダの読み込み処理を追加します。
今回はCocos2d-x v3系で書いた場合です。
Cocos2d-x v2.2で書いた場合をコメントで記載しておきます。
//======================= // シェーダーの設定 //======================= //ver2.2 //sprite->setShaderProgram(new CCGLProgram()); sprite->setGLProgram(new GLProgram()); //CCGLProgram *shader = sprite->getShaderProgram(); this->m_Shader = sprite->getGLProgram(); this->m_Shader->retain(); //shader->initWithVertexShaderFilename("shaders/glayscale.vsh", "shaders/glayscale.fsh"); this->m_Shader->initWithFilenames("shaders/glayscale.vsh", "shaders/glayscale.fsh"); //shader->addAttribute(kCCAttributeNamePosition, kCCVertexAttrib_Position); this->m_Shader->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_POSITION,GLProgram::VERTEX_ATTRIB_POSITION); //shader->addAttribute(kCCAttributeNameColor, kCCVertexAttrib_Color); this->m_Shader->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_COLOR,GLProgram::VERTEX_ATTRIB_COLOR); //shader->addAttribute(kCCAttributeNameTexCoord, kCCVertexAttrib_TexCoords); this->m_Shader->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD,GLProgram::VERTEX_ATTRIB_TEX_COORD); this->m_Shader->link(); this->m_Shader->updateUniforms(); // add the sprite as a child to this layer this->addChild(sprite, 0); this->m_grayRate = 1.0f; //グレーに向かってアニメーションさせる this->GrayChange(); return true; }
色付きからグレースケールにアニメーションするため、
時間毎にグレーをどれ位適用するかを送っています。
/** * グレーに向かって変更する */ void HelloWorld::GrayChange() { if(this->m_grayRate > 0.0f) { this->m_grayRate -= 0.1f; this->runAction(Sequence::create(DelayTime::create(0.5f),CallFunc::create(CC_CALLBACK_0(HelloWorld::GrayChange,this)), NULL)); } else { this->m_grayRate = 0.0f; } //これからuniformの変数を送りますのメソッド this->m_Shader->updateUniforms(); //グレーの合成する適用率をシェーダーのソースに送ります。 this->m_Shader->setUniformLocationWith1f(this->m_Shader->getUniformLocationForName("u_Rate"), this->m_grayRate); }
これで色付きからグレースケールにアニメーションする動きが確認できたのではないでしょうか。