【記事メモ】CustomMeshComponentの作成 Part1

UEのカスタムメッシュの作成について、まとめていただいている記事の資料を抜粋して意訳&メモまとめです。

Part0

【記事メモ】CustomMeshComponentの作成 Part0 - SHIBA LOG

 

今回はPart1。

 

medium.com



※自分用メモなので端折っている箇所もあります。参考程度に

※少し古い記事なのでUE5では事情が異なるケースがあります。わかる所は補足予定

※独自メモは@memoで記入

 

ここではVertexFactoryの概念とその実装について説明。

VertexFactoryに関連する抑えておきたい用語

RHI...Rendering Hardware Interface

グラフィックスAPIの抽象化レイヤ

(https://docs.unrealengine.com/5.0/en-US/API/Runtime/RHI/)

DirectXやVulkanなど異なるグラフィックスAPIをサポートするためのインターフェイス

 

FRenderResource/FRHIResource

殆どのレンダリング関係のリソースクラスはこのクラスを継承している、重要なクラス。

FRHIResource

頂点バッファ、インデックスバッファなどRHI系のリソースが継承している。

基本的にこれらを直接操作することはない。

 

FRenderResource...

基本的に自分たちが触るのはこのクラスで、内部でRHIResourceを作成し、カプセル

化している。そのため、InitRHIやReleaseRHIなどの関数がある


VertexFactory

VertexFactoryの目的...頂点管理。頂点情報の受け渡し(CPUからGPUへ)

頂点バッファ作ったり、頂点のレイアウトを定義したり...

 

先程あげた用語を使いつつ、説明すると

VertexFactoryは「FRenderResource」を継承しておりメッシュデータを何らかの形で取

得し、それを利用して「FRHIResource」を作成する。

レンダラーがメッシュを描画するとき

VertexFactory、厳密に言うとカプセル化した「FRHIResource」を必要とする。

 

VertexFactoryはいつ、どうやってRHIResourceを作成するか?

ここでは、すでに組み込まれている「LocalVertexFactory」を元に説明を行う。

 

※LocalVertexFactoryを構成するクラス図

似たような名前のクラス図が色々出てくるので図をみながら確認したほうが良い

https://miro.medium.com/max/700/1*r2ej5rO4Lmc5oj8PKWPOjQ.jpeg

FStaticMeshDataType

VertexFactoryが、RHIResourceを初期化するために必要なデータを持つクラス

LocalVertexFactoryの中ではFStaticMeshDataTypeを継承した「FDataType」というローカルクラスがあり

そこにスキンメッシュで利用する物を含めて、そちらのクラスを利用している。

@memo FVertexStreamComponentとFRHIShaderResourceViewをセットで持っている

FVertexStreamComponent

FDataTypeが持つ(FStaticMeshDataType)クラスの一つとしてStreamComponentがある。

位置やUVなど一つの属性毎に作られている。

UE4では全てのAttribute(位置、UVなど)を一つの頂点バッファに格納するのでは

なく、Attribute毎に頂点バッファを使用している。

 

理由はこちらに記載

Storing vertex data: To interleave or not to interleave? | Anteru's Blog

 

@memo 上記リンクをざっくりまとめると

・アクセスの柔軟性

特定のAttributeを無効に簡単にすることが出来る(NULLバッファを差し込んだり)

シャドウマップを作る時は位置情報だけがあれば良いため、他のAttributeと切り離すことで、キャッシュ、GPU帯域幅の利用率が最も効率が良くなる

・キャッシュ効率が良い

といったことがあるらしい

 

つまり中身としては、VertexBufferリソースと、Streamに関するMetaデータのラッパ。

FVertexStreamComponentで内包している、FVertexBufferはRenderResourceを継承している

FVertexElement

Vertex Declarationを作成するために、使用されるストリームに関するいくつかのデータ(ただし、ストリームの Vertex Buffer ではない)を含んでいる

EVertexElementTypeはデータフォーマット。

DirectXでいうDXGI_FORMAT(https://docs.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format

FVertexDeclarationElementList

上記、FVertexElementの配列

FRHIVertexDeclaration

InputLayoutと同等のRHIResource。

 

要は頂点情報の並びを定義するもの。位置、法線、頂点カラー、など

VertexFactoryは、上記記載の「FVertexDeclarationElementList」を引数として

InitDeclarationというDeclarationを定義するための関数を持つ

 

InitDeclarationのコード内部に関しては、注意事項が2つある。

・InitDeclarationを呼び出すときに「StreamType」を指定する必要がある。

このStreamTypeによって、InitDeclarationでVertexDeclarationを割り当てる

変数が違う。

 

基本的にはDefaultが利用されるのだが

位置のみ/位置&法線のタイプがあり、これらは例えばDepth Passのような特定パスで利用される。

 

・VertexDeclarationのキャッシュ機能がついている

全く同等のDeclarationは異なるRHIResourceを生成することなく、再利用のためキャッシュ機構が備わっている。

 

FVertexStream

Vertex Streamをセットするために必要な情報を含む構造体

FVertexStreamComponentと似ているものになる

 

FVertexInputStream

もう一つの頂点ストリームデータ型。こちらも同じ用なデータを含んでいる。

FVertexStreamComponentに近いが、VertexBufferではなくFRHIBufferを持つ

VertexFactoryのGetStreams関数で、配列として、レンダラーから取得される

内部で持っているFVertexStreamごとにFVertexInputStreamを生成して配列として返す

 

それ以降の処理としてVertexFactoryにStreamを要求し

実際にBindするコードに興味がある場合は、以下の関数あたりをチェック

FMeshDrawCommand::SubmitDraw

FRHICommandList::SetStreamSourc

FRHICommandList::DrawIndexedPrimitive

 

Vertex Factory Shader

UE4 は頂点ファクトリーのシェーダーコードにテンプレートベースの方法を使用している

Vertex Factory Shader(LocalVertexFactory.ush/LocalVertexFactoryCommon.ush)は

頂点シェーダを定義しておらず、エントリーポイントを持たない

 

ただ、特定のインターフェイスに従って関数、構造体を定義すれば

実行中のパスの頂点シェーダによって利用される。

 

そのため、 VertexFactoryのShaderファイルの拡張子はushで、シェーダヘッダファイルとなっている。

 

この仕組みにより各Vertex Factory毎にVertexShaderの全体を記載する必要がないため

モジュール化、コードの再利用が行える。

 

ただし、コンパイルするシェーダ数は多くなる。

UE4のShadrer Permuations

Unreal Engine 4 Rendering Part 5: Shader Permutations | by Matt Hoffman | Medium

 

Vertex Factory Shader Permutations

ushに応じて、Material*VertexFactoryの最小限の組み合わせをビルドするようになっている

ShouldCompilePermutations→ShouldCompilePermutationから情報を取得

@memo 特定の組み合わせのみをコンパイルするようにフラグでtrueを返す

The Vertex Factory Macros

VertexFactoryはどのように自分が使用するushを把握するか?

まず、VertexFactoryのクラスには

「DECLARE_VERTEX_FACTORY_TYPE」マクロを定義しておく必要がある

このマクロは、定義したクラスにStaticTypeというFVertexFactoryType型の

静的メンバ変数と、StaticType へのポインタを返すだけのメンバメソッドを追加する。

 

次に、「IMPLEMENT_VERTEX_FACTORY_TYPE」でushへのパスを指定する

@memo UE5では引数の指定方法が少し変わっている。フラグ単位ではなく、enumのビット単位で渡すようになっている

The Vertex Factory Shader Parameters

頂点に特定のシェーダパラメータのセットを渡したいときは

FVertexFactoryShaderParameters

を継承して、クラスを実装する必要がある(具体的なところはPart2にて)

The Local Vertex Factory Uniform Buffer

すべてのVertexFactoryで使われているわけではないが

FLocalVertexFactory+そのサブクラスで利用されるので、言及する

 

LocalVertexFactoryは汎用的に利用できるものになっていて、複数ケースをサポートできる。

GlobalUniformBufferを利用して、Vertex Shaderに追加データを渡していて

良い例としては、手動頂点フェッチ(MANUAL_VERTEX_FETCH)。

 

VertexFactoryは、他の追加データと共に、必要なSRVをそのUniformbufferにわたす

BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT~

END_GLOBAL_SHADER_PARAMETER_STRUCTを利用して定義する

FLocalVertexFactoryは、内部でテンプレート化されたRHIユニフォームバッファーへの参照を持っている

 

@memo 「CreateLocalVFUniformBuffer」でUniformBufferの生成が行われていて

手動頂点フェッチではない場合はNULLのデータで埋めていそう

 

より興味細かい所は以下を参照

Learning Unreal Engine 4: Adding a Global Uniform Shader Parameter(1) | by YiChen Lin | Medium

The Vertex Factory Shader Code (VertexFactory.ush)

VertexFactoryのShaderファイルの拡張子はushとなっていて

ここで定義する必要があるのは、インターフェイスの関数と、構造体だけで良い

 

例えば、頂点データレイアウトを記述する...

FVertexFactoryInput

FPositionOnlyVertexFactoryInput

FPositionAndNormalOnlyVertexFactoryInput

 

頂点シェーダーからピクセルシェーダーにデータを渡すための補間関数である...

VertexFactoryGetTangentToLocal

VertexFactoryGetWorldPosition

VertexFactoryGetRasterizedWorldPosition

VertexFactoryGetPositionForVertexLighting

VertexFactoryGetInterpolantsVSToPS

 

Docを読んだほうがわかりやすいかも

Shader Debugging Workflows Unreal Engine | Unreal Engine 5.0 Documentation

シェーダー開発 | Unreal Engine ドキュメント

@memo

↑のVertexFactoryを読むと、より分かりやすいかも

 

FLocalVertexFactoryから継承したVertexFactoryはどのようにushを利用するか?

LocalVertexFactory.ushを使うか、独自シェーダを使うか?どちらの方法でも可能。

 

ただ、現状だとFLocalVertexFactoryから継承しているVertexFactory系のクラスは

全てLocalVertexFactory.ushを使っている。

同じファイルにコードを追加して

シェーダではプリプロセッサマクロを利用して切り替えを行い、アクティブなコードだけが含まれるようにコンパイルしている

VertexFactoryの「ModifyCompilationEnvironment」関数にて、SetDeineを呼び出すことで制御が可能

 

プリプロセッサマクロにて制御

https://miro.medium.com/max/700/1*DtxGTtT-3p4bbK6KKxb8dQ.jpeg

SetDefine適用

https://miro.medium.com/max/700/1*E2JRZ70IrIEB-F2vP29VKA.jpeg

 

Part1は以上。Part2へつづく

(以降はサンプルプロジェクトのコード読んだほうがわかりやすいかも)