GameMaker:Studio Shader Wave(波)

 shader   wave   GLSL_ES 

配布されていたシェーダのサンプルのコードを整理したもの

Sample Wave

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

    1. Create
    2. DrawGUI

上記の二つのイベントへ収まるように作った。そして無駄な変数定義を全て排除。

なるべくユーザ定義関数化し、コードの使い回しが簡単。

参考動作:http://prester.org/html5/gms_shader_wave/

参考(YoYoGames MarketPlace):xygthop3’s Shader Resources

キーボードの Q / A 、 W / S 、 E / D キーで値変更可

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

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

Android ( アプリがフルスクリーンで動作する環境 ) の場合、画面サイズおよび画面比率に応じたテクスチャのサイズ調整処理が必要となります。

このサンプルはテクスチャの UV を使って波のように画面を動かしています。

フルスクリーンで動作する場合にサイズ調整処理を間に入れない場合には application surface の等倍サイズで UV 情報も fragment シェーダに渡されています。テクスチャサイズと画面サイズが一致しないため、シェーダを使った描画はテクスチャサイズに沿った描画結果となってしまい、つまり画面に意図しない余白が生じます。

無駄な余白を回避してフル画面できっちり表示するための処理として、ユーザ定義関数である DrawGUI_Wave に必要な処理が含まれています。

draw_surface_ext 命令を使っている部分が上記該当処理です。

ウィンドウモードでもウィンドウのサイズを変更できる場合にはサイズ変更後の拡大縮小率を求めてテクスチャサイズの情報にも反映させる必要があります。

シェーダー名:SH_Wave

Vertex Shader

attribute vec3 in_Position;
attribute vec2 in_TextureCoord;

varying vec2 v_texcoord;

void main()
{
    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * vec4(in_Position, 1.0);
    v_texcoord  = in_TextureCoord;
}

Fragment Shader

precision mediump float;
varying vec2 v_texcoord;

uniform float time;
uniform float wave_amount;
uniform float wave_distortion;
uniform float wave_speed;

float tws = (time * wave_speed);

void main()
{
    vec2 uv = v_texcoord;
    /*
    uv.x   += cos((uv.y * wave_amount) + tws) / wave_distortion;
    uv.y   += sin((uv.x * wave_amount) + tws) / wave_distortion;
    */
    // 以下は、こういう書き方もできるよね的なサンプル、ただし描画結果は上記と微妙に異なります
    uv += (vec2( cos((uv.y * wave_amount) + tws) , sin((uv.x * wave_amount) + tws)) / wave_distortion);
    
    gl_FragColor = texture2D(gm_BaseTexture,uv);
}

ユーザ定義関数 Init_Shader_Wave

///Init_Shader_Wave();
/* 
動作確認済み
 → windowsPC == ok
 → Android == 画面比率の調整が必要
 → HTML5   == ok
*/
draw_set_font(font0);

    if (!shader_is_compiled(SH_Wave)) {
        show_message("Your hardware doesn't support shader - SH_Wave");
    }
    else{
        /* for Debug */
        shader_enabled = true;
        
        /* for Fragment Shader, uniform variables */
        //                      ~~~~~~~
        var a,b,c,d;
        a =  0;
        b =  5; // higher = more waves | 0.0 == the movement of circling
        c = 25; // higher = less distortion
        d =  2; // higher = faster
        
        // (a) time
        uni_time            = shader_get_uniform(SH_Wave , "time");
        var_time_var        = a;
        
        // (b) wave_amount
        uni_wave_amount     = shader_get_uniform(SH_Wave , "wave_amount");
        var_wave_amount     = b; // higher = more waves | 0.0 == Circling
        
        // (c) wave_distortion
        uni_wave_distortion = shader_get_uniform(SH_Wave , "wave_distortion");
        var_wave_distortion = c; // higher = less distortion
        
        // (d) wave_speed
        uni_wave_speed      = shader_get_uniform(SH_Wave , "wave_speed");
        var_wave_speed      = d; // higher = faster
    };

ユーザ定義関数 DrawGUI_Wave

///DrawGUI_Wave();
var a;
a = 0.01;// higher = faster | 0.00 == stop;
var_time_var += a;
    if shader_enabled {
        shader_set(SH_Wave);
            shader_set_uniform_f(uni_time,            var_time_var);
            shader_set_uniform_f(uni_wave_amount,     var_wave_amount);
            shader_set_uniform_f(uni_wave_distortion, var_wave_distortion );
            shader_set_uniform_f(uni_wave_speed,      var_wave_speed);
            var b,c;
            b = 1.0;
            c = 1.0;
                if os_type == os_android {
                    b = display_get_width()  / view_wview;
                    c = display_get_height() / view_hview;
                    if (b > c) b = c;
                    else       c = b;
                };
            draw_surface_ext(application_surface,0,0,b,c,0,0,0);
        shader_reset();
    };

ユーザ定義関数 DrawGUI_Show_Status

///DrawGUI_Show_Status();
if keyboard_check_pressed(ord("R")) room_restart();
/* ================================================ */
if keyboard_check(ord('Q')) var_wave_amount += 1;
if keyboard_check(ord('A')) var_wave_amount -= 1;

if keyboard_check(ord('W')) var_wave_distortion += 1;
if keyboard_check(ord('S')) var_wave_distortion -= 1;

if keyboard_check(ord('E')) var_wave_speed += 1;
if keyboard_check(ord('D')) var_wave_speed -= 1;

draw_set_colour(c_red);
draw_text(0, 0,"Real FPS: "                + string(fps_real));

draw_set_colour(c_yellow);
draw_text(0,30,"Wave amount     (Q & A): " + string(var_wave_amount));
draw_text(0,60,"Wave distortion (W & S): " + string(var_wave_distortion));
draw_text(0,90,"Wave speed      (E & D): " + string(var_wave_speed));
/* ================================================ */
draw_set_colour(c_green);
draw_text(0,room_height-30,"Shader SH_Wave");

実際のイベント処理

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

Create Event

Init_Shader_Wave();

Draw GUI Event

DrawGUI_Wave();
DrawGUI_Show_Status();

application_surface を対象としてシェーダでエフェクトをかけます。

Draw イベントで描画した画面を DrawGUI へ描画するという手順。シェーダで application_surface 全体を対象にする場合は DrawGUI のタイミングで描画するのが無難。

今回はテクスチャの UV 情報を扱うシェーダなので、フルスクリーンで動作するのがデフォルトとなっているモバイル系のアプリ向け処理が含まれています。

具体的には DrawGUI_Wave(); の

            var b,c;
            b = 1.0;
            c = 1.0;
                if os_type == os_android {
                    b = display_get_width()  / view_wview;
                    c = display_get_height() / view_hview;
                    if (b > c) b = c;
                    else       c = b;
                };
            draw_surface_ext(application_surface,0,0,b,c,0,0,0);

上記抜粋部分が該当します。

view_wview と display_get_width() 等の値が一致する場合にはこの処理が不要と成ります。

このサンプルのルームサイズは 縦横 512px となっているため、あるいはウィンドウモードで動作するアプリケーションの場合には倍率を測る処理が不要なので、倍率は 1.0 固定で済む。

しかしモバイルはデフォルトがフルスクリーン動作なので、ルームサイズがディスプレイの画面サイズよりも小さい場合には解像度に応じた拡縮処理が自動的に行われています。このためテクスチャが等倍のまま UV 情報をフラグメントに送ってしまうと、シェーダは等倍時の UV 情報に応じた処理を実行し、その結果得られたシェーダの描画結果は画面サイズよりも小さく、余白を生じてしまいます。




次へ

前へ