回答:
広角レンズは、他の通常のレンズモデルと異なる動作をしてはなりません。それらは、より大きなFOV(あるD3DXMatrixPerspectiveFovLH
意味では、DirectXを使用していると仮定しています)、またはより大きな左/右および下/上値(OpenGLのglFrustum
意味)を持っています。
本当に面白い部分は魚眼レンズのモデリングにあると思います。ありますフィッシュアイはQuakeのそれはソースが付属して、あなたが勉強できること。
ただし、魚眼レンズの投影は非常に非線形です。より一般的な(監視カメラに限られている私の知る限り)種類のレンズではM
、空間内のポイントがユニット半球の表面に投影され、その表面がユニットディスクに平行投影されます。
M
x M: world position
\ M': projection of M on the unit hemisphere
\ ______ M": projection of M' on the unit disc (= the screen)
M'_x' `-.
,' |\ `.
/ | \ \
/ | \ \
| | \ |
__________|_____|____\_________|_________
M" O 1
より興味深い効果をもたらす可能性のある他の魚眼マッピングがあります。それはあなた次第です。
HLSLで魚眼効果を実装する2つの方法を見ることができます。
利点:コードを変更する必要はほとんどありません。フラグメントシェーダーは非常にシンプルです。のではなく:
...
float4 screenPoint = mul(worldPoint, worldViewProjMatrix);
...
あなたは次のようなことをします(おそらくかなり簡単にすることができます):
...
// This is optional, but it computes the z coordinate we will
// need later for Z sorting.
float4 out_Point = mul(in_Point, worldViewProjMatrix);
// This retrieves the world position so that we can project on
// the hemisphere. Normalise this vector and you get M'
float4 tmpPoint = mul(in_Point, worldViewMatrix);
// This computes the xy coordinates of M", which happen to
// be the same as M'.
out_Point.xy = tmpPoint.xy / length(tmpPoint.xyz);
...
欠点:レンダリングパイプライン全体が線形変換と考えられていたため、結果の投影は頂点に対して正確ですが、すべての変動はテクスチャ座標と同様に間違っており、三角形は歪んでいるように見えても三角形として表示されます。
回避策:より多くの三角形のサブディビジョンを使用して、洗練されたジオメトリをGPUに送信することにより、より良い近似値を取得することが許容される場合があります。これはジオメトリシェーダーでも実行できますが、このステップは頂点シェーダーの後に行われるため、ジオメトリシェーダーは独自の追加投影を実行する必要があるため、非常に複雑になります。
別の方法は、広角投影を使用してシーンをレンダリングし、その後、フルスクリーンフラグメントシェーダーを使用して画像を歪曲して魚眼効果を実現することです。
ポイントに魚眼画面のM
座標(x,y)
がある場合、それはで(x,y,z)
半球の表面に座標があることを意味しz = sqrt(1-x*x-y*y)
ます。つまり(ax,ay)
、theta
そのようなFOVでレンダリングされたシーン内の座標がありますa = 1/(z*tan(theta/2))
。(ここでの数学については100%確信が持てませんが、今夜もう一度確認します)。
したがって、フラグメントシェーダーは次のようになります。
void main(in float4 in_Point : POSITION,
uniform float u_Theta,
uniform sampler2D u_RenderBuffer,
out float4 out_FragColor : COLOR)
{
z = sqrt(1.0 - in_Point.x * in_Point.x - in_Point.y * in_Point.y);
float a = 1.0 / (z * tan(u_Theta * 0.5));
out_FragColor = tex2D(u_RenderBuffer, (in_Point.xy - 0.5) * 2.0 * a);
}
利点:ピクセル精度に起因する歪みを除いて、歪みのない完璧な投影が得られます。
欠点:FOVが180度に到達できないため、シーン全体を物理的に表示できません。また、FOVが大きいほど、画像の中心の精度が低下します。これは、最高の精度が必要な場所です。
回避策:精度の低下は、いくつかのレンダリングパス(たとえば5)を実行することで改善でき、キューブマップの方法で投影を行います。別の非常に簡単な回避策は、目的のFOVに最終画像を単純にトリミングすることです-レンズ自体に180度のFOVがある場合でも、その一部のみをレンダリングしたい場合があります。これは「フルフレーム」フィッシュアイと呼ばれます(実際に画像をトリミングしているときに「フル」なものを取得したような印象を与えるため、ちょっと皮肉です)。
(注:これが有用であるが十分に明確ではない場合は、教えてください。これについてより詳細な記事を書きたいと思います)。
はずz = sqrt(1.0 - in_Point.x * in_Point.x - in_Point.y * in_Point.y)
ですよね?
私のGLSL実装は次のとおりです。
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 v_texCoord;
uniform sampler2D u_texture;
uniform float fovTheta; // FOV's theta
// fisheye
void main (void)
{
vec2 uv = v_texCoord - 0.5;
float z = sqrt(1.0 - uv.x * uv.x - uv.y * uv.y);
float a = 1.0 / (z * tan(fovTheta * 0.5));
// float a = (z * tan(fovTheta * 0.5)) / 1.0; // reverse lens
gl_FragColor = texture2D(u_texture, (uv* a) + 0.5);
}