月別アーカイブ: 2014年5月

メモ:UE4のRenderingの呼ばれ方(2)

[前回の整理] FrowardRenderer におけるDrawVisible系メソッド

DrawVisible系のメソッドは、結局のところ、TStaticMeshDrawList::DrawElement()を呼び出す。

template<typename DrawingPolicyType>
void TStaticMeshDrawList<DrawingPolicyType>::DrawElement(
  const FViewInfo& View,
  const FElement& Element,
  uint64 BatchElementMask,
  FDrawingPolicyLink* DrawingPolicyLink,
  bool& bDrawnShared
)

FrowardRendererのBasePassで使用するDrawingPolicyTypeは、FMeshDrawingPolicyが基底の、TBasePassForForwardShadingDrawingPolicy<T>のテンプレート特殊化クラス。

template<typename LightMapPolicyType>
class TBasePassForForwardShadingDrawingPolicy : public FMeshDrawingPolicy

FrowardRendererで使用されているLightMapPolicyTypeは以下の通り。

FNoLightMapPolicy
TLightMapPolicy<LQ_LIGHTMAP>
TDistanceFieldShadowsAndLightMapPolicy<LQ_LIGHTMAP>
FSimpleDirectionalLightAndSHIndirectPolicy

TStaticMeshDrawList::DrawElement()の大まかな流れ

引数のbDrawnSharedは、直前のDrawElement呼び出しで使用されたDrawPolicyLinkが同じものであるかどうかが代入される。
falseの場合は

DrawingPolicyLink->DrawingPolicy.DrawShared()

を呼び出す。

引数のBatchElementMaskに応じて次のメソッドを、必要な回数呼び出す。

DrawingPolicyLink->DrawingPolicy.SetMeshRenderState()
DrawingPolicyLink->DrawingPolicy.DrawMesh()

上記の通り、DrawElement()の実行部分は、DrawingPoilicyに実装されている。
DrawShared()が、端的に言えば、シェーダー/マテリアル切り替え単位の設定部分。
SetMeshRenderState()が、DrawCallごとに設定が必要なUniform/Texture/Stateの設定部分。
DrawMesh()がDrawCallの呼び出し部分となる。

FMeshDrawingPolicyについて

TBasePassForForwardShadingDrawingPolicyの基底クラスである、FMeshDrawingPolicyを見るとDrawCall呼び出しの大まかな流れが分かる。
DrawElement()に関連する下記の3つのメソッドについて見てみる

DrawShared()
FMeshDrawingPolicyのメンバーで保持しているVertexFactoryの
VertexFactory->Set()
を呼び出し、頂点配列をBindしている。
SetMeshRenderState()
引数で指定されたMeshに設定されているフラグと、FMeshDrawingPolicyに設定されているフラグをチェックして、FillMode,CullModeの指定を行っている。
DrawMesh()
引数で受け取った、Mesh,BatchElementIndexより、DrawCallを呼び出す頂点範囲とIndexBufferを示すFMeshBatchElementを取得し、これに基き、実際のDrawCallを呼び出す。

FMeshDrawingPolicyでは、シェーダーの設定は行われておらず、頂点配列のBindとFillMode,CullModeの指定とDrawCallの呼び出しが行われている。
また、同一DrawingPolicy内では、頂点配列は共有されており、これを複数のDrawCallで共有できる仕組みを有していることが分かる。

TBasePassForForwardShadingDrawingPolicyについて

次に、FMeshDrawingPolicyの派生クラスである、TBasePassForForwardShadingDrawingPolicy<LightMapPolicyType>について見てみる。

コンストラクタ
コンストラクタでは、VertexShaderとPixelShader(後述)を取得している。これらは、LightMapPolicyTypeと、RenderTargetのフォーマットで決まる。
DrawShared()
下記メソッドの呼び出し
RHISetBoundShaerState()
VertexShader->SetParameters()
PixelShader->SetParameters()
LightMapPolicy.Set()
SetMeshRenderState()
下記メソッドの呼び出し
LightMapPolicy.SetMesh()
VertexShader->SetMesh()
PixelShader->SetMesh()
FMeshDrawingPolicy::SetMeshRenderState()
DrawMesh()
FMeshDrawingPolicyと同じ

RHISetBoundShaerState()は、IA/VS/PSのBindを行っており、FMeshDrawingPolicyと比較すると、シェーダー関連の設定と、LightMapPolicyによる設定が追加されているのが分かる。

FNoLightMapPolicyについて

ForwardShadingで使用されているLightMapPolicyTypeの中で、一番単純な、FNoLightMapPolicyの、DrawCallに関連するメソッドを見てみる。

