Unity の Shader コードだけでカメラのアスペクト比を得る

概要

 現在レンダリング中のカメラのアスペクト比を Shader コードだけで完結して得る。 _ScreenParams を用いると求める値が得られないシチュエーションが存在するため、代わりの計算手法としてプロジェクション行列の逆行列を用いる。

問題

 Unity の Built-in shader variables にアスペクト比を得るための _ScreenParams.xy が用意されているが、これはあくまで RenderTarget の解像度であるためカメラのアスペクト比とは一致しない。
 一致しない状況としてたとえば、あるオフスクリーンレンダリング用のカメラにセットされたレンダーテクスチャの解像度は 1024x1024 だが、カメラのアスペクト比自体は 16:9 である、が挙げられる。その疑似コードを以下に示す。

// MonoBehaviour

var camera = GetComponent<Camera>(); // オフスクリーンカメラ

camera.targetTexture = new RenderTexture(1024, 1024, 16);  // RenderTarget は 1024x1024

camera.aspect = 16f / 9f; // カメラのアスペクト比は 16:9
// ShaderLab HLSL

float aspect = _ScreenParams.x / _ScreenParams.y; // -> 1.0    :(

手法

 プロジェクション行列の逆行列 unity_CameraInvProjection が用意されているため、プロジェクション空間での近平面のもっとも右上の頂点を用意してビュー空間に逆変換する。

// ShaderLab HLSL

// 右上なので (x, y) = (1, 1)
// かつ近平面に存在するので z は DirectX のとき 0, OpenGL のとき -1
// w は nearClip の値
float4 projectionSpaceUpperRight = float4(1, 1, UNITY_NEAR_CLIP_VALUE, _ProjectionParams.y);

// プロジェクション行列の逆行列で変換
float4 viewSpaceUpperRight = mul(unity_CameraInvProjection, projectionSpaceUpperRight);

// 幅 / 高さ でアスペクト比
float aspect = viewSpaceUpperRight.x / viewSpaceUpperRight.y;

まとめ

 プロジェクション行列の性質を利用してアスペクト比を計算した。プロジェクション行列の周りは DirectX や OpenGL の違い、Reversed Z の違いによる環境依存が大きいので、もうちょっと賢い方法があったら教えて欲しい。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.