ユニバーサルトランジション(cross dissolve)

 transition   dissolve   in_Colour   sampler2D   clamp   views   safari 

吉里吉里のユニバーサルトランジション( Universal Transition ) を GLSL ES で再現。

吉里吉里とは、日本国内でアドベンチャー等ゲーム製作に利用される製作ツールの名称。

吉里吉里のユニバーサルトランジションは、一般的にはクロスディゾルブとかクロスフェーディングと呼ばれる画面遷移( トランジション/transition ) 効果の亜種。シーン切り替えの際に多用される演出の一つで、吉里吉里は「ルール画像」を利用してディゾルブ( オーバーラップ ) する手法を「ユニバーサルトランジション」と呼んでいます。

シェーダとしては実装が簡単、「ルール画像」も日本国内で素材が豊富、オリジナルのルール画像を作成するのも簡単。

ユニバーサルトランジション with GLSL ES

イベントを一つのオブジェクトへまとめ、

上記三つのイベントへ収まるように作った。無駄な変数定義を排除、処理をユーザ定義関数化しコードの使い回し簡単。

今回のサンプルでは http://4you.bz/rule からダウンロードしたルール画像から 162.png をルームサイズへ縮小して利用しました。

ルール画像 162.png (512✕512への縮小で比率が狂ってます)

値は -1.0 〜 1.0 までの範囲で制限。

要 WebGL、Google Chrome/FireFox 等で動作を確認。

HTML5( WebGL ) と Windows PC で描画結果が異なる場合があります。

シェーダー名:SH_Tr_dissolve

参考動作:Shader-Transition : Cross Dissolve

OpenGL-ES_50px_May16 WebGL_50px_June16

( MacBook Pro OS X El Capitan + Safari 11601.7.7 で正常に動作せず )

Vertex Shader

 
attribute vec3 in_Position;       // (x,y,z)
attribute vec2 in_TextureCoord;   // (u,v)
attribute vec4 in_Colour;         // (r,g,b,a)
// 頂点カラーは文字色で利用しています

varying vec2 v_vTexcoord;
varying vec4 v_vColour;

void main()
{
    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * vec4(in_Position, 1.0);
    v_vTexcoord = in_TextureCoord;
    v_vColour   = in_Colour;//for Color of draw Strings
}
 

Fragment Shader

 
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

uniform float   u_Muscle;
uniform sampler2D u_Grad;

void main()
{
    vec4 src     = v_vColour * texture2D(gm_BaseTexture , v_vTexcoord);
    vec4 dst     = texture2D(u_Grad , v_vTexcoord);
    float ok     = clamp(0.0, dst.r + u_Muscle, 1.0);
    gl_FragColor = vec4(src.rgb, ok);
}
 

シェーダコードはとても簡単。サンプラーの追加が必要なので、ここから GML のバインド処理が多少の手間かかります。

配布されている「ルール画像」を利用すればリソースを作成する手間が省けます。

ユーザ定義関数 Init_Shader_dissolve

///Init_Shader_dissolve();
draw_set_font(font0);
var a,b,c,d,e;
a = SH_Tr_dissolve;
    if !shader_is_compiled(a) {
        b = "ur hardware ain't support shader - SH_Tr_dissolve";
        show_message(b);
    }
    else{
        b      = "u_Grad";
        c      = "u_Muscle";
        d      = gradmap;
        e      = background_get_texture(d);
        
        /* Instance variables for Uniform */
        U_Grad = shader_get_sampler_index(a, b);
        U_Musc = shader_get_uniform(a, c);
        Muscle = -1.000;// start up value
        Value  =  0.08;
        VMax   =  1.0;  // Maximum value
        VMin   = -1.0;  // Minimum value
        
        /* set texture on GPU Stage */
        shader_set(a);
        texture_set_stage(U_Grad, e);
        shader_reset();

        /* Instance variable for Surface */
        Surf   = noone;
    };
 

二枚の画像をオーバーラップするだけなら「ルール画像」は不要。ルール画像を利用することで単なるディゾルブをカスタムできるのがユニバーサルトランジションの特徴。

