web-dev-qa-db-ja.com

デプスバッファーから真のZ値を取得する

シェーダーのデプスバッファーからサンプリングすると、予想どおり0〜1の値が返されます。カメラのニアクリッププレーンとファークリッププレーンが与えられた場合、このポイントでの真のz値、つまりカメラからの距離を計算するにはどうすればよいですか?

47
Hannesh

から 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パースペクティブマトリックスは次のようになります。 from songho.ca

この行列に同種の点[x、y、z、1]を掛けると、次のようになります。マトリックス内)。

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上で計算され、ユニフォームとして送信される必要があります。

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

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;
}
54
Calvin1602

これは古くて古い質問であることは知っていますが、さまざまな場面で何度もここに戻ってきたので、順方向と逆方向の変換を行うコードを共有すると思いました。

これは、@ Calvin1602の回答に基づいています。これらはGLSLまたは単純な古いCコードで動作します。

uniform float zNear = 0.1;
uniform float zFar = 500.0;

// depthSample from depthTexture.r, for instance
float linearDepth(float depthSample)
{
    depthSample = 2.0 * depthSample - 1.0;
    float zLinear = 2.0 * zNear * zFar / (zFar + zNear - depthSample * (zFar - zNear));
    return zLinear;
}

// result suitable for assigning to gl_FragDepth
float depthSample(float linearDepth)
{
    float nonLinearDepth = (zFar + zNear - 2.0 * zNear * zFar / linearDepth) / (zFar - zNear);
    nonLinearDepth = (nonLinearDepth + 1.0) / 2.0;
    return nonLinearDepth;
}
10
david van brink