
Unreal Engine 5.3から、ビューポートサイズを動的に動かしたときに固定されるカメラのデフォルトのFOVが水平FOV(X FOV)から垂直FOV(Y FOV)に変更になりました。リリースノートにも
Changed the default aspect ratio constraint to maintain Y FOV since camera FOV is now always handled as horizontal FOV. If preserving the exact framing for a given window size is important, you can specify the old setting value (maintain X FOV) in DefaultEngine.ini.
と書かれています。コード上ではEAspectRatioAxisConstraint::AspectRatio_MaintainXFOVがEAspectRatioAxisConstraint::AspectRatio_MaintainYFOVに変更になりました。
が、少なくとも私はこの説明の意味が全く分からなかったのですが、私だけ??でしょうか???
上の動画でビューポートサイズの横幅が変化したときに3DCGの大きさは変わらずに両横がトリミングされるか、或いはいままで見えていなかった左右分が追加で描画されていますので、確かに垂直FOVが固定されています。が、だとすると
camera FOV is now always handled as horizontal FOV.
の意味が全く分かりません。垂直FOVが固定ということは水平FOVが動的に変化するのですが、にもかかわらずカメラFOVとしては水平FOVを指定するというのはどういうことなのでしょうか?
APlayerCameraManager (PlayerCameraManager.h)クラス内ではFOVは下記で指定しますが、
|
1 2 3 |
/** FOV to use by default. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=PlayerCameraManager) float DefaultFOV; |
この情報はFMinimalViewInfo構造体(CameraTypes.h)の変数FOVに渡されます。
…が、その中身を見てみると…
|
1 2 3 |
/** The horizontal field of view (in degrees) in perspective mode (ignored in orthographic mode). */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Camera) float FOV; |
と書かれており、どうやら常に水平FOVとして扱われるそうです。よくわかりません。
さらにコードを追っていくと、プロジェクション変換行列が作られる部分で非常に謎な仕様になっています。具体的にはFMinimalViewInfo::CalculateProjectionMatrixGivenViewRectangle (CameraStackTypes.cpp)関数なのですが、重要なところだけ抜粋すると下記のとおりです。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// If x is bigger, and we're respecting x or major axis, AND mobile isn't forcing us to be Y axis aligned const bool bMaintainXFOV = ((SizeX > SizeY) && (AspectRatioAxisConstraint == AspectRatio_MajorAxisFOV)) || (AspectRatioAxisConstraint == AspectRatio_MaintainXFOV); if (bMaintainXFOV) { // If the viewport is wider than it is tall XAxisMultiplier = 1.0f; YAxisMultiplier = SizeX / (float)SizeY; } else { // If the viewport is taller than it is wide XAxisMultiplier = SizeY / (float)SizeX; YAxisMultiplier = 1.0f; } |
|
1 2 3 4 5 6 7 8 9 |
if (!bMaintainXFOV && ViewInfo.AspectRatio != 0.f && !CVarUseLegacyMaintainYFOV.GetValueOnGameThread()) { // The view-info FOV is horizontal. But if we have a different aspect ratio constraint, we need to // adjust this FOV value using the aspect ratio it was computed with, so we that we can compute the // complementary FOV value (with the *effective* aspect ratio) correctly. const float HalfXFOV = FMath::DegreesToRadians(FMath::Max(0.001f, ViewInfo.FOV) / 2.f); const float HalfYFOV = FMath::Atan(FMath::Tan(HalfXFOV) / ViewInfo.AspectRatio); MatrixHalfFOV = HalfYFOV; } |
ViewInfo.FOVがユーザが指定したFOVで固定されていればぱっと見ではHalfXFOVも固定されていて、HalfYFOVが調整されている…ように見えますが、それでは上の動画の挙動になりません。固定されているのは垂直FOV、つまりHalfYFOVのはずです。
実はHalfYFOVのところで出てくるViewInfo.AspectRatioはビューポートの現在のサイズで動的に求められた数値ではなく、APlayerCameraManager内で
|
1 |
DefaultAspectRatio = 1.33333f; |
と固定されており、これがFMinimalViewInfo.AspectRatioに渡されます。HalfXFOVもHalfYFOVも固定されているのです!しかしこのままでは水平FOV、垂直FOVがどちらも固定されていることになり、表示される3DCGが伸び縮みしてしまいそうです。
実はこのコードの少し後では下記の実装になっています。
|
1 2 3 4 5 6 7 8 |
InOutProjectionData.ProjectionMatrix = FReversedZPerspectiveMatrix( MatrixHalfFOV, MatrixHalfFOV, XAxisMultiplier, YAxisMultiplier, ClippingPlane, ClippingPlane ); |
さらにこのFReversedZPerspectiveMatrixの中身を見ると、行列の1行1列目がXAxisMultiplier / Tan(MatrixHalfFOV)、2行2列目がYAxisMultiplier / Tan(MatrixHalfFOV)になっていることがわかります。
AspectRatioAxisConstraint == AspectRatio_MaintainYFOVの場合はXAxisMultiplier = SizeY / SizeX,
YAxisMultiplier = 1.0f;でSizeX, SizeYが実際のビューポートのサイズなので、つまり
行列の1行1列目が(SizeY / SizeX) / Tan(MatrixHalfFOV)、2行2列目が1 / Tan(MatrixHalfFOV)となり、垂直FOVが固定されています。しかしここで計算されているMatrixHalfFOVはビューポートの実際の縦横とは関係のないViewInfo.AspectRatioを基に計算されたものでした。
つまりUnreal Engineのデフォルトの仕様では、DefaultFOVを指定しても、その値は水平FOVにも垂直FOVにもなりません。
…って意味わからなくないですか??
ビューポートサイズが動的に変わってもFMinimalViewInfo.AspectRatioの値が全く関係ない数値になっているのもわかりません。
現状、直感的にわかりやすくするにはAPlayerCameraManager.DefaultAspectRatio = 1.0fにすることです。こうすれば、AspectRatioAxisConstraint == AspectRatio_MaintainYFOVの場合に垂直FOVがDefaultFOVに一致します。DefaultAspectRatioはエンジンの他の部分でも使われているようなのですが、1.0fに変えることで他の部分に影響が出るかどうかまでは検証出来ていません、すみません。
なぜこれに気付いたかというと、表示される3DCGの大きさに合わせてカメラ位置を調整するという、ゲームでは定番のことをやろうとしたものの、なんかFOVの値おかしくないか??と思ったからです。
他にもUnreal Engineならではの落とし穴があったのですが、うまくいくと下図のように、全体の3DCGの大きさに合わせて自動的に良い感じのカメラ位置になってくれるのでした。