Set()
引数で受け取った、FVertexFactory型の、VertexFactory->Set()を呼び出し、VertexFactoryが保持している頂点配列を、Bindしている。
引数で渡されるVertexFactoryは、TBasePassForForwardShadingDrawingPolicy<T>のメンバーのVertexFactoryなので、遡れば、DrawingPolicyLink->DrawingPolicyのVertexFactoryということになる。つまり同一DrwaingPolicyLink内で頂点配列は共有される。
SetMesh()
何もしない。

VertexShaderとPixelShaderについて

TBasePassForForwardShadingDrawingPolicy<LightMapPolicyType>内で宣言されている、VertexShaderとPixelShaderは以下の通り。

TBasePassForForwardShadingVSBaseType<LightMapPolicyType>* VertexShader;
TBasePassForForwardShadingPSBaseType<LightMapPolicyType>* PixelShader;

どちらもFMeshMaterialShaderを基底にしている。

template<typename LightMapPolicyType>
class TBasePassForForwardShadingVSBaseType : public FMeshMaterialShader, public LightMapPolicyType::VertexParametersType

template<typename LightMapPolicyType>
class TBasePassForForwardShadingPSBaseType : public FMeshMaterialShader, public LightMapPolicyType::PixelParametersType

VertexShader, PixelShaderの各インスタンスは、DrawingPolicyのコンストラクト時に、ポインタが取得される。どちらもShaderの実体を保持するが、設定するUniformやTextureの実体は保持しない。
DrwaElement()に関連する以下のメソッドを見てみる。

VertexShader->SetParameters()
以下のメソッドを呼び出す
HeightFogParameters.Set(GetVertexShader(), &View);
FMeshMaterialShader::SetParameters(GetVertexShader(),MaterialRenderProxy,InMaterialResource,View,TextureMode);
PixelShader->SetParameters()
以下のメソッドを呼び出す
FMeshMaterialShader::SetParameters(GetPixelShader(),MaterialRenderProxy,MaterialResource,*View,TextureMode);
このルーチンはVertexShaderで使われたものと同様。
VertexShader->SetMesh()
以下のメソッドを呼び出す
FMeshMaterialShader::SetMesh(GetVertexShader(),VertexFactory,View,Proxy,BatchElement);
PixelShader->SetMesh()
ReflectionCubeMapのBind(必要に応じて)
以下のメソッドを呼び出す
FMeshMaterialShader::SetMesh(GetPixelShader(),VertexFactory,View,Proxy,BatchElement);

上記のことから、TBasePassForForwardShadingDrawingPolicy内で宣言されている、VertexShaderとPixelShaderクラスは、
FMeshMaterialShaderに対して、HeightFog用のパラメータ設定と、ReflectionCubeMapのBindを追加したものと考えられる。

FMeshMaterialShaderについて

シェーダーのパラメータ設定はおおよそ、この基底クラスに集約されている。DrawElement呼び出しからは、以下のメソッドが呼び出される。

FMeshMaterialShader::SetParameters(GetVertexShader(),MaterialRenderProxy,InMaterialResource,View,TextureMode);

FMaterialShader::SetParameters()を呼び出すだけ。FMaterialShader::SetParameters()では、
引数Viewが保持するUniformBufferのBind (View関連パラメータのUniform)
引数MaterialRenderPorxyが保持してるUniformBufferのBind (Material関連パラーメータのUniform)
引数MaterialRenderPorxyがFGuidを保持してるUniformBufferのBind (ParameterCollection(Sceneが実体保持)のUniform)
引数MaterialRenderPorxyが保持してる2DTextureのBind
引数MaterialRenderPorxyが保持してるCubeTextureのBind

以下は条件によって必要に応じて各種設定される
DeferredShading用のTextureの設定(PixelShader向け)
引数Viewが保持するAtomosphere pass用のUniformパラメータの設定
PostProcessパラメータの設定
EyeAdaptation用のTextureの設定
PerlinNoiseGradiantテクスチャの設定
PerlinNoise3Dテクスチャの設定

これ等のリソースの設定位置(BindSlot)は基本的に、FMaterialShaderが保持している。
対して、設定されるリソースの実体は、引数のView/MaterialRenderProxy、もしくはGlobal変数から取得される。

FMeshMaterialShader::SetMesh(GetPixelShader(),VertexFactory,View,Proxy,BatchElement);

引数VertexFactoryに関連するUniform/Textureの設定。
引数BatchElementに格納されているUniformの設定(内容は変換マトリクスなど)
引数Proxy,Viewに格納されているUniformの設定(内容はLODのFading用)

