0

に格納されている深度値からフラグメントの位置を再構築しようとしていGL_DEPTH_ATTACHMENTます。これを行うには、深度を線形化し、深度にカメラ位置からファー プレーン上の対応するポイントへの光線を掛けます。

この方法は、ここで説明する 2 番目の方法です。カメラからファー プレーンへのレイを取得するために、ファー プレーンの 4 つのコーナーへのレイを取得し、それらを頂点シェーダーに渡し、フラグメント シェーダーに補間します。次のコードを使用して、カメラからワールド空間の遠方平面の角までの光線を取得しています。

std::vector<float> Camera::GetFlatFarFrustumCorners() {
    // rotation is the orientation of my camera in a quaternion.
    glm::quat inverseRotation = glm::inverse(rotation);
    glm::vec3 localUp = glm::normalize(inverseRotation * glm::vec3(0.0f, 1.0f, 0.0f));
    glm::vec3 localRight = glm::normalize(inverseRotation * glm::vec3(1.0f, 0.0f, 0.0f));
    float farHeight = 2.0f * tan(90.0f / 2) * 100.0f;
    float farWidth = farHeight * aspect;

    // 100.0f is the distance to the far plane. position is the location of the camera in word space.
    glm::vec3 farCenter = position + glm::vec3(0.0f, 0.0f, -1.0f) * 100.0f;
    glm::vec3 farTopLeft = farCenter + (localUp * (farHeight / 2)) - (localRight * (farWidth / 2));
    glm::vec3 farTopRight = farCenter + (localUp * (farHeight / 2)) + (localRight * (farWidth / 2));
    glm::vec3 farBottomLeft = farCenter - (localUp * (farHeight / 2)) - (localRight * (farWidth / 2));
    glm::vec3 farBottomRight = farCenter - (localUp * (farHeight / 2)) + (localRight * (farWidth / 2));

    return { 
        farTopLeft.x, farTopLeft.y, farTopLeft.z,
        farTopRight.x, farTopRight.y, farTopRight.z,
        farBottomLeft.x, farBottomLeft.y, farBottomLeft.z,
        farBottomRight.x, farBottomRight.y, farBottomRight.z
    };
}

これは、ワールド空間でファー プレーンのコーナーを取得する正しい方法ですか?

これらのコーナーをシェーダーで使用すると、結果が正しくなく、得られるものがビュー スペースにあるように見えます。これらは私が使用しているシェーダーです:

頂点シェーダー:

layout(location = 0) in vec2 vp;
layout(location = 1) in vec3 textureCoordinates;

uniform vec3 farFrustumCorners[4];
uniform vec3 cameraPosition;

out vec2 st;
out vec3 frustumRay;

void main () {
    st = textureCoordinates.xy;
    gl_Position = vec4 (vp, 0.0, 1.0);
    frustumRay = farFrustumCorners[int(textureCoordinates.z)-1] - cameraPosition;
}

フラグメント シェーダー:

in vec2 st;
in vec3 frustumRay;

uniform sampler2D colorTexture;
uniform sampler2D normalTexture;
uniform sampler2D depthTexture;

uniform vec3 cameraPosition;
uniform vec3 lightPosition;

out vec3 color;

void main () {
    // Far and near distances; Used to linearize the depth value.
    float f = 100.0;
    float n = 0.1;
    float depth = (2 * n) / (f + n - (texture(depthTexture, st).x) * (f - n));
    vec3 position = cameraPosition + (normalize(frustumRay) * depth);
    vec3 normal = texture(normalTexture, st);


    float k = 0.00001;
    vec3 distanceToLight = lightPosition - position;
    float distanceLength = length(distanceToLight);
    float attenuation = (1.0 / (1.0 + (0.1 * distanceLength) + k * (distanceLength * distanceLength)));
    float diffuseTemp = max(dot(normalize(normal), normalize(distanceToLight)), 0.0);
    vec3 diffuse = vec3(1.0, 1.0, 1.0) * attenuation * diffuseTemp;

    vec3 gamma = vec3(1.0/2.2);
    color = pow(texture(colorTexture, st).xyz+diffuse, gamma);

    //color = texture(colorTexture, st);
    //colour.r = (2 * n) / (f + n - texture( tex, st ).x * (f - n));
    //colour.g = (2 * n) / (f + n - texture( tex, st ).y* (f - n));
    //colour.b = (2 * n) / (f + n - texture( tex, st ).z * (f - n));
}

