cocos2d-x Glowエフェクト


cocos2d-x Advent Calendar21日目の記事です。

## はじめに

今回はアウトラインを色付ける[サンプル](https://github.com/cocos2d/cocos2d-x/blob/v3/tests/cpp-tests/Resources/Shaders/example_Outline.fsh)を元に、Glowエフェクトっぽいモノを作っていきます。

Glowエフェクトとは、その名の通り光る演出です。


## フラグメントシェーダーの用意

早速ですが、サンプルのフラグメントシェーダーを元にGlowエフェクトっぽく書いたのが下記になります。

```glow.fsh
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;

uniform float u_ctime;
uniform float u_threshold;
uniform float u_radius;
uniform vec4  u_outlineColor;

void main() {
    float radius = u_radius;

    vec4 accum  = vec4(0.0);
    vec4 normal = vec4(0.0);

    normal = texture2D(CC_Texture0, v_texCoord);
    
    for(float i = 1.0; i <= radius; i += 1.0) {
        accum += texture2D(CC_Texture0, vec2(v_texCoord.x - 0.01 * i, v_texCoord.y - 0.01 * i));
        accum += texture2D(CC_Texture0, vec2(v_texCoord.x + 0.01 * i, v_texCoord.y - 0.01 * i));
        accum += texture2D(CC_Texture0, vec2(v_texCoord.x + 0.01 * i, v_texCoord.y + 0.01 * i));
        accum += texture2D(CC_Texture0, vec2(v_texCoord.x - 0.01 * i, v_texCoord.y + 0.01 * i));
    }
    
    accum.rgb =  u_outlineColor.rgb * u_outlineColor.a * accum.a * 0.95;
    float opacity = ((1.0 - normal.a) / radius) * (u_ctime / u_threshold);
    
    normal = (accum * opacity) + (normal * normal.a);
    
    gl_FragColor = v_fragmentColor * normal;
}
```

このフラグメントシェーダーをSpriteに適用させてみます。
バーテックスシェーダーには`ccPositionTextureColor_noMVP_vert`を使用します。


```HelloWorldScene.cpp

bool HelloWorld::init()
{

    // ... 省略

    auto sprite = Sprite::create("HelloWorld.png");
    sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
    
    Vec4 color(0.5f, 0.5f, 0.5f, 1.0f);
    m_ctime = 0.01f;  // メンバ変数
    GLfloat gtime = 1.0f;
    GLfloat radius = 5.0f;
    
    auto fileUtiles = FileUtils::getInstance();
    auto fragmentFullPath = fileUtiles->fullPathForFilename("shaders/glow.fsh");
    auto fragSource = fileUtiles->getStringFromFile(fragmentFullPath);
    
    auto glprogram = GLProgram::createWithByteArrays(ccPositionTextureColor_noMVP_vert, fragSource.c_str());
    auto glProgramState = GLProgramState::getOrCreateWithGLProgram(glprogram);
    
    glProgramState->setUniformVec4("u_outlineColor", color);
    glProgramState->setUniformFloat("u_ctime", m_ctime);
    glProgramState->setUniformFloat("u_threshold", gtime);
    glProgramState->setUniformFloat("u_radius", radius);
    
    sprite->setGLProgramState(glProgramState);
    
    glProgramState->setUniformCallback("u_ctime", CC_CALLBACK_2(HelloWorld::callbackColor, this));
    
    this->addChild(sprite, 1);
        
    return true;
}

void HelloWorld::callbackCTime(GLProgram* glProgram, Uniform* uniform)
{
    m_ctime += 0.05f;
    glProgram->setUniformLocationWith1f(uniform->location, (sin(m_ctime) + 1)/2);
}

```


![完成イメージ](https://qiita-image-store.s3.amazonaws.com/0/20511/47b75e5c-db15-1c66-0ae1-1cdff6dbe430.gif "glow_blink.gif")


`setUniformCallback` が非常に便利ですね。


## 色を変化させオーラ感を出す

同じフラグメントシェーダーを利用します。

```HelloWorldScene.cpp
bool HelloWorld::init()
{

    // ... 省略

    auto sprite = Sprite::create("HelloWorld.png");
    sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
   
    Vec4 color(0.5f, 0.5f, 0.5f, 1.0f);
    GLfloat ctime = 1.0f;
    GLfloat gtime = 1.0f;
    GLfloat radius = 5.0f;
        
    auto fileUtiles = FileUtils::getInstance();
    auto fragmentFullPath = fileUtiles->fullPathForFilename("shaders/glow.fsh");
    auto fragSource = fileUtiles->getStringFromFile(fragmentFullPath);
        
    auto glprogram = GLProgram::createWithByteArrays(ccPositionTextureColor_noMVP_vert, fragSource.c_str());
    auto glProgramState = GLProgramState::getOrCreateWithGLProgram(glprogram);
        
    glProgramState->setUniformVec4("u_outlineColor", color);
    glProgramState->setUniformFloat("u_ctime", ctime);
    glProgramState->setUniformFloat("u_threshold", gtime);
    glProgramState->setUniformFloat("u_radius", radius);
        
    sprite->setGLProgramState(glProgramState);
     
    glProgramState->setUniformCallback("u_outlineColor", CC_CALLBACK_2(HelloWorld::callbackColor, this));
        
    this->addChild(sprite, 1);
    
    return true;
}

void HelloWorld::callbackColor(GLProgram* glProgram, Uniform* uniform)
{
    float r = CCRANDOM_0_1();
    float g = CCRANDOM_0_1();
    float b = CCRANDOM_0_1();
    float a = 1.0f;
    
    glProgram->setUniformLocationWith4f(uniform->location, r, g, b, a);
}

```


![完成イメージ](https://qiita-image-store.s3.amazonaws.com/0/20511/b736ad7b-bc8d-d58c-8c22-7835f24c8305.gif "glow_color.gif")

以上になりますが、それっぽくなったでしょうか？
GLSLを勉強するなら、最近だとWebGLから入るのが環境に左右されず本質を勉強出来そうで良さそうですね。


#### ps.

Advent Calendar大遅刻組です...、申し訳ありませんん。
cocos2d-x Advent Calendar 過疎り気味なので開いている日にでも投稿します。。