ポストエフェクトをもっと手軽に: "PracticalPixelEffects"の紹介

  • 4
    Like
  • 0
    Comment

とりあえず、こんな感じのものが作れるようになります。
ezgif.com-video-to-gif.gif
ヘッダファイルはこちらになります: https://gist.github.com/Pctg-x8/da0000a41a640b7ac5a24c9adea7d02a

概要

"PracticalPixelEffects"はポストエフェクトに代表されるようなピクセルエフェクトの製作を補助するSiv3D向けのヘッダライブラリです。Siv3Dでは遅延シェーディングは絵作りまですべてライブラリが面倒を見てくれるためパイプラインに入り込む余地がありませんが、PracticalPixelEffectsは出来上がりのテクスチャおよび描画命令のまとまりを受け取ってエフェクトを適用するので、遅延シェーディングのみならずForward Rendering(適切な日本語訳がわからない)でも利用できるようになっています。
ライセンスはMITです。Gistにあるものをそのままppfx.hなどと命名したファイルに張り付けてご利用いただければと思います。

実装

実装としては単純なヘッダライブラリで、ピクセルエフェクトそのものを表し様々な補助関数を有するPixelEffectクラスと、GPUを使用して高速にプロシージャルなテクスチャを作成するクラスの補助をするAcceleratedGeneratorBaseクラスの2つで構成される非常にシンプルなライブラリとなっています。

PixelEffectクラスでは、const String& path, const Size& framesizeの2つを受け取って初期化するようになっています。このクラスのメインとなるメソッドはvoid drawEffected(RendererFunction)となっており、これをオーバーライドすることでコアな表現を提供することになります。引数はエフェクト適用前の、ソースとなるテクスチャを描画するためのコードで、drawEffected内部では必ずこのコードを実行する必要があります。サンプルの波紋エフェクトでは次のように定義しています。

void drawEffected(RendererFunction func)
{
    this->clearBackbuffer(Palette::Black);
    this->renderToBackbuffer(func);

    Graphics2D::BeginPS(this->shader());
    if(this->mappedTexture.has_value())
    {
        Graphics2D::SetTexture(ShaderStage::Pixel, 1, this->mappedTexture.value());
    }
    else
    {
        Graphics2D::SetTexture(ShaderStage::Pixel, 1, none);
    }
    Graphics2D::SetConstant(ShaderStage::Pixel, 1, this->params);
    this->backbuffer().draw();
    Graphics2D::EndPS();
}

前段2行でエフェクト適用元の初期化(どちらもPixelEffectクラスで提供されるメソッド)、後段で実際にパラメータを設定してエフェクトの適用を行っています。(Graphics2D::SetTextureにOptionで指定できるオーバーロードがあってもよい(むしろそっちのほうがいい)と思うのですがいかがでしょうか)

続いてAcceleratedGeneratorBaseクラスですが、こちらはconst String& pathを受け取って初期化するようになっています。こちらは特にオーバーライドすべきメソッドは存在しなくて、追加パラメータ関係のメソッドをはやすだけで使用可能となっています。サンプルの波紋シェーダではディスプレースメントの量と方向を決めるためにプロシージャルテクスチャの生成を行っていますが、その場合は次のようなクラス定義になります。

// Accelerated WaterWave Texture Generator
class WaterWaveGenerator : public AcceleratedGeneratorBase
{
    struct OffsetInfoBuffer
    {
        float phaseOffset, padding[3];
    };
    ConstantBuffer<OffsetInfoBuffer> offsetInfo;

    virtual void setAdditionalBuffers() override
    {
        Graphics2D::SetConstant(ShaderStage::Pixel, 2, this->offsetInfo);
    }
public:
    // Initialize generator
    WaterWaveGenerator() : AcceleratedGeneratorBase(L"./waterwave.hlsl") {}

    /// vvv Custom Properties vvv ///

    // Set phase offset of sine curve
    void setPhaseOffset(float ph) { this->offsetInfo->phaseOffset = ph; }
};

シェーダ入力

シェーダ入力セマンティクスはSiv3Dのそれに準拠します。具体的には以下のものを共通で受け取れるようになっています。

struct VSOutput
{
    float4 pos : SV_Position;
    float2 texcoord : TEXCOORD0;
    float4 color : COLOR0;
};

おしまい

簡単にですが以上で紹介を終わりにします。ゲーム作りにおいてポストエフェクトが与える影響というものは図り知れないものがあると思うのでぜひとも絵作りに役立てていただければと思います。

サンプル背景: GTGraphics Windows10 Themeから http://gtgraphics.de/en