エンジニア

グレースケールのアニメーション表示をしてみる

投稿日:2015年3月10日 更新日:

今回のエンジニアブログを担当する大原です。

演出部分を作っていく中で、色付きのキャラクタからグレーに向かってアニメーションさせたいときがあります。
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);
}

アニメーションのスクリーンショット
色付きから
grayscale_color

グレースケールに
grayscale_gray

これで色付きからグレースケールにアニメーションする動きが確認できたのではないでしょうか。

採用情報

ワンダープラネットでは、一緒に働く仲間を幅広い職種で募集しております。

-エンジニア
-,

© WonderPlanet Inc.