引数VertexFactoryは頂点配列に相当するデータなので、これに対応するUniformやTextureが設定される。
具体的には、SpeedTree/Particle/GPUSkinning/VectorFieldVisualizationなど、UE4のMaterialで保持しないシェーダーリソース情報の設定がここで行われる。

大まかな流れ

UE4_render_draw_overview

だいたいこんな感じだと思われる。

広告

メモ: UE4のRenderingの呼ばれ方

ただの私的メモ。
だが、UE4のエンジン側の描画周りのソース見始める人の取っ掛かりとして役に立つかもしれないので。
UE4のRendering(not Shader or UserInterface)に関するざっくりとした流れ。
たぶんVisualStudioでソース追っかけながら見ないと何書いてるかさっぱりだと。
続きを読む

Burley Diffuseについて考えてみる

Brent Burleyさんが、下記の論文中で述べている、Fresnelの効果を組み入れた、Diffuseモデルについて考えてみます。

参照

Physically-Based Shading at Disney[Brent Burley, SIGGRAPH 2012]

Diffuseの式の特徴について

参照論文の5.3項で以下のようなDiffuseの式が示されています。

d_fd
d_fd90

fdの先頭の係数は、LambertモデルのBRDFです。それ以下の部分がLambertモデルの変調をしていると言えると思います。Lambertモデルに対する係数は、roughnessの値を0~1までとすると、0.25~6.25まで変化するので、従来のDiffuseに対して、非常に幅の大きな変化量を持っていることが分かります。
式の特性として以下のが挙げられると思います。

  • NdotL, NdotVがそれぞれ0(90度)に近づくにしたがって、Fd90の値の影響を大きく受ける。
  • Fd90の値は、LdotHが1(0度)のときに最大(2.5)となり、LdotHが0(90度)のときに0.5となる。

したがって、LightとViewが同じ方向で、incidentの法線がLight/Viewに対して、90度の角度を持つとき、最大(6.25) の値となります。一方で、Fresnel反射が最大となる、LightとViewが正対した条件では最小(0.25)となります。incidentの法線がViewやLightに対して正対するにつれて、Fd90の影響は小さくなり、1.0へと近づいていきます。
このことから、上記の式はフレネル反射が増大するときは値が小さくなっていき、View/Lightがgrazing angleに近づきつつも、Fresnel反射の値が小さい時は、値が大きくなるように作られています。
Slick近似のFresnel反射の計算と一緒に使用することで、roughnessの高いマテリアルで、エッジ付近の明るさを高く保つ効果が期待できます。
また、この式は、Helmholtzの相反性を保っています。

式の意味を考えてみる

上記の式は、相反性を保っていますが、Fd90の値は、LightとViewの成す角度に敏感に反応します。また、LdotHが1(0度の時)に値が最大となります。これはどちらかというと、拡散反射というよりは、指向性を持った再帰反射モデルに近いものだと思います。
また、Fd90が1以上か、1以下かで、式の意味が異なります。
Fd90が1以下ならば、Fresnel反射によって、媒質に入る前に反射される減衰と、媒質内で反射される減衰を考慮している式に見えますが、Fd90が1以上で起きる増幅に関しては、拡散反射で説明するのが難しいです。Diffuse項で計算すべきものなのかは考慮が必要だと思います。この式に関しては、論文中でも経験的に求められた式である旨が記されているので、深い物理的根拠があるわけではなさそうです。

roughnessが0の状態ならば、光が媒質に対して入射する量は、NdotLでSchlickの近似式より反射量を計算し、残りの量を入射量とする事で計算可能だと思います。
媒質から射出する光の量をNdotVでSchlickの近似式を計算することで求めるのも、屈折を考慮しないと論文中で明言しているので、理に適っていると思います。
ただし、拡散反射では、ひとたび媒質に入射した光の向きは、一様に拡散されると考えるのが基本で、Fd90の値のようにLdotHの影響を強く受けるの関数が計算に含まれるのは、拡散反射以外の要素を計算しているといえると思います。
しかし、Fd90に指向性を導入しないとすると、roughnessの関数にするしかなく、入射した以上の光を射出することは出来ないので、最大値が1の関数とするしかありません。
この様に計算すると、grazing angleに近づくにしたがって、Diffuseの値は急速に低下する関数となります。しかし、LambertモデルのDiffuseは、grazing angle付近で暗いと言われることが多く、これをさらに強調することになってしまいます。
そのため論文著者は、Fd90のような指向性を導入し、Fresnelの値が小さい時のgrazing angle付近の暗さを補完するための式を導入したのだと思います。