📝

Unity2021からRenderTextureDescriptorにDepthStencilFormatというパラメータが追加されていた

2024/04/07に公開

補足:この記事ではUnity2022.3.21のC#コードを参照しています

RenderTextureDescriptor.depthStencilFormat

いつのタイミングからか、RenderTextureDescriptorにDepthStencilFormatという変数が追加された。ドキュメントではUnity2021.2から変数の説明が追加されている。

https://docs.unity3d.com/ja/2021.2/ScriptReference/RenderTexture-depthStencilFormat.html

/// <summary>
///   <para>The desired format of the depth/stencil buffer.</para>
/// </summary>
public GraphicsFormat depthStencilFormat { get; set; }

その名の通りdepth/stencil bufferのFormatを設定するパラメータ。

既に存在しているstencilFormatとの違いは?

Stencil bufferはDepth bufferの中に埋め込まれており、Stencilを設定するということは、Stencil + Depthを設定する必要があるということになる(参考)。
そのため、stencilFormat単体でStencilのFormatを指定することは難しく、DepthとStencil両方を設定するFormatで決定するほうが良い、ということだと思われる。stencilFormatの説明書きにも似たようなことが書かれている。

This property does not specify the format of the stencil buffer, which is constrained by the depth buffer format specified in RenderTexture.depth.
このプロパティ(stencilFormat)は、ステンシル バッファーの形式を指定しません。ステンシル バッファーは、RenderTexture. Depth で指定された深度バッファー形式によって制限されます。

それに伴いdepthBufferBitsのgetter, setterも処理が変わり、depthBufferBitsのsetterに関しては、内部的にはdepthStencilFormatの値も変更されている。また、変更する際にはRenderTexture.GetDepthStencilFormatLegacyが呼ばれるようになる。

Unityのどこで使用している?

URPだとUniversalRendererの_CameraDepthTexture をAllocするために必要なRenderTextureDescriptorを定義するときに指定している。ただ、場合によってFormatが違うようで、

#if UNITY_SWITCH || UNITY_ANDROID
const GraphicsFormatk_DepthStencilFormat= GraphicsFormat.D24_UNorm_S8_UInt;
const intk_DepthBufferBits= 24;
#else
const GraphicsFormat k_DepthStencilFormat = GraphicsFormat.D32_SFloat_S8_UInt;
const int k_DepthBufferBits = 32;
#endif

と、SwitchとAndroidはD24_UNorm_S8_UInt、そのほかはD32_SFloat_S8_UIntで指定している。
このD24_UNorm_S8_UIntというGraphicsFormatは、ドキュメントに書いている通りDepthとStencilの2つを32bitとして扱うように定義している。

A two-component, 32-bit packed format that has 8 unsigned integer bits in the stencil component, and 24 unsigned normalized bits in the depth component.
ステンシル コンポーネントに 8 つの符号なし整数ビット、深度コンポーネントに 24 の符号なし正規化ビットを持つ 2 コンポーネントの 32 ビット パック形式。

D24_UNORM_S8_UINT、D32_SFLOAT_S8_UINTとは?

グラフィックスAPIがサポートする深度テクスチャフォーマット。Vulkanを例に挙げると、

  • VK_FORMAT_D32_SFLOAT: 32-bit float for depth
  • VK_FORMAT_D32_SFLOAT_S8_UINT: 32-bit signed float for depth and 8 bit stencil component
  • VK_FORMAT_D24_UNORM_S8_UINT: 24-bit float for depth and 8 bit stencil component

がサポートされている。

Depth buffering - Vulkan Tutorial

Sebastian Aaltonen氏曰く、2022/11の段階ではAndroid端末におけるD24_UNORM_S8_UINTのカバー率は75%、D32_SFLOAT_S8_UINTは74%と低い。Unty的にもExperimental定義されている。

https://x.com/SebAaltonen/status/1596079110367109122

ただ、基本的にサポートされていないGraphicsFormatの場合、サポートされているGraphicsFormatに切り替わるようになるため、ExperimentalでもUniversalRendererに導入されているのだと思う。

今後はdepthStencilFormatのみ設定すればよい?

UniversalRendererで_CameraDepthTexture Alloc用RenderTextureDescriptorを定義している箇所では、

depthDescriptor.graphicsFormat = GraphicsFormat.None;
depthDescriptor.depthStencilFormat =k_DepthStencilFormat;
depthDescriptor.depthBufferBits =k_DepthBufferBits;

と、depthStencilFormatとdepthBufferBits両方を設定している。depthStencilFormatだけの変更では足りない?depthBufferBitsの設定ではdepthStencilFormatの値が変更されているから、RenderTexture.GetDepthStencilFormatLegacy 内で何か必要な設定がおこなわれている?

実験として、BreakPointを貼って値がどのように変化するかを確認してみる。

depthBufferBitsに24を代入したが、実際には32と定義されていた。depthBufferBitsのsetterで呼び出されているRenderTexture.GetDepthStencilFormatLegacy によって、現在のPlatformに適切な値に変更が加えられていそう。DepthやStencilのFormatを呼び出す場合、無難にUniversalRenderer準拠でdepthStencilFormatとdepthBufferBitsそれぞれを設定する方が良いかもしれない。

Discussion