ユニバーサルトランジションを再現する場合、ルール画像はサンプラーとして GPU へ事前送信する必要がある。テクスチャのポインタ値を得て texture_set_stage 関数を利用。この時、背景やスプライトをテクスチャとしてポインタ値を得るか、surface へ描画して surface のポインタ値を得るか、二種類が考えられる。

GM:S の仕様が絡んでくるため background_get_texture 関数はトラブルが発生する可能性。surface への描画結果を渡すのが一番無難だが、 surface は正規の手順を踏むと手続きが若干多くなるなど、一長一短。

background_get_texture 関数の利用時、注意したいのが GM:S のバグ。

背景のポインタ値を得る際、稀にテクスチャのグループ化が狂って保存されるケースがある。グループ化が狂うとテクスチャの uv 情報が狂い、サンプラーとして転送後に利用したら表示位置や表示サイズに悪影響が認められた。GPU へ転送後、サンプラーの uv 情報が何かおかしいと思ったら、一度テクスチャグループを手動で分割しグループを保存し直せば改善される。

この辺りのバッドノウハウはできれば回避したいので、surface を使う方法がトラブルも無く安心感ある。

ユーザ定義関数 Draw_Source

///Draw_Source();
var a;
a = "SH_Tr_dissolve";
draw_set_colour(c_green);
draw_text(10,10,a);
draw_self();
 

先に application surface に対して通常の draw を使って描画しておく。この描画結果をソース画像として扱い、次に DrawGUI で Surface と GPU へ転送済みのルール画像( 2D サンプラー ) を使って合成処理を行う。

ユーザ定義関数 DrawGUI_dissolve

///DrawGUI_dissolve();
if (!surface_exists(Surf)){
    var z,w,h;
    z                  = 0;
    w                  = view_wview[z];
    h                  = view_hview[z];
    Surf               = surface_create(w , h);
    view_surface_id[z] = Surf;
    surface_set_target(Surf);
    draw_clear_alpha(c_white, -1);
    surface_reset_target();
}
else{
    var a,b,c,d,e;
    a  = SH_Tr_dissolve;// Shader
    b  = Muscle;
    c  = Value;
        if (b > VMax or b < VMin) c = c * -1;
    b += c;
    d  = background0;      // Resource for destination
    e  = Surf;             // Surface (equal view[0])
    
    /* Surface Begin */
    surface_set_target(e);

    /* Shader Begin */
    shader_set(a);
    shader_set_uniform_f(U_Musc, b);
    draw_background(d, 0,0);

    /* Reset on the target Shader & Surface */
    shader_reset();
    surface_reset_target();
    
    /* Normal Draw */
    draw_surface(e, 0,0);

    /* finalize */
    Muscle = b;
    Value  = c;
};
 

今回は surface と views をリンクさせる手法を使ったので、ルーム設定から views を必ず有効化する。

surface と views をリンクさせれば、surface への描画は views への描画と一致。

ユーザ定義関数 DrawGUI_Debug

///DrawGUI_Debug();
if keyboard_check_pressed(ord("R")) room_restart();

var a,b,c;
a = room_height - 50;
b = Muscle;
c = string_format(b,1,5);
draw_text(10,a,c);
 

イベント処理

シェーダと4つのユーザ定義関数を使って簡潔なイベントを作成。

Create Event

///Init
Init_Shader_dissolve();

Draw Event

///Draw
Draw_Source();

DrawGUI Event

///DrawGUI
DrawGUI_dissolve();
DrawGUI_Debug();

ユニバーサルトランジションはディゾルブ( Dissolve ) の亜種だが、様々な遷移パターンを画像から作ることができるため、計算に依る遷移効果よりも作るのがお手軽でパターンを増やすのも楽。

「ルール画像」は吉里吉里/KAG 用をそのまま流用できます。権利関係調べたり解像度を合わせたりが面倒な場合は、既存のものを参考にして自作すれば速い。自作する場合、色数はインデックスカラー、すなわち 256 色( 8bit グレースケール ) で作ればリソースサイズを小さくできる。




次へ

前へ