pixijs-custom-rendering について
PixiJS v8でカスタムレンダリングを行う開発者向けのスキルです。シェーダー、UniformGroup、フィルター、WebGLとWebGPUの実装パターンや設定上の注意点を扱います。
主な機能
- 01GLSLとWGSLのシェーダー作成
- 02UniformGroupとテクスチャリソースの設定
- 03カスタムフィルターの作成
- 04UBOによるuniform管理
- 05カスタムバッチ処理とGPU機能
活用シーン
- 01シーンオブジェクト用のシェーダー作成
- 02フラグメントシェーダーによるフィルター実装
- 03WebGLとWebGPUへのレンダリング対応
- 04uniform、UBO、テクスチャ設定の調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ pixijs/ pixijs-skills --skill pixijs-custom-renderingインストール後、Claude Code で/pixijs-custom-renderingを入力して読み込まれたか確認します。
同梱ファイルreferences/advanced-gpu.mdreferences/uniform-types.md
詳細
カスタムシェーダーは、Shader.from({ gl, gpu, resources }) を介して GLSL および WGSL のプログラムをシーンオブジェクトにバインドします。ユニフォームは型付きの UniformGroup に格納し、テクスチャは別個のリソースとして渡します。同じシェーダーを WebGL と WebGPU の両方で使用できます。
クイックスタート
const uniforms = new UniformGroup({
uTime: { value: 0, type: "f32" },
});
const shader = Shader.from({
gl: { vertex: vertexSrc, fragment: fragmentSrc },
resources: { uniforms },
});
const geometry = new MeshGeometry({
positions: new Float32Array([0, 0, 100, 0, 100, 100, 0, 100]),
uvs: new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]),
indices: new Uint32Array([0, 1, 2, 0, 2, 3]),
});
const mesh = new Mesh({ geometry, shader });
app.stage.addChild(mesh);
app.ticker.add(() => {
shader.resources.uniforms.uniforms.uTime = performance.now() / 1000;
});
関連スキル: pixijs-filters(組み込みフィルター)、pixijs-scene-mesh(カスタムジオメトリ)、pixijs-performance(バッチ最適化)、pixijs-migration-v8(v7 からのシェーダー API の移行)。
主なパターン
デュアルレンダラーシェーダー(WebGL + WebGPU)
import { Shader, GlProgram, GpuProgram, UniformGroup } from "pixi.js";
const glVertex = `...`; // GLSL vertex (write `#version 300 es` yourself if you want WebGL2/GLSL ES 3.0)
const glFragment = `...`; // GLSL fragment
const wgslSource = `...`; // WGSL combined
const shader = Shader.from({
gl: { vertex: glVertex, fragment: glFragment },
gpu: {
// entryPoint names are arbitrary; they must match the @vertex / @fragment
// function names in your WGSL source. PixiJS ships examples using
// 'mainVert' / 'mainFrag' but `main` is equally valid.
vertex: { entryPoint: "mainVert", source: wgslSource },
fragment: { entryPoint: "mainFrag", source: wgslSource },
},
resources: {
myUniforms: new UniformGroup({
uColor: { value: new Float32Array([1, 0, 0, 1]), type: "vec4<f32>" },
uMatrix: { value: new Float32Array(16), type: "mat4x4<f32>" },
}),
},
});
gl のみを指定した場合、シェーダーは WebGL 専用です。gpu のみを指定した場合は、WebGPU 専用です。compatibleRenderers のビットマスクは自動的に設定されます。
GlProgram は #version 300 es を自動挿入しません。#version 300 es を自分で記述した場合、PixiJS はそれを保持し、シェーダーを GLSL ES 3.0 として扱います。記述しない場合は、WebGL1 互換マクロ(#define in varying、#define texture texture2D)を挿入し、シェーダーを WebGL1 形式の GLSL として実行します。GlProgram は常にデフォルトの精度(頂点シェーダーは highp、フラグメントシェーダーは mediump)とプログラム名を挿入します。また、GLSL ES 3.0 でデフォルト精度が指定されていないサンプラー型(usampler2D、isampler2D、sampler2DArray、sampler3D など)には highp を宣言するため、これらに精度指定の行は不要です。自分で記述した場合は、その指定が保持されます。sampler2D と samplerCube には、デフォルトの lowp が適用されます。GLSL ES 3.0 では、attribute/varying の代わりに in/out を、texture2D() の代わりに texture() を、gl_FragColor の代わりに out vec4 を使用してください。
リソースとしてのテクスチャ
テクスチャはユニフォームではなく、リソースです。テクスチャの source と style を別々に渡してください。
import { Shader, UniformGroup, Texture, Assets } from "pixi.js";
const texture = await Assets.load("myImage.png");
const shader = Shader.from({
gl: { vertex: vertSrc, fragment: fragSrc },
resources: {
uTexture: texture.source,
uSampler: texture.source.style,
myUniforms: new UniformGroup({
uAlpha: { value: 1.0, type: "f32" },
}),
},
});
// Swap texture at runtime
shader.resources.uTexture = otherTexture.source;
リソースはフラットなキーと値のマップです。キーはシェーダーソース内のユニフォーム名/バインディング名と一致する必要があります。
リソースにはプレーンオブジェクトも指定できます(自動的に UniformGroup にラップされます)。
const shader = Shader.from({
gl: { vertex: vertSrc, fragment: fragSrc },
resources: {
myUniforms: {
uTime: { value: 0, type: "f32" },
},
},
});
UBO モード(ユニフォームバッファーオブジェクト)
UBO モードでは、ユニフォームを単一の GPU バッファーにまとめます。WebGPU では必須で、WebGL(WebGL2 以降)では任意です。
import { UniformGroup } from "pixi.js";
const ubo = new UniformGroup(
{
uProjection: { value: new Float32Array(16), type: "mat4x4<f32>" },
uAlpha: { value: 1.0, type: "f32" },
},
{ ubo: true, isStatic: true },
);
// Must call update() manually when isStatic is true
ubo.uniforms.uAlpha = 0.5;
ubo.update();
UBO のルール:
- サポートされるのは
f32およびi32ベースの型のみです(u32は不可)。行列は浮動小数点型のみです。 - サンプラー/テクスチャは UBO に格納できません。
- リソース内の UniformGroup 名は、シェーダー内の UBO ブロック名と完全に一致させる必要があります。
- 構造と順序は、シェーダーのレイアウトと完全に一致させる必要があります。
- UBO の同期では内部で
new Functionを使用します。厳格な CSP 環境(unsafe-evalなし)では、起動時に一度pixi.js/unsafe-evalをインポートして、フォールバック同期経路に切り替えてください。これを行わないと、UBO を使用するシェーダー(したがって WebGPU も)は初回使用時に例外をスローします。
カスタムフィルター
Filter.from({ gl, resources }) は簡略記法です。フラグメントシェーダーのみを渡してください。PixiJS が出力フレームの位置を処理するデフォルトの頂点シェーダーを用意します。
import { Filter } from "pixi.js";
const filter = Filter.from({
gl: {
fragment: `
in vec2 vTextureCoord;
out vec4 finalColor;
uniform sampler2D uTexture;
uniform float uStrength;
void main(void) {
vec4 color = texture(uTexture, vTextureCoord);
finalColor = mix(color, vec4(1.0 - color.rgb, color.a), uStrength);
}
`,
},
resources: {
filterUniforms: {
uStrength: { value: 0.5, type: "f32" },
},
},
});
filter.resources.filterUniforms.uniforms.uStrength = 1.0;
カスタム頂点シェーダーを使用する場合は、new Filter({ glProgram: new GlProgram({ vertex, fragment }), resources }) を使用してください。
フィルターシェーダーの規約(GLSL ES 3.0)
varying vec2 vTextureCoord;の代わりにin vec2 vTextureCoord;を使用します。gl_FragColorの代わりにout vec4 finalColor;を使用します。texture2D(uTexture, uv)の代わりにtexture(uTexture, uv)を使用します。- デフォルトの頂点シェーダーでは、
uInputSize、uOutputFrame、uOutputTextureと、ヘルパー関数filterVertexPosition()/filterTextureCoord()を利用できます。
フィルターの背後にあるレンダーターゲットのサンプリング
blendRequired: true を設定し、フラグメントシェーダーで uBackTexture をサンプリングします。PixiJS はフィルターを実行する前に、描画先のピクセルをこのユニフォームにコピーします。
const blendFilter = Filter.from({
gl: { fragment: blendFragSrc },
resources: { uniforms: { uAmount: { value: 0.5, type: "f32" } } },
blendRequired: true,
});
必要な場合にのみ blendRequired を有効にしてください。有効にすると、毎フレーム追加の GPU コピーが発生します。
実行時のユニフォーム更新
// Access the UniformGroup via resources
shader.resources.myUniforms.uniforms.uTime = performance.now() / 1000;
// For isStatic UBOs, call update() after changing values
shader.resources.myUniforms.update();
GPU の高度な機能
サンプルの全容については、references/advanced-gpu.md を参照してください。
- バッファーとテクスチャの部分アップロード:
buffer.update(sizeInBytes, offsetInBytes)は変更されたバイト範囲のみをアップロードします。bufferImageSource.update(startTexel, endTexel)はデータテクスチャのテクセル範囲のみをアップロードします。 - 頂点数とインデックス数、頂点の巻き順、カリング:
geometry.vertexCount(非推奨のgetSize()を置き換えます)、共有インデックスバッファーの先頭部分を描画するgeometry.indexCount、state.clockwiseFrontFaceとcullMode。 - WGSL オーバーライド定数(WebGPU):
Shader.from({ gpu, resources, overrides: { STEPS: 8 } })。異なる定数の組み合わせごとに、専用のパイプラインがコンパイルされます。 - カスタムバインドグループレイアウト(WebGPU):
generateGpuLayoutGroups(extractStructAndGroups(source))でデフォルトを生成して編集し、gpuLayoutとして渡します。 TextureViewによる深度サンプリング(WebGPU):new TextureView(depth, { aspect: "depth-only" })をリソースとしてバインドし、ターゲットの深度アタッチメントをdepthReadOnlyにします。- 整数テクスチャ:
rgba32uintなどを指定したBufferImageSource、usampler2D/texelFetchとtexture_2d<u32>/textureLoad、scaleMode: "nearest"。 - 3D テクスチャと配列テクスチャ:
TextureSource/BufferImageSourceのdepthまたはarrayLayerCountを指定し、sampler3D/texture_3d<f32>またはsampler2DArray/texture_2d_array<f32>でサンプリングします。テクスチャ全体がアップロードされ、renderer.render({ container, target: source, layer })は1つのスライスまたはレイヤーに描画します。 - ストレージテクスチャ(WebGPU):
TextureSourceにstorage: trueを指定し、renderer.texture.getGpuSource(source).createView()を使って独自のコンピュートパスから書き込みます。 - レンダーバンドル(WebGPU):
encoder.beginBundle()/endBundle()で一度だけ描画を記録し、isBundleValid()が有効な間、executeBundle()で再実行します。 - プールされたスクラッチテクスチャ:
TexturePool.getOptimalTexture({ width, height, resolution, antialias })とreturnTexture()。
ユニフォーム型のリファレンス
サポートされている型、対応する WGSL/GLSL 型、値の形式をまとめた表については、references/uniform-types.md を参照してください。
カスタムバッチャー(拡張機能ベース)
抽象クラス Batcher を使うと、用途に応じたカスタムバッチ処理を実装できます。サブクラスを作成し、拡張機能として登録します。
import { Batcher, extensions, ExtensionType } from "pixi.js";
import type {
BatcherOptions,
BatchableMeshElement,
BatchableQuadElement,
Geometry,
Shader,
} from "pixi.js";
class MyBatcher extends Batcher {
public static extension = {
type: [ExtensionType.Batcher],
name: "my-batcher",
};
public name = "my-batcher";
protected vertexSize = 6; // floats per vertex
public geometry: Geometry;
public shader: Shader;
constructor(options: BatcherOptions) {
super(options);
// Initialize geometry and shader
}
public packAttributes(
element: BatchableMeshElement,
float32View: Float32Array,
uint32View: Uint32Array,
index: number,
textureId: number,
): void {
// Pack mesh vertex attributes into the batch buffer
}
public packQuadAttributes(
element: BatchableQuadElement,
float32View: Float32Array,
uint32View: Uint32Array,
index: number,
textureId: number,
): void {
// Pack quad vertex attributes into the batch buffer
}
}
extensions.add(MyBatcher);
要素は batcherName でバッチャーを参照します。BatchableElement インターフェースには、batcherName、texture、blendMode、indexSize、attributeSize、topology、packAsQuad が必要です。
InstructionSet ごとに状態をキャッシュするカスタム InstructionPipe(BatcherPipe がバッチャーを保持する方法と同様)は、destroyInstructionSet(instructionSet) を実装してください。所有するレンダーグループが破棄されるとレンダラーがこれを呼び出すため、キャッシュされた GPU オブジェクトはリークせず、レンダーグループとともに解放されます。
public destroyInstructionSet(instructionSet: InstructionSet): void {
this._cache[instructionSet.uid]?.destroy();
delete this._cache[instructionSet.uid];
}
よくある間違い
[CRITICAL] 古い Shader.from(vertex, fragment, uniforms) コンストラクター
誤り:
const shader = Shader.from(vertex, fragment, { uTime: 1 });
正しい例:
const shader = Shader.from({
gl: { vertex, fragment },
resources: {
uniforms: new UniformGroup({
uTime: { value: 1, type: "f32" },
}),
},
});
v8 では、gl/gpu プログラムと resources を含むオプションオブジェクトが必要です。位置引数を使う API は削除されました。
[CRITICAL] 型注釈のない UniformGroup
誤り:
new UniformGroup({ uTime: 1 });
正しい例:
new UniformGroup({ uTime: { value: 1, type: "f32" } });
すべてのユニフォームには、明示的な { value, type } の組が必要です。型を省略すると、実行時エラー「Uniform type undefined is not supported.」が発生します。
[HIGH] サポートされていない型、または構造が間違っている UBO
UBO モードがサポートするのは、f32 および i32 を基にした型(スカラーとベクトル)です。u32 はサポートされている UniformGroup 型の一覧に含まれていないため、エラーが発生します。行列は浮動小数点数のみです(mat*<f32>)。サンプラーを UBO に配置することはできません。
構造体の名前とフィールドの順序は、シェーダーの UBO 宣言と完全に一致している必要があります。一致しない場合、エラーが発生せず、描画結果が乱れます。
[HIGH] テクスチャを UniformGroup に入れる
誤り:
new UniformGroup({
uTexture: { value: texture, type: "f32" },
});
正しい例:
const shader = Shader.from({
gl: { vertex, fragment },
resources: {
uTexture: texture.source,
uSampler: texture.source.style,
myUniforms: new UniformGroup({
uAlpha: { value: 1.0, type: "f32" },
}),
},
});
テクスチャはユニフォームではなく、リソースです。texture.source(TextureSource)と texture.source.style(TextureStyle)を、トップレベルのリソースエントリーとして渡してください。
【HIGH】別のレンダーターゲットに対してレンダーバンドルを再生する
誤り:
const bundle = encoder.endBundle();
// later, on every frame, regardless of what is bound
encoder.executeBundle(bundle);
正しい例:
if (!bundle || !encoder.isBundleValid(bundle)) {
bundle = record();
}
encoder.executeBundle(bundle);
バンドルには、記録時のパスのアタッチメントと頂点の巻き順、および記録に使用したデバイスの情報が組み込まれています。別のターゲットで再生すると、フレーム全体が WebGPU の検証に失敗するか、裏返しの状態で描画されます。デバイスの消失後に再生すると、明示的に拒否されます。
【MEDIUM】Geometry.getSize() の読み取り
geometry.getSize() は 8.20.0 以降非推奨で、警告が出力されます。キャッシュされ、バッファーまたは属性の変更時にのみ再計算される geometry.vertexCount を使用してください。