シェーダの利用

これは

シェーダプログラムの利用方法を記述したドキュメントです。

対象バージョン

本ドキュメントの対象バージョンは akashic-engine@2.3.2 以降です。

また、本機能を利用するには WebGL 1.0 が利用可能な環境が必要です。 こちらのページ で現在利用しているブラウザが WebGL をサポートしているか確認することができます。

シェーダとは

WebGL, OpenGL などで、3 次元空間に存在する情報を 2 次元空間に描画するために用いられているプログラムのことを指します。詳細については他のドキュメントを参照ください。

本ドキュメントでは Akashic でシェーダを利用する方法について記述します。 akashic-engine@2.3.2 ではフラグメントシェーダのみをサポートします。

準備

ゲーム開発の準備が終わっていない場合は Akashic を利用したゲーム開発 を読んでゲーム開発の環境を整えて下さい。

本ドキュメントの内容は akashic-sandbox@0.13.24 以降のバージョンでサポートされています。手元の akashic-sandbox のバージョンがこれより古い場合は以下のコマンドで最新のものをインストールしてください。

npm i @akashic/akashic-sandbox -g

コンテンツで WebGL を有効にするため、 game.jsonrendererswebgl を追加します。詳細は game.json の仕様 を参照してください。

利用

まずはゲーム内でシェーダを利用するための準備をします。

以下は出力色をモノクロ化するフラグメントシェーダの記述になります。

#version 100
precision mediump float;

varying vec2 vTexCoord;
uniform sampler2D uSampler;
uniform float uAlpha;

void main(void)
{
  // 対象ピクセルの色情報を取得
  vec4 color = texture2D(uSampler, vTexCoord);

  // 対象ピクセルのRGB値を加算
  float sum = dot(color.rgb, vec3(1.0));

  // モノクロ化
  vec3 outColor = vec3(sum / 3.0);

  // 最終出力色
  gl_FragColor = vec4(outColor, color.a * uAlpha);
}

このシェーダをコンテンツ内で利用するために、 g.ShaderProgram を生成します。 (注意: この生成方法は akashic-engine@2.3.2 時点での記述になります。今後のバージョンでは変更される可能性があります。)

// フラグメントシェーダのテキストデータ
const fragmentShader =
  "#version 100\n" +
  "precision mediump float;\n" +
  "varying vec2 vTexCoord;\n" +
  "uniform sampler2D uSampler;\n" +
  "uniform float uAlpha;\n" +
  "void main(void)\n" +
  "{\n" +
  "  vec4 color = texture2D(uSampler, vTexCoord);\n" +
  "  float sum = dot(color.rgb, vec3(1.0));\n" +
  "  vec3 outColor = vec3(sum / 3.0);\n" +
  "  gl_FragColor = vec4(outColor, color.a * uAlpha);\n" +
  "}";

const shader = new g.ShaderProgram({
  fragmentShader: fragmentShader
});

g.ShaderProgramg.E やそれを継承したクラス (g.Sprite , g.Pane など) に適用できます。以下のようにコンストラクタのパラメータに指定することができます。

var sprite = new g.Sprite({
  scene: scene,
  src: scene.assets["sample"],
  width: 100,
  height: 100,
  shaderProgram: shader
});

既存の g.Sprite に適用することもできます。その場合は変更後に g.Sprite#modified() を呼ぶ必要があります。

sprite.shaderProgram = shader;
sprite.modified();

シェーダプログラムは親エンティティの値を継承します。そのため、シェーダプログラムを適用したエンティティに複数の子エンティティに追加することで、複数のエンティティに同一のシェーダプログラムを適用することができます。

設定したシェーダを戻したい場合、 undefined または null を指定します。 undefinednull は以下のように挙動が異なることにご留意ください。

  • undefined
    • 親エンティティのシェーダプログラムを利用する。(デフォルト)
  • null
    • 親エンティティのシェーダプログラムを利用せず、常に初期のシェーダプログラム( undefined )を利用する。

注意点として、 g.ShaderProgram は一度生成したら以降プロパティを変更することはできません。 (例外的に g.ShaderProgram#uniforms#value のみ変更が許可されています。詳細は後述します。)

変数

akashic-engine@2.3.2 時点では、以下の変数がエンジン側から暗黙的に与えられます。

  • varying
    • vec2 vTexCoord
      • 対象の描画元のテクスチャ座標
      • 本値は 0 ~ 1.0 の範囲内で任意の矩形領域となり得ることに注意。
  • uniform
    • sampler2D uSampler
      • 対象の描画元のテクスチャ番号
    • float uAlpha
      • 透過度 (0 ~ 1.0 の範囲内)

ユーザ定義変数

ユーザが独自に定義した変数をフラグメントシェーダに与えることができます。

先程のサンプルにおいて、各色の重み付けをコンテンツから指定した例が以下になります。

#version 100
precision mediump float;

varying vec2 vTexCoord;
uniform sampler2D uSampler;
uniform float uAlpha;

uniform float redScale;
uniform float greenScale;
uniform float blueScale;

vec3 monoScale = vec3(redScale, greenScale, blueScale);
float monoScaleSum = dot(monoScale, vec3(1.0));

void main(void)
{
  // 対象ピクセルの色情報を取得
  vec4 color = texture2D(uSampler, vTexCoord);

  // 対象ピクセルのRGB値を加算
  float sum = dot(color.rgb, monoScale);

  // モノクロ化
  vec3 outColor = vec3(sum / monoScaleSum);

  // 最終出力色
  gl_FragColor = vec4(outColor, color.a * uAlpha);
}

redScale , greenScale , blueScale が uniform 値として渡されるようにフラグメントシェーダを修正しています。

g.ShaderProgram の生成時に利用する uniform を定義します。

const shader = new g.ShaderProgram({
  fragmentShader: fragmentShader,
  uniforms: {
    redScale: {
      type: "float",
      value: 0.299
    },
    greenScale: {
      type: "float",
      value: 0.587
    },
    blueScale: {
      type: "float",
      value: 0.114
    }
  }
});

uniform は、フラグメントシェーダで利用する際の変数名をキーとしたオブジェクトによって定義されます。各キーは g.ShaderUniform によって定義された形式で記述する必要があります。 type は対象の uniform の型、valueはその値を示します。

type には以下が指定できます。

  • float
  • int
  • vec2
  • vec3
  • vec4
  • ivec2
  • ivec3
  • ivec4
  • mat2
  • mat3
  • mat4

また、以下のように Array を利用することもできます。

フラグメントシェーダ

uniform param[3];

コンテンツ

const shader = new g.ShaderProgram({
  fragmentShader: fragmentShader,
  uniforms: {
    param: {
      type: "float",
      value: [0.0, 1.0, 0.4]
    }
  }
});

g.ShaderProgram#uniform#value の値はプログラムの実行中に任意に変更できます。

scene.update.add(() => {
  shader.uniforms.time.value = scene.game.age;
});