これは、これらのシェーダーの下で私のシーンの照明がどのように見えるかです: 恐ろしい照明

これは、再構築した位置が完全に間違っているか、間違った空間にあることが原因であると確信しています。再構築の何が問題なのですか?それを修正するにはどうすればよいですか?

4

1 に答える 1

2

最初にやりたいことは、ワールド/ビュー スペース (実際には、ここで再構築しようとしているスペースが何であれ) に各フラグメントの初期位置を格納する、G バッファー セットアップへの一時的な追加を開発することです。次に、深度バッファーからこれらの位置を再構築するだけのシェーダーを作成します。画面の半分に元の G バッファーが表示され、残りの半分に再構築された位置が表示されるようにすべてを設定します。このようにして、矛盾をすぐに見つけることができるはずです。

そうは言っても、深度バッファーから (オブジェクト空間) 位置を再構築するために過去に使用した実装を確認することをお勧めします。基本的に、最初にビュー スペースに入り、次に逆モデル ビュー マトリックスを使用してオブジェクト スペースに移動します。ワールド空間に合わせて簡単に調整できます。おそらく最も柔軟な実装ではありません.FOVがハードコードされているためですが、代わりにユニフォームを使用するように簡単に変更できます...

フラグメント シェーダーを縮小:

flat in mat4 inv_mv_mat;
     in vec2 uv;

...

float linearZ (float z)
{
#ifdef INVERT_NEAR_FAR
  const float f = 2.5;
  const float n = 25000.0;
#else
  const float f = 25000.0;
  const float n = 2.5;
#endif

  return n / (f - z * (f - n)) * f;
}

vec4
reconstruct_pos (float depth)
{
  depth = linearZ (depth);

  vec4 pos = vec4 (uv * depth, -depth, 1.0); 
  vec4 ret = (inv_mv_mat * pos);

  return ret / ret.w;
}

次のように、ディファード シェーディング ライティング パスの頂点シェーダー ステージで少し追加のセットアップが必要です。

#version 150 core

in       vec4 vtx_pos;
in       vec2 vtx_st;

uniform  mat4 modelview_mat; // Matrix used when the G-Buffer was built
uniform  mat4 camera_matrix; // Matrix used to stretch the G-Buffer over the viewport

uniform float buffer_res_x;
uniform float buffer_res_y;

     out vec2 tex_st;
flat out mat4 inv_mv_mat;
     out vec2 uv;


// Hard-Coded 45 degree FOV
//const float fovy = 0.78539818525314331; // NV pukes on the line below!
//const float fovy = radians (45.0);
//const float tan_half_fovy = tan (fovy * 0.5);

const float   tan_half_fovy = 0.41421356797218323;

      float   aspect        = buffer_res_x / buffer_res_y;
      vec2    inv_focal_len = vec2 (tan_half_fovy * aspect,
                                    tan_half_fovy);

const vec2    uv_scale     = vec2 (2.0, 2.0);
const vec2    uv_translate = vec2 (1.0, 1.0);


void main (void)
{
  inv_mv_mat  = inverse (modelview_mat);
  tex_st      = vtx_st;
  gl_Position = camera_matrix * vtx_pos;
  uv          = (vtx_st * uv_scale - uv_translate) * inv_focal_len;
}

深度範囲の反転は、ディファード シェーディングに役立つと思われるものです。通常、パースペクティブ深度バッファーは、近距離では必要以上の精度を提供し、高品質の再構築には十分ではありません。深度範囲を反転して物事を反転させると、ハードウェアの深度バッファーを使用しながら、物事を少し均等にすることができます。これについては、こちらで詳しく説明しています。

于 2013-11-03T06:42:27.363 に答える