Skip to content

API リファレンス: RuntimeEffect & SkSL

RuntimeEffectSkSL (Skia Shading Language) へのゲートウェイです。SkSL は GPU 上で直接実行されるカスタムフラグメントシェーダーを記述できる強力な言語です。

SkSL を学ぶ

SkSL は GLSL と非常に似ていますが、すべての Skia バックエンド間での移植性のために最適化されています。

  • 公式 SkSL ドキュメント: SkSL の構文と機能の決定版ガイド。
  • Skia Fiddle: ブラウザで SkSL コードを記述してテストできるインタラクティブなプレイグラウンド。
  • The Book of Shaders: GLSL 向けに書かれていますが、概念とコードのほとんどは SkSL に直接適用できます。

SkSL スクリプトの読み込み

Java で SkSL を文字列としてハードコードすることも可能ですが、シンタックスハイライトと保守性を向上させるために、シェーダーは別の .sksl ファイルに保存する方がはるかに優れています。

推奨パターン

java
public class ShaderLoader {
    public static Shader loadShader(String path) throws IOException {
        String sksl = Files.readString(Path.of(path));
        RuntimeEffect effect = RuntimeEffect.makeForShader(sksl);
        return effect.makeShader(null);
    }
}

SkSL の記述

シェーダー SkSL は main 関数を持たなければなりません。

glsl
// my_shader.sksl
uniform float iTime;
uniform vec2  iResolution;

vec4 main(vec2 fragCoord) {
    vec2 uv = fragCoord / iResolution;
    return vec4(uv.x, uv.y, sin(iTime) * 0.5 + 0.5, 1.0);
}

重要な考慮事項

座標

main に渡される fragCoordローカルキャンバス座標です。正規化された UV (0.0 から 1.0) が必要な場合は、解像度をユニフォームとして渡し、自分で座標を割る必要があります。

精度

  • float: 32 ビット浮動小数点数。
  • half: 16 ビット浮動小数点数。モバイル GPU のパフォーマンスを向上させるために、色や単純なエフェクトには half を使用します。

事前乗算済みアルファ

Skia はシェーダーが事前乗算済みアルファ形式で色を返すことを期待します。1.0 未満のアルファを返す場合は、R、G、B コンポーネントにそのアルファ値を乗算する必要があります。

glsl
vec4 main(vec2 p) {
    float alpha = 0.5;
    vec3 color = vec3(1.0, 0.0, 0.0); // 赤
    return vec4(color * alpha, alpha); // 正しい事前乗算済みアルファ
}

シェーダーのアニメーション化 (ユニフォーム)

シェーダーをアニメーション化するには、SkSL コードで uniform 変数を宣言し、Java から毎フレーム更新します。

1. SkSL コード

glsl
// rainbow.sksl
uniform float iTime;
uniform float iWidth;
uniform float iHeight;

vec4 main(vec2 fragCoord) {
    // 座標を 0..1 に正規化
    vec2 uv = fragCoord / vec2(iWidth, iHeight);
    
    // 動く虹のパターンを作成
    float r = sin(uv.x * 6.28 + iTime) * 0.5 + 0.5;
    float g = sin(uv.y * 6.28 + iTime + 2.0) * 0.5 + 0.5;
    float b = sin((uv.x + uv.y) * 6.28 + iTime + 4.0) * 0.5 + 0.5;
    
    return vec4(r, g, b, 1.0);
}

2. Java コード

ユニフォーム値を渡すには Data または ByteBuffer を使用します。順序は SkSL での宣言順と一致しなければなりません。

java
// エフェクトを一度コンパイル
RuntimeEffect effect = RuntimeEffect.makeForShader(skslCode);

// アニメーションループ内:
long now = System.nanoTime();
float time = (now - startTime) / 1e9f;

// ユニフォーム用のバッファを作成: 3 floats * 4 bytes = 12 bytes
// Skija はユニフォームにリトルエンディアンのバイト順を期待
try (Data uniforms = Data.makeFromBytes(ByteBuffer.allocate(12)
        .order(ByteOrder.LITTLE_ENDIAN)
        .putFloat(time)          // iTime
        .putFloat(500f)          // iWidth
        .putFloat(500f)          // iHeight
        .array())) 
{
    // 更新されたユニフォームで新しいシェーダーを作成
    try (Shader shader = effect.makeShader(uniforms, null, null)) {
        Paint p = new Paint().setShader(shader);
        canvas.drawPaint(p); // 画面を塗りつぶす
    }
}

RuntimeEffectBuilder

ユニフォーム用にバイト配列を手動でパックするのはエラーが発生しやすいです。RuntimeEffectBuilder は、名前でユニフォームを設定できるようにすることでこれを簡素化します。

java
RuntimeEffect effect = RuntimeEffect.makeForShader(sksl);
RuntimeEffectBuilder builder = new RuntimeEffectBuilder(effect);

// 名前でユニフォームを設定 (型安全)
builder.setUniform("iTime", 1.5f);
builder.setUniform("iResolution", 800f, 600f);
builder.setUniform("iColor", new float[] { 1, 0, 0, 1 }); // vec4

// Shader/ColorFilter/Blender を作成
Shader shader = builder.makeShader();