ちょっとした小ネタです。
UE上でポストエフェクトを書いていると、近隣のピクセルのワールド位置が欲しくなる事があるのではないでしょうか。
しかしながらWorldPositionノードではUVが指定できないので取得したいピクセルを自由に選ぶ事ができません。
今回はその取得方法を解説します
参考になるコード
実はすでにUEのエンジン内には目当ての処理を実装している箇所があります。
下記の場所にあるLumenPosition.ushです。
Engine\Shaders\Private\Lumen\LumenPosition.ush
この中のGetWorldPositionFromScreenUVが参考にし易いので、こちらの手法を実際に使ってみましょう。
float3 GetWorldPositionFromScreenUV(float2 ScreenUV, float SceneDepth)
{
float2 ScreenPosition = (ScreenUV - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;
float3 WorldPosition = mul(float4(GetScreenPositionForProjectionType(ScreenPosition, SceneDepth), SceneDepth, 1), DFHackToFloat(PrimaryView.ScreenToWorld)).xyz;
return WorldPosition;
}
使ってみる
ではまず、取得したワールド位置から距離を見て適当な濃淡をつけてみる事でWorldPositionと同等の性能がある事を確認します。
テスト用に下記BPを組みました。
SphereMaskを使い、原点から半径4mをマスクします。
ワールド位置を取得する処理はカスタムノードを使っての実装となります。
カスタムノード内で書いたUVと深度からワールド位置を取得するコード
float2 uv = ClampSceneTextureUV(ViewportUVToSceneTextureUV(DERIV_BASE_VALUE(inUV), 1), 1);
float3 depth = SceneTextureLookup(uv, 1, false).rgb;
float2 screen = (uv - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;
float3 wp = mul(float4(GetScreenPositionForProjectionType(screen, depth.x), depth.x, 1), DFHackToFloat(PrimaryView.ScreenToWorld)).xyz;
return wp;
気を付ける所として、指定するUVは表示されいてる領域を0-1に正規化したものでは無く実際のUVです。
したがって、ClampSceneTextureUVとViewportUVToSceneTextureUVを使って補正を掛ける必要があります。
結果
どういった場面で使うか
実際での使いどころの一例として、上記のマスクを標準のDOFではない、自前のDOFのフォーカス範囲の判定に使ってみました。
ブラー時、サンプリング毎にワールド位置を取得する事でフォーカス範囲を判定しています。
標準DOFの後のパスでAfterDOFとして書かれた半透明にかぶせる為の機能として使ったりします。
最後に
定期的に使う必要出てくる機能です。都度やり方を調べる事が多いので自分のメモの意味も込めて書きました。
同じ事を調べていたどなたかの役に立てば幸いです。
注意点
今回はエンジン内部のコードを参考にしましたが、更新のタイミングで定義が変わったりする事があります。
実際に5.3→5.4の更新の際はLWCHackToFloatがDFHackToFloatに代わりました。
バージョンを上げる際はその辺り注意して追うようにしてください。