🐙

HTML5GameでWebGLシェーダーを試しまくる

2020/11/05に公開

シェーダーとは…という解説はプロにお任せして、
とにかくHTML5Game(Phaser)上でWebGLシェーダーを気楽に楽しみます。

サンプルプロジェクトですぐに試す

こちらのPhaser3 + TypeScriptプロジェクトテンプレートがすぐに試せる状態です。
ありがたく使わせてもらいましょう。

template

背景のワープ演出(starfields.glsl.js)と、下部のネオン効果(plasma-bundle.glsl.js)がシェーダーによるものです。

サンプルプロジェクトをちょっと変更する

サンプルプロジェクトにて利用できるシェーダーは他にもあります。
例えばdist/assets/starfields.glsl.js内にシェーダーがいくつかあります。
試しにLayer Starfieldを動かします。src/game.tsで呼び出しているシェーダー名を変更します。

game.ts
        this.add.shader('Layer Starfield', 0, 0, 800, 600).setOrigin(0);

星に還れそうな感じがcoolですね。

世界中のShaderを試す

ShaderToyというサイトにてシェーダー作品の力作が投稿されています。
これがPhaserで作ったGame上で動いたら最高です。

まずは炎シェーダーをPhaser上で動かしてみます。
サンプルのglsl.jsの書き方を参考にassets/fire.glsl.jsファイルを作成します。

fire.glsl.jsコード
fire.glsl.js
---
name: Flame
type: fragment
author: XT95 https://www.shadertoy.com/view/MdX3zr
---

precision highp float;

uniform float time;
uniform vec2 resolution;

#define iTime time
#define iResolution resolution

// 以下はShaderToyのページからのコピーコードとなります。
float noise(vec3 p) //Thx to Las^Mercury
{
// 省略
	vec3 i = floor(p);
mix(vec4(1.,.5,.1,1.),vec4(0.1,.5,1.,1.),p.y*.02+.4), pow(glow*2.,4.));

}
// コピーここまで

void main(void)
{
    mainImage(gl_FragColor, gl_FragCoord.xy);
}

ファイルを追加したらあとはメインで呼び出します。

game.tsコード
game.ts
export default class Demo extends Phaser.Scene
{
    constructor ()
    {
        super('demo');
    }

    preload ()
    {
        this.load.image('logo', 'assets/phaser3-logo.png');
        this.load.image('libs', 'assets/libs.png');
        this.load.glsl('bundle', 'assets/plasma-bundle.glsl.js');
        // this.load.glsl('stars', 'assets/starfields.glsl.js');
        this.load.glsl('fire', 'assets/fire.glsl.js'); // 追記
    }

    create ()
    {
        this.add.shader('Flame', 0, 0, 800, 600).setOrigin(0); //変更

        this.add.shader('Plasma', 0, 412, 800, 172).setOrigin(0);

        this.add.image(400, 300, 'libs');

        const logo = this.add.image(400, 70, 'logo');

        this.tweens.add({
            targets: logo,
            y: 350,
            duration: 1500,
            ease: 'Sine.inOut',
            yoyo: true,
            repeat: -1
        })
    }
   }

const config = {
    type: Phaser.AUTO,
    backgroundColor: '#000000', // 背景色合成が汚くなったので黒にします
    width: 800,
    height: 600,
    scene: Demo
};

良い感じにあぶられてますね!

もう一つ動かしてみます。カッコいい海のShaderです。

大きめなglslコードですが、うまく動きます。
こちらには#define iMouse mouseが必要でした。
動画ではなく海の表現をShaderでやるのはすごいですね。
完全なコードはGitHubに上げました。

ShaderToyのglslコードをPhaserで動かすポイント

サンプルを見る感じ、Phaserで動かすためには3箇所追記が必要です。

  • Phaerから呼び出すシェーダー名称(name/type/author)部分
  • 外部パラメーター定義(percision/uniform)と変数名の変換(#define)
    • シェーダーによってはマウス位置パラメータがある等、少々異なる場合あり
  • main関数の追加(mainからmainImageを呼ぶ)

ほとんど機械的に移植できると思います。

終わり

今回はダイナミックなShaderの作品をPhaserで動かしましたが、実際のGame開発でのShader利用は演出目的でスプライトや特定の画面へのフィルタ効果の方が多いと思います。(多分)

次はもう少し実戦よりのShaderの活用方法を記事としてまとめたいです。

その他補足

紹介したサンプルプロジェクトはPhaser + TypeScript環境のベースとして使えます。
ただ、自分の環境ではビルドが遅くてイマイチな感じでした。(rollup.jsのせい?)

Phaer + TypeScript開発環境をいくつか調査しましたが
、ビルドにwebpackを利用するこちらプロジェクトの構成が良さそうです。素直に動きませんでしたが。
この環境を使った日本語記事はこちらの記事が参考になりました。

Discussion