深度バッファから真のZ値を取得する 質問する

深度バッファから真のZ値を取得する 質問する

シェーダーの深度バッファからサンプリングすると、予想どおり 0 から 1 の間の値が返されます。カメラのニアクリッププレーンとファークリッププレーンを考えると、この時点での実際の Z 値、つまりカメラからの距離をどのように計算すればよいでしょうか。

ベストアンサー1

からhttp://web.archive.org/web/20130416194336/http://olivers.posterous.com/linear-depth-in-glsl-for-real

// == Post-process frag shader ===========================================
uniform sampler2D depthBuffTex;
uniform float zNear;
uniform float zFar;
varying vec2 vTexCoord;
void main(void)
{
    float z_b = texture2D(depthBuffTex, vTexCoord).x;
    float z_n = 2.0 * z_b - 1.0;
    float z_e = 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear));
}

[編集] では、説明は以下のとおりです (2 つの間違いがあります。以下の Christian のコメントを参照してください)。

OpenGL パースペクティブ マトリックスは次のようになります。songho.caより

この行列に同次点 [x,y,z,1] を掛けると、[don't care, don't care, Az+B, -z] が得られます (A と B は行列の 2 つの大きな要素です)。

次に、OpenGL はパースペクティブ除算を実行します。つまり、このベクトルを w コンポーネントで除算します。この操作はシェーダー (シャドウマッピングなどの特殊な場合を除く) ではなくハードウェアで実行されるため、制御できません。w = -z なので、Z 値は -A/z -B になります。

現在、正規化されたデバイス座標になっています。Z 値は 0 から 1 の間です。何らかの愚かな理由で、OpenGL ではこれを [-1,1] の範囲 (x と y と同様) に移動する必要があります。スケーリングとオフセットが適用されます。

この最終値はバッファに保存されます。

上記のコードはまったく逆のことを行います。

  • z_bはバッファに格納されている生の値です
  • z_nはz_bを[-1,1]から[0,1]に線形変換する。
  • z_e は、z_n=-A/z_e -B と同じ式ですが、代わりに z_e について解きます。これは、z_e = -A / (z_n+B) と同等です。ちなみに、A と B は CPU で計算され、uniforms として送信される必要があります。

反対の機能は次のとおりです。

varying float depth; // Linear depth, in world units
void main(void)
{
    float A = gl_ProjectionMatrix[2].z;
    float B = gl_ProjectionMatrix[3].z;
    gl_FragDepth  = 0.5*(-A*depth + B) / depth + 0.5;
}

おすすめ記事