メインコンテンツまでスキップ

シェーダークアッドパス

RenderPassShaderQuad は、シェーダーでクアッドを描画する RenderPass です。そのため、フラグメントシェーダーはクアッドが覆うピクセルごとに 1 回実行されます。覆う範囲はデフォルトではレンダーターゲット全体で、ビューポートを設定するとその一部になります。これはポストプロセスエフェクトや、GPU でテクスチャを計算するあらゆるパスの基本的な構成要素です。

このパスは、その shader 用のクアッドを作成し、レンダーステートを設定し、実行時にクアッドを描画します。シェーダーを設定してそのまま使用することも、シェーダーを作成してパラメーターを設定するパスの基底クラスとして使用することもできます。

シェーダー​

頂点シェーダーは、SEMANTIC_POSITION を使用する属性でクアッドの角を受け取ります。quadVS シェーダーチャンクがこの頂点シェーダーを実装し、クアッドの UV 座標を uv0 varying でフラグメントシェーダーに渡すため、記述するのはフラグメントシェーダーだけです。パスが WebGL2 と WebGPU の両方で動作するように、GLSL と WGSL の両方で記述します。

uniform sampler2D sourceTexture;
uniform vec3 tint;
varying vec2 uv0;

void main() {
vec4 color = texture2D(sourceTexture, uv0);
gl_FragColor = vec4(color.rgb * tint, color.a);
}

ShaderUtils.createShader に両方のフラグメントシェーダーを渡して、シェーダーを作成します。

const shader = ShaderUtils.createShader(device, {
uniqueName: 'TintShader',
attributes: { aPosition: SEMANTIC_POSITION },
vertexChunk: 'quadVS',
fragmentGLSL: tintGLSL,
fragmentWGSL: tintWGSL
});

シェーダーは共有されるため、パスはシェーダーを破棄しません。

シェーダーパラメーターの設定​

シェーダーのテクスチャとユニフォームは、グラフィックスデバイスのスコープに設定されます。他のパスが同じ名前のパラメーターを設定することがあるため、クアッドを描画する直前に設定してください。パラメーターはパスの作成時に一度だけ解決し、オーバーライドした execute の中で、クアッドを描画する super.execute() を呼び出す前に値を設定します。

// テクスチャに色を乗算してレンダーターゲットに描画するパス
class TintPass extends RenderPassShaderQuad {
constructor(device, sourceTexture) {
super(device);
this.name = 'TintPass';
this.sourceTexture = sourceTexture;
this.tint = new Float32Array([1, 0.5, 0.5]);
this.shader = ShaderUtils.createShader(device, {
uniqueName: 'TintShader',
attributes: { aPosition: SEMANTIC_POSITION },
vertexChunk: 'quadVS',
fragmentGLSL: tintGLSL,
fragmentWGSL: tintWGSL
});

// シェーダーパラメーターを一度だけ解決する
this.sourceTextureId = device.scope.resolve('sourceTexture');
this.tintId = device.scope.resolve('tint');
}

execute() {
// クアッドを描画する直前にパラメーターを設定する
this.sourceTextureId.setValue(this.sourceTexture);
this.tintId.setValue(this.tint);
super.execute();
}
}

const pass = new TintPass(app.graphicsDevice, sourceTexture);
pass.init(renderTarget);

// クアッドがレンダーターゲット全体を覆うため、内容をロードせずにクリアする
pass.setClearColor(Color.BLACK);

毎フレーム描画するにはパスをカメラに追加します(カメラパスを参照してください)。または、pass.render() で一度だけレンダリングします。

レンダーステートと描画領域​

デフォルトでは、クアッドはレンダーターゲット全体を覆い、ブレンド、深度テスト、フェイスカリングなしで描画され、レンダーターゲットの内容を置き換えます。これを変更するには、次のプロパティを設定します。

  • blendState - クアッドをレンダーターゲットの内容と合成します。
  • depthState、stencilFront、stencilBack - クアッドをレンダーターゲットの深度またはステンシルに対してテストします。
  • viewport、および必要に応じて scissor - レンダーターゲットの一部だけに描画します。

たとえば、パスを直接使用して、テクスチャアトラスの 1 つのタイルにシェーダーを描画します。アトラスはクリアされないため、他のタイルは内容を保持します。

const pass = new RenderPassShaderQuad(app.graphicsDevice);
pass.name = 'AtlasTile';
pass.shader = tileShader;
pass.init(atlasRenderTarget);

pass.viewport = new Vec4(256, 0, 256, 256);
pass.render();

// 同じタイルの上に 2 つ目のシェーダーをブレンドする
pass.shader = overlayShader;
pass.blendState = BlendState.ALPHABLEND;
pass.render();

pass.destroy();

複数のクアッドの描画​

スプライトのように、それぞれがインデックスに応じて読み取るデータだけが異なる多数のクアッドを描画するには、numInstances を設定して、1 回のインスタンス描画でまとめて描画します。各インスタンスは自身のインデックス(GLSL では gl_InstanceID、WGSL では pcInstanceIndex)を読み取ってクアッドのデータを取得し、たとえばクアッドを配置します。デフォルトでは numInstances は undefined で、インスタンス化せずに 1 つのクアッドを描画します。0 を設定すると何も描画しませんが、パス自体はレンダリングされるため、レンダーターゲットのクリアは引き続き行われます。

ライトごとに 1 つなど、異なるパラメーターで複数のクアッドを描画するには、execute をオーバーライドし、クアッドごとにパラメーターまたはビューポートを設定して super.execute() を呼び出します。これにより、パスのレンダーステートでクアッドが描画されます。

execute() {
for (const light of this.lights) {
this.colorId.setValue(light.color);
this.viewport = light.screenRect;
super.execute();
}
}

クアッドの即時描画​

テクスチャを一度だけ生成する場合など、単発の描画には drawQuadWithShader を使用します。この関数は、パスをセットアップせずに、シェーダーでクアッドをレンダーターゲットへ即座に描画します。

device.scope.resolve('scale').setValue(4);
drawQuadWithShader(device, renderTarget, shader);

呼び出すたびにパスを作成し、レンダリングして破棄するため、呼び出しごとにメモリの割り当てが発生します。グラフィックスデバイスに現在設定されているブレンドステートで描画し、レンダーターゲットの内容をクリアせずにロードします。クアッドを毎フレーム描画する場合や、独自のレンダーステートで描画する場合は、一度作成して再利用する RenderPassShaderQuad を使用してください。

例​

2 つの RenderPassShaderQuad パスが、テクスチャに光の軌跡を描きます。フェードパスは逆減算のブレンドステートでテクスチャを暗くし、ブラシパスは各ブラシの経路に沿ってドットを描画します。ブラシパスはドットごとにビューポートを設定し、すべてを 1 つのパスで描画します。

Light Trails

ポストプロセスエフェクト内の RenderPassShaderQuad が、深度や明るさが急激に変化する箇所で、シーンにインクの輪郭線を描画します。

Ink Outline