Unreal Engine 4 Postprocess

はじめに

UE4のRendererModuleはユーザが独自のパスを追加できるいくつかのコールバックがあります. Unityのように独自のポストプロセスパスを追加したいため調査しました.

IRendererModule のコールバック

GetModule("Renderer") で取得できる IRendererModule にある描画関連のコールバックは次の3つになります.

  • PostOpaqueRender
  • OverlayRender
  • ResolvedSceneColor

テンプレートとしてのコードは次のようになります. 各パスでは何もしていないですが, RenderDocでパスの流れを確認するにはこれで十分です.

RenderHookComponent.cpp
#include "RenderHookComponent.h"
#include <Modules/ModuleManager.h>
#include <Modules/ModuleInterface.h>
#include <RendererInterface.h>
#include <RenderUtils.h>
#include <ClearQuad.h>

URenderHookComponent::URenderHookComponent()
{
    PrimaryComponentTick.bCanEverTick = false;
}

void URenderHookComponent::BeginPlay()
{
    Super::BeginPlay();
    // Get Rendere Module
    IRendererModule* RendererModule = (IRendererModule*)(FModuleManager::Get().GetModule("Renderer"));

    // Add delegates
    FPostOpaqueRenderDelegate OverlayRenderDelegate;
    OverlayRenderDelegate.BindUObject(this, &URenderHookComponent::OnOverlayRender);
    OnOverlayRenderHandle_ = RendererModule->RegisterOverlayRenderDelegate(OverlayRenderDelegate);

    FPostOpaqueRenderDelegate PostOpaqueRenderDelegate;
    PostOpaqueRenderDelegate.BindUObject(this, &URenderHookComponent::OnPostOpaqueRender);
    OnPostOpaqueRenderHandle_ = RendererModule->RegisterPostOpaqueRenderDelegate(PostOpaqueRenderDelegate);

    OnResolvedSceneColorHandle_ = RendererModule->GetResolvedSceneColorCallbacks().AddUObject(this, &URenderHookComponent::OnResoledSceneColor);
}

void URenderHookComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    Super::EndPlay(EndPlayReason);
    IRendererModule* RendererModule = (IRendererModule*)(FModuleManager::Get().GetModule("Renderer"));
    if(OnOverlayRenderHandle_.IsValid()){
        RendererModule->RemoveOverlayRenderDelegate(OnOverlayRenderHandle_);
        OnOverlayRenderHandle_.Reset();
    }
    if(OnPostOpaqueRenderHandle_.IsValid()){
        RendererModule->RemovePostOpaqueRenderDelegate(OnPostOpaqueRenderHandle_);
        OnPostOpaqueRenderHandle_.Reset();
    }
    if(OnResolvedSceneColorHandle_.IsValid()){
        RendererModule->GetResolvedSceneColorCallbacks().Remove(OnResolvedSceneColorHandle_);
        OnResolvedSceneColorHandle_.Reset();
    }
}

void URenderHookComponent::OnOverlayRender(FPostOpaqueRenderParameters& PostOpaqueRenderParameters)
{
    FRHICommandList& RHICmdList = *PostOpaqueRenderParameters.RHICmdList;
    FRHIRenderPassInfo RenderPassInfo;
    RHICmdList.BeginRenderPass(RenderPassInfo, TEXT("OverlayRender"));
    RHICmdList.EndRenderPass();
}

void URenderHookComponent::OnPostOpaqueRender(FPostOpaqueRenderParameters& PostOpaqueRenderParameters)
{
    FRHICommandList& RHICmdList = *PostOpaqueRenderParameters.RHICmdList;
    FRHIRenderPassInfo RenderPassInfo;
    RHICmdList.BeginRenderPass(RenderPassInfo, TEXT("PostOpaqueRender"));
    RHICmdList.EndRenderPass();
}

void URenderHookComponent::OnResoledSceneColor(FRHICommandListImmediate& RHICmdList, class FSceneRenderTargets& SceneContext)
{
    FRHIRenderPassInfo RenderPassInfo;
    RHICmdList.BeginRenderPass(RenderPassInfo, TEXT("ResoledSceneColor"));
    DrawClearQuad(RHICmdList, FLinearColor::White);
    RHICmdList.EndRenderPass();
}

ローカルマルチプレイを考慮した次のような場合,

Local Multiplay

レンダリングパスは次のようになります. Post OpaqueOverlayは各プレーヤのビュー毎に, Post Resolved Scene Colorはシーン全体で一度だけ呼び出されます. これはデフォルトの単純なシーンのパスですので, 例えばCustom Depthなどの機能を使用した場合にパスが追加されると思います.

Postprocessing

独自ポストプロセスパス

パスを追加する箇所は, OverlayPost Resolved Scene Colorが候補になると思います.

全てのポストプロセスを自前で用意するならばこれで話は終わりなのですが, やはりPostProcessingVolumeも使いたいと思うでしょう. 再利用できないかと調べましたが, 相変わらず各機能がエンジンに深く関連しているため難しいように思います.

ポストプロセスを行う場合, 一般的にシーンのカラーをレンダーターゲット以外にコピーする必要があります. PostProcessingVolumeは最小でも次の処理をしています, この処理が重複するオーバーヘッドも考慮しないといけません.

  • PostProcessInput0へSceneColorをコピー
  • CustomStencilへステンシルをコピー

まとめ

独自のパスを追加できるのですが, PostProcessingVolumeの再利用が難しいためオーバーヘッドが気になります.