【UE5】チルトレンズの設計をしよう(2) ~ボケをずらす~
前回の続きです。
「ボケをずらす」って言うと日本語がちょっと変な気もしていますが、つまるところ「合焦面の傾きをPost Processで再現する」ということです。
言うだけだと良くわかんないと思いますので作例を用意してみました。
前後に並んだCubeがあったとします。F値を開放にしたとき(=よくボケる設定。)、普通ならばピントを合わせたCubeにしかもちろんピントは合いませんが、チルトレンズがあれば全部のCubeにピントを合わせることができる、ということが可能です。
その逆もできて、ピントの合う範囲を極端に狭くすることもできます。この特性を利用した写真が、皆さんがよく見かけるミニチュア風の写真です。大抵は写真の上と下にブラー処理をかけただけのやつも多いですけど、一眼レフカメラ本体に内蔵されたミニチュア加工処理で可能です。
チルト撮影はなかなかに面白くて、楽しいんです。本物のカメラでやったことがある方だとわかると思いますが単純にテンションが上がります。見たことのない景色が現れますからね。大人数と大量の自動車が行き交う交差点だとずっと見ていられる楽しさがあります。
やってみよう
ところで前回ではピントの合っている具合をグレースケールで表現していましたが、どうにもわかりにくいので、[表示]→[視覚化]→[被写界深度レイヤー]とだいたい同じにしてみようと思います。
補完に使うEase-in-outはなんでもいいと思いますが、色々試してみた結果Quint(5乗)あたりが良さげでした。
対応後はこんな感じで表現されます。わかりやすいですね。
黒:ピントが合っている箇所
緑:前ピン
青:後ピン
質問:ちなみにこれって既にエンジンあたりにMaterial Functionで持ってたりする?
1.ピントのルールを決める:ピントが合う、合わないを数値化する
まずはブラー処理を実装してしまったほうが早そうです。
そしてピントに関する値を以下のようなルールとしましょう。
・ピントが完全に合うとき:0.0。
・ピントが全然合わないとき:1.0。
回転角度の±ですが、図のように左を向いている場合で以下のようなルールとします。
・反時計回りの回転:プラス値。
・時計回りの回転:マイナス値。
2.Post Process Materialを実装する
まずはLerpノードを置きます。
・AにPost Process Sceneを接続します。
・BにCustom Nodeを接続します。
・Alphaに前回自力で求めたDepthを接続します。
で、これではおかしいので直します。前回の実装では、ピントの合う面(Focus Distance)はDepth=0.5だとしていましたけど、今回決めたルールではDepthの値の範囲は0.0~1.0ですので、下記のように変換してみます。
手順1:値の範囲を-0.5~0.5に変換する。
Depth – 0.5 とします。
⇒ピントが合うDepth=0.5が0.0に変換できました
手順2:Depth値の範囲に沿うように絶対値に変換する。
Clamp(Depth) とします。
⇒値の範囲が0.0~0.5となりました
手順3:Depth値の範囲に沿うように0.0~1.0の範囲に変換する。
Depth × 2 とします。
⇒値の範囲が0.0~1.0となりました。
しかも0.0がピントが合うときで、
ピントが手前か奥にずれているかは問いませんが、とにかくピントからのズレ量を0.0~1.0に変換できました。
ここまでの成果
Post Process Materialのエディタ画面はこんな感じです。
そしてレンダリング結果はこんな風になります。
画面の上下に極端なブラーをかけたこの絵がほしいのであれば、作業はここで終了としても構わないでしょう。
POLYCITYの街で試したところです。色の表現を[被写界深度レイヤー]と大体同じにしてみました。黒いところがピントが合っているところです。
3.チルトの考慮を加える
さてやっと本題に突入かなと思います。チルトレンズとしての考慮をマテリアルエディタに実装していきましょう。
まあすごい雑な発言ですが、結局のところDepth Mapに対してピントのズレ量を値を足したり引いたりすればいいだけのはずです。
じゃあ一旦足したりするノードを組んでみましょう。
4.Focus Distanceが正しいことの確認
というかこの[Focus Distance]パラメータですが、普通のCamera ActorのFocus Distanceと一致しているのか? という疑念がでますので検証します。
Camera Actorを同じ位置、向きに置いて、Debug PlaceをONにします。紫の面が出てきます。
例えば値を22,000とします。
チルトカメラのDepthの50%グレーが紫の面と同じになればちゃんとしたFocus Distanceで制御できていることになりますが、うまくいっています。次に進みましょう。
5.ズレる量を考える
とりあえず実装イメージを掴むため簡単に作りたいので、ΘF=45[deg]で固定して進めてみましょう。
下図でHと書いてある場所は、カメラで見えている範囲に対してのとある高さを示しています。
写真と合成するとこんなイメージになります。
じゃあこのHの場所は合焦面が45degも傾いたとき、ピントのあう距離のズレdがどのくらい奥にずれてしまうんだ?という話になります。
これは簡単でTanΘFでいいわけですから、
TanΘF = d / H
となり、距離dは
d = H・TanΘF
です。
つまりカメラで見えた高さHのところにある物体のピントの合う距離(Focus Distance)は、
Focus Distance = u + d
Focus Distance = u + H・TanΘF
で求まる事がわかります。そんでもって今回の簡易実装ではΘF=45[deg]でいっちゃおうと言っていますので、
Focus Distance = u + H・Tan(45[deg])
Focus Distance = u + H
です。
なんだかすごい簡単な話になってきました。カメラの中心から高さHの位置のピントを、Hだけずらせばいいってことになります。
6.さらに話を簡単に捉えてみます
CGの世界では次の2つのお約束があります。
・Scene Depthは標準化された世界であり、値の範囲は0.0~1.0です。
・Screenも標準化された世界であり、値の範囲は-0.5~0.5です。
これを、超乱暴ですが「2つとも同じ座標系の世界だ」と考えちゃいます。
だけども上記約束の下であれば、例えばΘF = 45[deg]のときは、高さH = dの値をスクリーン座標の高さhにしちゃっていい。という超シンプルな話にできます。
スクリーンの高さの値は[ScreenPosition]と[BreakOutFloat2Components]という2ノードで得られますので、h = H = dだから、値をそのまんまDepthに足しちゃえ! って。
そうするとノードは次のようにできます。
[TiltAngle]ノードの値は0.0となっていますが、今回の説明では値は45ということです。
簡単化とか言いましたがノードの方はしっかり組んでおきます、ΘFのところは45度で[Angle]としてパラメータ化しておきました。
現時点の状態
この状態で動作確認してみましょうか。
Playして、[Angle]パラメータをいじくると・・・これが結構ちゃんと動いてくれているように見えるという。
実は標準化したものどうしで加算してもOKだったのかな? とか思ったのですが、そんなはずない。
下図を見たとき、ScreenとDepthの長さが常に同じになるわけがない。
つづく
まあ今回の簡易実装は実験的というか寄り道というか。そんな程度に留めておきましょう。
次でラスト。簡単化した部分をちゃんと計算していきましょう。