シェーダでピクセルを数える


Unreal Engine 4 (UE4) その2 Advent Calendar 2017の12日目です

# やりたいこと
オブジェクトの可視判定を正確に取りたい！
ゲーム用の判定であればコリジョンを設定してRayを飛ばせば完了だが、もっと厳密な判定をしたい！
ひらひら舞うスカートから下着が見えていないか、厳密にチェックしたい！

ということで、ピクセル単位での可視をリアルタイムで判断できるようなものを実装してみます。
利用しているのはUE4.18.1です。

実装したもの　↓
ユニティちゃんの画面描画ピクセル数と、座標平均値のマーカがが表示されています。

<blockquote class="twitter-video" data-lang="ja"><p lang="ja" dir="ltr">こはく先輩の描画ピクセル数を数えた <a href="https://t.co/oWY3SX3Cn7">pic.twitter.com/oWY3SX3Cn7</a></p>&mdash; はるべえ (@ruyo_h) <a href="https://twitter.com/ruyo_h/status/940374955350310912?ref_src=twsrc%5Etfw">2017年12月12日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


<img src="https://qiita-image-store.s3.amazonaws.com/0/36233/a67df663-0794-c044-3e8e-04710ade4ef2.png" width="25%"> <img src="https://qiita-image-store.s3.amazonaws.com/0/36233/062b2f8e-35d1-cb5b-121f-3dbd136f65d7.png" width="25%"> <img src="https://qiita-image-store.s3.amazonaws.com/0/36233/46017d65-769f-2e43-8ec2-17f0606d2ae3.png" width="25%">

※伝わらないですがリアルタイムで滑らかに動作しています。


# ざっくり方針

対象オブジェクトのCustomDepthへ書き込みを有効にして、そのバッファをComputeShaderでチェックします。
CPUでも実現できると思いますが、勉強がてらShaderでやってみます。あとCPUだとなんとなく処理が重そうな気がします。

概要は以下です。

- Shaderプラグインのサンプルを導入して、ComputeShaderのタスクを呼び出せるようにする
- ComputeShaderからCustomDepthを参照できるようにする
- バッファをチェックする

# UE4ShaderPluginDemoの導入

こちらを利用させて頂きました。
https://github.com/Temaran/UE4ShaderPluginDemo

本家の更新は止まっていますが、PullRequestにシェーダプラグイン対応(UE4.17以降)を上げている方がいます。ありがたく取り込みます。
ComputeShaderUsageExample.hにある、ExecuteComputeShader を呼べば入り口は完成です。
Blueprintから呼べるよう ノードを追加しておきます。

# UE4ShaderPluginDemoからの変更
改造していきます。
###Depthを参照できるようにする
主にcpp側の対応です。
PixelShaderサンプルをコピペ元にして、TextureParameter、TextureParameterSRV の処理を追加します。

可視判定に利用するバッファは 以下のコードで参照できます。
また、ComputeShader.Build.csにはRendererへの依存リストも追加します。

※今回の実装では、Depth,CustomDepthあたりしか参照できません。
深追いしてないですが、GBufferを参照するにはComputeShaderの実行タイミングを変更するか、なんらか前フレームの絵を保存しておく必要がありそうです。

```cpp:ComputeShaderUsageExample.cpp
// 略
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
if (SceneContext.GBufferA) {
	TextureParameter = (const FTexture2DRHIRef&)(SceneContext.GBufferA->GetRenderTargetItem().TargetableTexture);
}
if (SceneContext.SceneDepthZ) {
	TextureParameter = (const FTexture2DRHIRef&)(SceneContext.SceneDepthZ->GetRenderTargetItem().TargetableTexture);
}
if (SceneContext.CustomDepth) {
	TextureParameter = (const FTexture2DRHIRef&)(SceneContext.CustomDepth->GetRenderTargetItem().TargetableTexture);
}

if (NULL != TextureParameterSRV)
{
	TextureParameterSRV.SafeRelease();
	TextureParameterSRV = NULL;
}
TextureParameterSRV = RHICreateShaderResourceView(TextureParameter, 0);
ComputeShader->SetSurfaces(RHICmdList, TextureParameterSRV);
// 略
```

```cs:ComputeShader.Build.cs
	// -- 略
	PublicDependencyModuleNames.AddRange(
		new string[]
		{
			"Core",
			"CoreUObject",
			"Engine",
			"RenderCore",
			"ShaderCore",
			"RHI",
			"Renderer",	// <-- 追加
		}
	);
	// -- 略
```


###ピクセルカウンタの追加
主にusf側の対応です。
元のComputeShaderサンプルはOutputSurfaceに画像を書き込むプログラムです。
OutputSurfaceはUAVなので、今回はこれをそのままピクセル数の記録先にします。
CustomDepthが書き込まれていたらカウンタ+1します。（SceneDepthより奥にあるピクセルはカウントしません）

```cpp:ComputeShaderExample.usf
	float sizeX, sizeY;
	TextureParameter.GetDimensions(sizeX, sizeY);

	float2 iResolution = float2(sizeX, sizeY);
	float2 uv = (ThreadId.xy / iResolution.xy) - 0.5;

	float f1 = (float)TextureParameterDepth.Load(int3(sizeX * uv.x, sizeY * uv.y, 0));
	float f2 = (float)TextureParameterCustomDepth.Load(int3(sizeX * uv.x, sizeY * uv.y, 0));

	if (f1>0 && f1<=f2){
		float2 v = {0,0};
		uint temp;
		InterlockedAdd(OutputSurface[v], 1U, temp);
	}
```

これをCPU側で取り出して、BlueprintなりUMGに渡して完成です。

# 最後に

UE4ShaderPluginDemo はシェーダまわりの一通りの実装が入っており、とても参考になりました。
解説を省略しましたが、実装したものは描画ピクセル座標の平均値にマーカーが出るようにしてあります。
OutputSurfaceに情報を埋め込めば 他にも色々できそうです。
オブジェクトの可視判定としては、特定のマテリアルやShaderModelIDで判別できれば実用的かな と思うところです。

<img src="https://qiita-image-store.s3.amazonaws.com/0/36233/383ba462-8932-0bdc-a11c-9ae648d171a1.png" width="50%">

以上です。お疲れ様でした。




<div><img src="http://unity-chan.com/images/imageLicenseLogo.png" alt="ユニティちゃんライセンス"><p>この作品は<a href="http://unity-chan.com/contents/license_jp/" target="_blank">ユニティちゃんライセンス条項</a>の元に提供されています</p></div>