BLOGブログ

2019.07.31UE4UE/ Editor拡張

[UE4]独自の描画を行うViewportを作る ② ShaderとRHI

執筆バージョン: Unreal Engine 4.22

こんにちは、エンジニアの小倉です。
前回に引き続き、エディタ拡張などで使える、描画内容をカスタムしたViewportの作り方を前後編にわけて紹介します。
上記画像は、前回と今回の記事で紹介する方法で得られる最終結果となります。

記事構成

ViewportClientとCanvas
ShaderとRHI      <= イマココ!

目次

Rendererとは
Renderer用モジュールを作成する
Shaderを作成する
頂点データを作成する
Rendererを作成する
Rendererを使用する
参考記事
ソースコード

Rendererとは

ここでは、描画を行うための一連の処理をまとめたものをRendererと呼ぶこととします。
Rendererの大まかな処理の流れは次のようになります。

  1. Game Threadでドローコマンドを発行するためのタスクを作成する
  2. Render Threadでタスクが実行される
  3. Render Targetを描画対象として設定する
  4. Graphics Pipeline Stateを設定する
  5. Viewportを設定する
  6. 頂点バッファをデータストリームに設定する
  7. ドローコマンドを発行する

この手順に沿って描画を行うためには、Shaderや頂点バッファなどを事前に作成する必要があります。
順に紹介していきます。

Renderer用のモジュールを作成する

RenderTestプラグインにSimpleRenderという新しいモジュールを追加します。

SimpleRenderer.Build.csは基本的なモジュールに加えて、”RenderCore“、”Projects“、”RHI“を追加します。

StartupModule関数に、Shaderファイルへのパスを設定する記述を追加します。
以下のようにAddShaderSourceDirectoryMapping関数を用いると、プラグイン直下のShadersフォルダに配置した.usfファイルを参照することができるようになります。


最後に、RenderTest.upluginにSimpleRendererモジュールを追加します。
Shaderを扱うため、LoadingPhaseはPostConfigInitにします。

Shaderを作成する

Shaderを開発する前に、Engine/Config/ConsoleVariables.ini のr.ShaderDevelopmentMode=1のコメントアウトを外します。
これによりShader開発における問題(例えば文法エラーなど)があるときに原因を特定しやすくなります。
参考:https://www.unrealengine.com/ja/tech-blog/debugging-the-shader-compiling-process

次に、Shadersフォルダ内にShaderを追加します。ここではファイル名をSimpleShader.usfとしました。

MainVSがVertex Shaderのエントリポイント、MainPSがPixel Shaderのエントリポイントです。
Vertex Shaderでは、頂点データからAttribute0セマンティクスによってスクリーン座標を受け取り、Attribute1セマンティクスによって頂点カラーを受け取ります。それをSV_POSITIONとCOLOR0セマンティクスの出力にそのまま渡し、Pixel Shaderではラスタライザで処理された頂点カラーを出力します。
参考:https://api.unrealengine.com/JPN/Programming/Rendering/ShaderInPlugin/Overview/index.html

次に、FGlobalShaderを継承してSahderクラスを作成します。

まず、Vertex ShaderとPixel Shader共通のFGlobalShader継承クラスであるFSimpleShaderを作成します(6行目)。
ShaderクラスはShouldCompiledPermutation関数を定義する必要があります。これはプラットフォームごとにShaderをキャッシュするかどうかを決定するために使われます(14行目)。
次に、Vertex ShaderのShaderクラスとしてFSimpleShaderVSを作成します(20行目)。
同様に、Pixel ShaderのShaderクラスとしてFSimpleShaderPSを作成します(32行目)。

最後に、IMPLEMENT_SHADER_TYPEマクロを使って、ShaderクラスとSimpleShader.usfへのパス、Shaderのエントリポイントと
Shader Stage(ここではVertex ShaderかPixel Shaderか)を設定します。

頂点データを作成する

ここではVertex Buffer、Vertex Declaration、Index Bufferの3つを作成します。

まず、Vertex Bufferを作成します。

Vertex Shaderは頂点データからスクリーン座標と頂点カラーを受け取るため、座標(FVector2D)と色(FVector4)を持つ単一の頂点データとしてFColorVertexを作成します(5~8行目)。
次に、FVertexBufferを継承したFSimpleVertexBufferを作成します(10行目)。ここでは、InitRHIでVertex Bufferが持つ頂点データ本体を作成します。ここでは単純な三角形を描くので、三角形の端点のスクリーン座標と色を頂点データとして与えます(13行目~29行目)。
最後に、TGlobalResourceテンプレートでFSimpleVertextBufferを静的なリソースとして宣言します(32行目)。静的なリソースとして宣言されたGSimpleVertexBufferは、このSimpleShader.cppファイル内で参照することができます。

次に、Vertex Declarationを作成します。

Vertex Declarationは、Vertex Shaderに与える頂点データの形式を設定します。例えば12行目は、STRUCT_OFFSETマクロによって取得したFColorVertexのColorメンバのバイトオフセットから、Float2だけAttribute1に割り当てるといったようになります。

最後に、Index Bufferを作成します。

Index Bufferは頂点データにインデックスを付与し、そのインデックスを用いてプリミティブを構築することで、頂点を再利用するために用います。ここでは三角形を描くだけなので、頂点の再利用を考慮する必要はありません。Indicesは0、1、2だけになります(8行目)。

Rendererを作成する

Rendererに必要なものが準備できたので、FSimpleRendererというRendererを作成します。

Render関数は、Render Targetを引数に受け取り、そのRender Targetに対して三角形の描画を行います。
ENQUEUE_RENDER_COMMANDマクロは、ドローコマンドを発行するためのタスクを追加(Enqueue)します。ここで追加されたタスクは、Game Threadから1または2フレーム遅延してRender Thread上で実行されます(16行目)。
このタスクのラムダ式にはRHICmdListがパラメータとして与えられており、これを介してドローコマンドを発行します。

DrawTestShaderRenderTarget_RenderThread関数は次のように実装しました。

FRHIRenderPassInfoによって、描画対象とするRender Targetを指定します(13行目)。
GlobalShaderMap関数によってFSimpleShaderVSとFSimpleShaderPSを取得します(17~19行目)。
Graphics Pipline Stateを設定します(22~31行目)。ここでは、デプス・ステンシルテストや、ブレンドなどの設定をします。
BoundShaderStateには、Vertext DeclarationやVertex Shader・Pixel Shaderを設定します(28~30行目)。
SetViewport関数によってビューポートを設定します(34~36行目)。
SetStreamSource関数によって頂点データをデータストリームに設定します(39行目)。
DrawIndexedPrimitive関数によってドローコマンドを発行します。ここでIndex Bufferを与えます(41~48行目)。

これでRendererに必要な処理は一通り実装できました。

Rendererを使用する

Rendererを使用するために、RenderTestモジュール側からSimpleRendererモジュールを使えるようにする必要があります。
まず、RenderTest.Build.csにSimpleRendererモジュールを追加します。

次に、RenderTestViewportClientのDraw関数にSimpleRendererによる描画処理を追加します。

これを実行すると、次のようになります。
SimpleRendererによって描画された三角形をビューポートに表示することができました。

参考記事

https://api.unrealengine.com/JPN/Programming/Rendering/ShaderInPlugin/index.html
https://zhuanlan.zhihu.com/p/66514192

ソースコード

Sample190716A_SimpleRenderer