Convert Depth to Scale
{{< twitter >}}
インスタンスの描画サイズを depth に応じて頂点シェーダで変更する
GM:S のオブジェクト・インスタンスが持つ組み込み変数「depth」を利用し、奥行きに合わせてインスタンスのサイズを変更する。
今回はこれまで公開してきた他サンプルとは少しコンセプトが異なり、オブジェクトを二つ使っている点に注意。
オブジェクトは二つ使って、上記三つのイベントへ収まるように作った。無駄な変数定義を排除、処理をユーザ定義関数化しコードの使い回し簡単。
参考( 床井研究室 ) [OpenGL][GLSL] シェーダで Point Sprite
スペースキー押しで描画切り替え、シェーダ機能のオン・オフ
要 WebGL、Google Chrome/FireFox 等で動作を確認。
HTML5( WebGL ) と Windows PC で微妙に描画結果が異なる場合があります。シェーダー名:SH_DepthToScale
Vertex Shader
attribute vec3 in_Position; // (x,y,z)
attribute vec2 in_TextureCoord; // (u,v)
// 今回、頂点カラーは無視しています、必要な場合は追加
varying vec2 v_vTexcoord;
uniform float U_Depth;
void main()
{
//vec4 setvec = vec4(in_Position, 2.0 - U_Depth);//効果は別だがこれもあり
vec4 setvec = vec4(in_Position.xy * U_Depth, in_Position.z, 1.0);
gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * setvec;
v_vTexcoord = in_TextureCoord;
}
attribute vec3 in_Position; は vec3 の修飾子が示す通り、三つの要素が入っているベクトル、in_position.xyz
gl_Position は GLSL ES の Vertex シェーダ固有の組み込み変数、行列とベクトルを掛け合わせた変換の結果が格納される。
4✕4 の正方行列と掛け合わせるために用いられるベクトルも要素が四つ必要で、前述した attribute vec3 in_Position; では要素数が一個足りない。足りない要素が即ち .w である。
//vec4 setvec = vec4(in_Position, 2.0 - U_Depth);//効果は別だがこれもあり
vec4 setvec = vec4(in_Position.xy * U_Depth, in_Position.z, 1.0);
不足している .w 要素分はスカラー値で 1.0 ( あるいは 2.0 - U_Depth ) と代入してある。
上記二つの処理は頂点位置を編集してサイズを調整するか、.w の値で見た目を調整するか、どちらも表示されるグラフィックスの大きさに影響を与える処理という点で共通する。
Fragment Shader
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
void main()
{
gl_FragColor = texture2D( gm_BaseTexture, v_vTexcoord );
}
ユーザ定義関数 Ev_Init_Shader
///Ev_Init_Shader();
draw_set_font(font0);
var s,m;
s = SH_DepthToScale;
if ( !shader_is_compiled(s) ) {
m = "ur hardware ain't support shader - SH_DepthToScale";
show_message(m);
}
else{
draw_set_colour(c_white);
/* Create Instances */
var a,b,c,d,e,f;
a = 40; // how number repeats
b = 4; // range max value || 2 ~ x00;
c = 0; // range min
d = 100; // offset value
e = room_width - d;
f = room_height - d;
m = "U_Depth";
repeat a
{
with (instance_create(0, 0, robo))
{
xstart = random_range(c , e);
ystart = random_range(c , f);
x = xstart;
y = ystart;
depth = irandom_range(-b , b);
/* Instance Variables for Vertex Shader */
U_Depth = shader_get_uniform(s , m);
RoboDTS = Robo_DepthToScale(b , depth);
};
};
/* Instance Variable for Debug */
Debug = true;
};
ユーザ定義関数 Ev_Debug_Keys
///Ev_Debug_Keys();
if keyboard_check_released(ord("R")) room_restart();
if keyboard_check_released(vk_space) ev.Debug = !ev.Debug;
ユーザ定義関数 Robo_DepthToScale
///Robo_DepthToScale(range_max , robo.depth);
var a,b,c,d,e;
a = 100.0;
b = argument0;
c = (abs(argument1 - b) / a);
d = (b/a);
e = ((d - (d - c)) / d);// depth を視点からのキョリに置き換える
return e;
ユーザ定義関数 Robo_Draw_Shader
///Robo_Draw_Shader();
var a,b,c;
a = SH_DepthToScale;// Shader Name
b = RoboDTS; // Instance Variable
if ev.Debug {
shader_set(a);
shader_set_uniform_f( U_Depth , b );
draw_self();
draw_text(x,y, string(b));
shader_reset();
}
else{
draw_self();
draw_text(x,y, string(b));
};
イベント処理
シェーダと4つのユーザ定義関数を使って簡潔なイベントを作成。
Create Event ( ev.Object )
///Init
Ev_Init_Shader();
オブジェクト “robo” のインスタンスを 40 個作成。
repeat ステートメント以降 with ステートメントを使ってインスタンスの初期配置を終える、と同時に今回の描画で必要となるインスタンス変数など必要なネタを仕込む。
GM:S はインスタンス同士の描画的な重なりを depth で制御している。draw イベントにおける draw の実行順も、インスタンスが複数ある場合には depth に適用された値でソートされ、これで描画の実行順が決まる。
depth は値が小さいほど手前へ、値が大きければ奥へ描画される。つまり depth で大きな値を割り当てられたインスタンスは draw の実行順では一番最初に実行され、徐々に小さい値を持つインスタンスが次々と順番で描画されていく。
プラス値( 大きな値 ) | ゼロ | マイナス値( 小さな値 ) |
---|---|---|
奥へ描画 | デフォルト | 手前へ描画 |
Draw 実行順の最初 | デフォルト | Draw 実行順の最後 |
インスタンスが二つあった場合、depth が -100 と depth が 100 のオブジェクト・インスタンスならば、Draw の実行順は depth == 100 のインスタンスが先、 depth == -100 のインスタンスは後。
スプライトは描画位置が重なっている場合、depth == -100 のインスタンスが手前に描画され、depth == 100 のインスタンスは奥に隠れたように描画される。
2D なので描画に奥行きは無いが、スプライトの重なりは depth で制御されている。depth の値を使って描画スケールを調整することによって、2D の画面に奥行きが発生するような擬似 3D 的表現を頂点シェーダを用いて実現する。
シェーダ使わなくともスプライトの拡縮描画は可能だが、シェーダ使ったほうが恐らく GM:S の標準よりも処理速度的には速い。特にインスタンス数が多い場合には GPU 処理の方が効率良く捌けるであろうことを期待している。
Step Event ( ev.Object )
///Step
Ev_Debug_Keys();
Draw Event ( robo.Object )
///Draw
Robo_Draw_Shader();
次へ
前へ