0

タスクの背景

John Chapman によるチュートリアルに基づくOGLDev チュートリアル 45の後にSSAOを実装しようとしています。OGLDev チュートリアルでは、非常に単純化された方法を使用します。これは、フラグメント位置を中心とした半径内のランダム ポイントをサンプリングし、その場所に保存されている実際のサーフェス深度よりも深い深度を持つサンプリング ポイントの数に応じて AO 係数をステップアップします (より多くの位置フラグメントの周りは、オクルージョンが大きいほど、その前にあります)。

私が使用する「エンジン」には、OGLDev ほどモジュラーなディファード シェーディングはありませんが、基本的には、まず画面全体の色をテクスチャ アタッチメントと深度レンダー バッファー アタッチメントを使用してフレーム バッファーにレンダリングします。深さを比較するために、フラグメント ビュー空間の位置は、テクスチャが添付された別のフレーム バッファにレンダリングされます。これらのテクスチャは、SSAO シェーダーによって後処理され、結果は画面いっぱいのクワッドに描画されます。独自の両方のテクスチャがクワッドにうまく描画され、シェーダー入力のユニフォームも問題ないように見えるので、エンジン コードを含めていないのはそのためです。

以下に示すように、フラグメント シェーダーはほぼ同じです。個人的な理解に役立つコメントをいくつか含めました。

#version 330 core

in vec2 texCoord;
layout(location = 0) out vec4 outColor;

const int RANDOM_VECTOR_ARRAY_MAX_SIZE = 128; // reference uses 64
const float SAMPLE_RADIUS = 1.5f; // TODO: play with this value, reference uses 1.5

uniform sampler2D screenColorTexture; // the whole rendered screen
uniform sampler2D viewPosTexture; // interpolated vertex positions in view space

uniform mat4 projMat;

// we use a uniform buffer object for better performance
layout (std140) uniform RandomVectors
{
    vec3 randomVectors[RANDOM_VECTOR_ARRAY_MAX_SIZE];
};

void main()
{
    vec4 screenColor = texture(screenColorTexture, texCoord).rgba;
    vec3 viewPos = texture(viewPosTexture, texCoord).xyz;

    float AO = 0.0;

    // sample random points to compare depths around the view space position.
    // the more sampled points lie in front of the actual depth at the sampled position,
    // the higher the probability of the surface point to be occluded.
    for (int i = 0; i < RANDOM_VECTOR_ARRAY_MAX_SIZE; ++i) {

        // take a random sample point.
        vec3 samplePos = viewPos + randomVectors[i];

        // project sample point onto near clipping plane
        // to find the depth value (i.e. actual surface geometry)
        // at the given view space position for which to compare depth
        vec4 offset = vec4(samplePos, 1.0);
        offset = projMat * offset; // project onto near clipping plane
        offset.xy /= offset.w; // perform perspective divide
        offset.xy = offset.xy * 0.5 + vec2(0.5); // transform to [0,1] range
        float sampleActualSurfaceDepth = texture(viewPosTexture, offset.xy).z;

        // compare depth of random sampled point to actual depth at sampled xy position:
        // the function step(edge, value) returns 1 if value > edge, else 0
        // thus if the random sampled point's depth is greater (lies behind) of the actual surface depth at that point,
        // the probability of occlusion increases.
        // note: if the actual depth at the sampled position is too far off from the depth at the fragment position,
        // i.e. the surface has a sharp ridge/crevice, it doesnt add to the occlusion, to avoid artifacts.
        if (abs(viewPos.z - sampleActualSurfaceDepth) < SAMPLE_RADIUS) {
            AO += step(sampleActualSurfaceDepth, samplePos.z);
        }
    }

    // normalize the ratio of sampled points lying behind the surface to a probability in [0,1]
    // the occlusion factor should make the color darker, not lighter, so we invert it.
    AO = 1.0 - AO / float(RANDOM_VECTOR_ARRAY_MAX_SIZE);

    ///
    outColor = screenColor + mix(vec4(0.2), vec4(pow(AO, 2.0)), 1.0);
    /*/
    outColor = vec4(viewPos, 1); // DEBUG: draw view space positions
    //*/
}

何が機能しますか?

  • フラグメントの色のテクスチャは正しいです。
  • テクスチャ座標は、描画して [0, 1] に変換される画面塗りつぶしクワッドの座標です。それらは次のように同等の結果をもたらしますvec2 texCoord = gl_FragCoord.xy / textureSize(screenColorTexture, 0);
  • (パースペクティブ) 射影行列は、カメラが使用するものであり、その目的のために機能します。いずれにせよ、これは問題ではないようです。
  • 意図したとおり、ランダム サンプル ベクトル コンポーネントは [-1, 1] の範囲にあります。
  • フラグメント ビュー スペースの位置のテクスチャは問題ないようです。

フラグメント ビュー スペースの位置

どうしたの?

フラグメント シェーダーの下部にある AO ミキシング ファクターを 0 に設定すると、fps キャップまでスムーズに実行されます (計算がまだ実行されていても、少なくともコンパイラーはそれを最適化しないと思います :D )。しかし、AO が混在している場合、フレーム描画ごとに最大 80 ミリ秒かかります (バッファーがいっぱいになっているかのように、時間とともに遅くなります)。結果は非常に興味深く、混乱を招きます。

サオノイズ問題

明らかに、マッピングは遠く離れているように見え、ちらつきノイズは、あたかもランダム サンプル ベクトルに直接対応しているかのように、非常にランダムに見えます。オクルージョン計算によるものではなく、AO 係数の追加によってのみ描画時間が大幅に増加したことは、最も興味深いことでした。描画バッファに問題はありますか?

4

1 に答える 1

0

この問題は、選択したテクスチャ タイプに関連しているように見えました。

ハンドル付きのテクスチャは、単に ではなくviewPosTexturefloat テクスチャ フォーマットGL_RGB16Fまたはとして明示的に定義する必要がありました。興味深いことに、個別のテクスチャはうまく描画され、問題は組み合わせでのみ発生しました。GL_RGBA32FGL_RGB

// generate screen color texture
// note: GL_NEAREST interpolation is ok since there is no subpixel sampling anyway
glGenTextures(1, &screenColorTexture);
glBindTexture(GL_TEXTURE_2D, screenColorTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, windowWidth, windowHeight, 0, GL_BGR, GL_UNSIGNED_BYTE, NULL);

// generate depth renderbuffer. without this, depth testing wont work.
// we use a renderbuffer since we wont have to sample this, opengl uses it directly.
glGenRenderbuffers(1, &screenDepthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, screenDepthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, windowWidth, windowHeight);

// generate vertex view space position texture
glGenTextures(1, &viewPosTexture);
glBindTexture(GL_TEXTURE_2D, viewPosTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, windowWidth, windowHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);

描画が遅いのは GLSL mix 機能が原因の可能性があります。その上でさらに調査します。

ちらつきは、各フレームでの新しいランダム ベクトルの再生成と通過によるものです。一度十分なランダムベクトルを渡すだけで問題が解決します。それ以外の場合は、SSAO の結果をぼかすと役立つ場合があります。

基本的に、SSAO は動作するようになりました。今では多かれ少なかれ明らかなバグです。

ここに画像の説明を入力

于 2015-05-23T14:51:20.880 に答える