シェーダを利用する
Akashic Engine v3 は、限定的にフラグメントシェーダをサポートしています。 シェーダを使うには、g.ShaderProgram
を利用します。
凡例
var fragmentShader = scene.asset.getText["/text/shader.glsl"].data; // テキストアセットからフラグメントシェーダを取得
var shader = new g.ShaderProgram({
fragmentShader: fragmentShader
});
var sprite = new g.Sprite({
..., // その他のプロパティ
shaderProgram: shader // シェーダの指定
});
sprite.shaderProgram = shader; // 既存の sprite にシェーダを指定
sprite.modified(); // modified() で表示に反映
利用例
次のコンテンツは、画像をモノクロ化して表示します。
詳細
INFO
以下、 WebGL のシェーダについての知識が必要です。
Akashic Engine は現在、フラグメントシェーダを限定的に利用することができます。
TIP
バーテックスシェーダは非サポートで、attribute も固定です。uniform は利用できます。 したがって現状の用途は、画像に対する単純なフィルタなどに限られます。
シェーダを利用するには、まず game.json
の renderers
に webgl
を追加します。
"renderers": ["webgl"],
シェーダは g.ShaderProgram
で表されます。g.ShaderProgram
は g.E
やそれを継承したクラス (g.Sprite
, g.Pane
など) に適用できます。
シェーダプログラムは親エンティティの値を継承します。そのため、シェーダプログラムを適用したエンティティに複数の子エンティティに追加することで、複数のエンティティに同一のシェーダプログラムを適用することができます。
下記のコードでは、テキストアセット monochrome.glsl
を読み込み Pane
に指定しています。Pane
の子エンティティである Sprite
と FilledRect
にもシェーダが適応されます。
#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);
}
function main() {
var scene = new g.Scene({
game: g.game,
assetPaths: ["/image/player.png", "/text/monochrome.glsl"]
});
scene.onLoad.add(function () {
var fragmentShader = scene.asset.getText("/text/monochrome.glsl").data;
var shader = new g.ShaderProgram({
fragmentShader: fragmentShader
});
var pane = new g.Pane({
scene: scene,
width: 300,
height: 300,
shaderProgram: shader
});
var sprite = new g.Sprite({
scene: scene,
src: scene.asset.getImage("/image/player.png"),
x: 100,
y: 100
});
var rect = new g.FilledRect({
scene: scene,
width: 100,
height: 100,
x: 50,
cssColor: "red"
});
pane.append(sprite);
pane.append(rect);
scene.append(pane);
});
g.game.pushScene(scene);
}
module.exports = main;
.glsl 内で利用している uAlpha
, uSampler
, vTexCoord
は固定で与えられます。 それぞれ opacity を反映した透明度、元の描画内容 (画像や FilledRect
の単色) をテクスチャとして参照するサンプラー、そしてテクスチャ座標です。
設定したシェーダを戻したい場合、 undefined
または null
を指定します。 undefined
と null
は以下のように挙動が異なることにご留意ください。
undefined
: 親エンティティのシェーダプログラムを利用する。(デフォルト)null
: 親エンティティのシェーダプログラムを利用せず、常に初期のシェーダプログラム(undefined
)を利用する。
sprite.shaderProgram = null;
sprite.modified();
変数
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;
});