GameMaker:Studio Shader オーバーレイ(Overlay)
PhotoShop の描画モードにある「乗算」と「スクリーン」とを掛け合わせた効果を持つ「オーバーレイ」を GLSL ES で再現。
オーバーレイは暗いところは暗く、明るいところは明るくと、上記二つのモードを合わせたような効果を持つ。これを GameMakerStudio の Shader として GLSL ES で再現する。
イベントを一つのオブジェクトへまとめるようにし、
- Create
- Alarm
- Draw
上記の三つのイベントへ収まるように作った。そして無駄な変数定義を可能な限り排除。
なるべくユーザ定義関数化し、コードの使い回しが簡単。
参考動作:http://prester.org/html5/gms_shader_overlay/
参考( YoYoGames旧フォーラム: ) Trying to do a photoshop effect over a …
参考:Photoshop math with GLSL shaders
参考:Photoshop Blendmodi in GLSL
キーボードの Q と A キーで値変更可能。
値は 0 〜 1 までの範囲で制限。要 WebGL、Google Chrome/FireFox 等で動作を確認。 スペースキーでシェーダ効果のオンオフ切り替え可能。
HTML5( WebGL ) と Windows PC で描画結果が異なる場合があります。古いグラフィックスドライバ上でオーバーレイの色が適用されないケースを確認済み。Android でも動きます。
グラフィックスリソースが複数必要に成ります。最低でも背景とその上へレイヤー的に重ねるシャドウマップ的な画像が必要。ただし単純なグラデーションならば予め用意しなくとも、Surface を使って Shader でグラデーションマップを動的に作ることなども可能。 今回はリソースツリーに背景として以下画像を利用しています。
値は 0 〜 1 までの範囲で制限。要 WebGL、Google Chrome/FireFox 等で動作を確認。 スペースキーでシェーダ効果のオンオフ切り替え可能。
HTML5( WebGL ) と Windows PC で描画結果が異なる場合があります。古いグラフィックスドライバ上でオーバーレイの色が適用されないケースを確認済み。Android でも動きます。
グラフィックスリソースが複数必要に成ります。最低でも背景とその上へレイヤー的に重ねるシャドウマップ的な画像が必要。ただし単純なグラデーションならば予め用意しなくとも、Surface を使って Shader でグラデーションマップを動的に作ることなども可能。 今回はリソースツリーに背景として以下画像を利用しています。
シェーダー名:SH_Overlay
Vertex Shader
attribute vec3 in_Position; // (x,y,z)
attribute vec4 in_Colour; // (r,g,b,a)
attribute vec2 in_TextureCoord; // (u,v)
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
void main()
{
gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * vec4(in_Position, 1.0);
v_vColour = in_Colour;
v_vTexcoord = in_TextureCoord;
}
Fragment Shader
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
uniform sampler2D texOverlay;//foreground colour ( luminance value )
float val = 2.0;// f(a, b) is 2ab
float num = 1.0;// 1 - 2(1 - a)(1 - b)
float cut = 0.5;// if a < 0.5
//luminance is 0.5 and ignore the linear blend
void main()
{
vec4 inColor = v_vColour * texture2D(gm_BaseTexture, v_vTexcoord);
vec4 overlay = texture2D(texOverlay, v_vTexcoord);
vec3 aa = ((val * inColor.xyz) * overlay.xyz);// 2.0 * a * b
vec3 bb = (num - ((val * (num - inColor.xyz))*(num - overlay.xyz)));
gl_FragColor = vec4(mix(aa,bb, floor(inColor.xyz + cut)), inColor.a);
//gl_FragColor = vec4(mix(aa,bb, floor(inColor.xyz - cut)), inColor.a);
//gl_FragColor = vec4(mix(bb,aa, floor(inColor.xyz - cut)), inColor.a);
//gl_FragColor = vec4(mix(aa,bb, abs(inColor.xyz + cut)), inColor.a);
//上記三行は派生効果としてのサンプル、それぞれ微妙に描画結果は異なる
}
ユーザ定義関数 Init_Shader_Overlay
///Init_Shader_Overlay();
if (!shader_is_compiled(SH_Overlay)) {
show_message("Your hardware doesn't support shader - SH_Overlay");
}
else{
/*
For Debug
*/
Debug = true;
Value = 0.5;
/*
For Shader
*/
stage = shader_get_sampler_index(SH_Overlay, "texOverlay");
Rotate = 0;
SRotate = 360;
//alarm[0] = HOLY;//alarm_set(0, HOLY);
/*
For Surfaces
*/
Surf_Copy = noone;
Surf = noone;
};
ユーザ定義関数 DrawEv_Shader_Overlay
///DrawEv_Shader_Overlay();
draw_background_stretched(background,0,0,room_width,room_height);
var a,b,c,d,e,f, g,h,j, z;
a = room_width>>1;
b = room_height>>1;
c = .2
d = 360;
e = 1.;
f = Value;
Rotate++;
SRotate -= c;
if Rotate == d Rotate = 0;
if SRotate == 0 Rotate = d;
g = d-Rotate;
h = d-SRotate;
j = alarm[0];//alarm_get(0);// alarm_get関数はHTML5でまだ未実装
//---------------------------------------------------------------
/*
Surf_Copy へ Overlay 用の bg_grad を描画する手順について
surface_get_texture は background_get_textureでもポインタ値を得られる。
ただし、その場合はリソースツリー上の bg_grad に対する個別の設定項目として
Used for 3D チェックオプションを有効にすることが必須と成る。
分かり難い設定項目で、トラブルを避ける意味でも surface 使った方が無難と考える
*/
if !surface_exists(Surf_Copy) {
Surf_Copy = surface_create(room_width,room_height);
surface_set_target(Surf_Copy);
draw_background(bg_grad,0,0);
surface_reset_target();
}
else{
if Debug {
shader_set(SH_Overlay);
texture_set_stage(stage, surface_get_texture(Surf_Copy));
/*
Surface 上の画像をオーバレイに利用して
二枚の絵を合成した結果を application_surface 上へ描画する
*/
draw_surface_ext(application_surface, 0,0, e,e,0,-1, f);
shader_reset();
};
};
//---------------------------------------------------------------
/*
ブレンドモードに関する覚書
draw_set_blend_mode_ext(bm_src_alpha, bm_one); == draw_set_blend_mode(bm_add);
draw_set_blend_mode_ext(bm_src_alpha, bm_inv_src_alpha); // == bm_normal
draw_set_blend_mode_ext(bm_src_alpha, bm_one); // == bm_add
draw_set_blend_mode_ext(bm_zero, bm_inv_src_colour); // == bm_subtract
draw_set_blend_mode_ext(bm_src_alpha, bm_inv_src_colour); // == bm_max
bm_zero (0, 0, 0, 0)
bm_one (1, 1, 1, 1)
bm_src_colour (Rs, Gs, Bs, As)
bm_inv_src_colour (1-Rs, 1-Gs, 1-Bs, 1-As)
bm_src_alpha (As, As, As, As)
bm_inv_src_alpha (1-As, 1-As, 1-As, 1-As)
bm_dest_alpha (Ad, Ad, Ad, Ad)
bm_inv_dest_alpha (1-Ad, 1-Ad, 1-Ad, 1-Ad)
bm_dest_colour (Rd, Gd, Bd, Ad)
bm_inv_dest_colour (1-Rd, 1-Gd, 1-Bd, 1-Ad)
bm_src_alpha_sat (f, f, f, 1) where f = min(As, 1-Ad)
*/
draw_set_blend_mode(bm_add);// 描画を加算モードへ変更
z = random_range(.3 , f);
draw_sprite_ext(ef_starnoize,0,a,b,e,e,SRotate,-1, z);
draw_sprite_ext(ef_light,0,a,b,e,e,g,-1,1);
//---------------------------------------------------------------
if !surface_exists(Surf) {
Surf = surface_create(room_width,room_height);
alarm[0] = HOLY;// HOLY はマクロに登録された定数
}
else{
surface_set_target(Surf);
if (j > 0){
draw_sprite_ext(ef_logo_base,0,a,b,e,e,h,-1,.6);
};
surface_reset_target();
draw_surface(Surf,0,0);// Draw to Application_surface
};
//---------------------------------------------------------------
z = random_range(f , .8);
if !j draw_sprite_ext(ef_logo_base,0, a,b,e,e, SRotate,-1, z);
draw_sprite_ext(ef_lihgtstar,0, a,b,e,e, 180-Rotate, -1, 1);
draw_set_blend_mode(bm_normal);// モードをノーマルへ戻す
draw_sprite(logo_body,0,a,b);
ユーザ定義関数 Show_Status
///Show_Status();
draw_set_colour(c_red);
draw_text(10,10,"Draw_HolyLight_TEST");
draw_text(10,60,"Shader Overlay TEST");
/*
draw_set_colour(c_lime);
draw_text(10,room_height-200,string(alarm_get(0)));
*/
draw_set_colour(c_purple);
draw_text(10,room_height-150,"Press Q & A, Ajust Overlay");
draw_set_colour(c_yellow);
draw_text(10,room_height-100, "press Space key, toggle Shader");
draw_set_colour(c_blue);
draw_text(10,room_height-50, "press R key, Restart the Room");
if keyboard_check(ord("Q")) Value += 0.01;
if keyboard_check(ord("A")) Value -= 0.01;
Value = clamp(Value, .0 , 1.);
if keyboard_check_pressed(vk_space) Debug = !Debug;
if keyboard_check_pressed(ord("R")) room_restart();
実際のイベント処理
シェーダと4つのユーザ定義関数を使って簡潔なイベントを作成。
Create Event
draw_set_font(font0);
draw_enable_alphablend(true);
Init_Shader_Overlay();
Alarm 0
///Alarm[0]
//show_message("TE");
Draw Event
DrawEv_Shader_Overlay();
Show_Status();
application_surface を対象としてシェーダでエフェクトをかけます。利用されている Surface はオーバレイ用のマップです。
シェーダで application_surface 全体を対象にする場合は DrawGUI のタイミングで描画するのが無難だけど、今回は Draw Event のタイミングで実行してます。
派生効果を作りやすいシェーダ。マップを工夫したり、フラグメント内のロジックを変更したりで色々楽しめます。
次へ
前へ