2016-10-27 4 views
0

Je suis mise en œuvre SSAO dans OpenGL, suivant ce tutoriel: Jhon Chapman SSAOInformatique OpenGL Normales et TBN Matrice de buffer de profondeur (de la mise en œuvre de SSAO)

Fondamentalement, la technique décrite utilise un noyau hémisphérique qui est orienté le long du fragment est normal. La position de l'espace de vision z de l'échantillon est ensuite comparée à sa valeur de tampon de profondeur d'espace d'écran. Si la valeur dans le tampon de profondeur est supérieure, cela signifie que l'échantillon s'est retrouvé dans une géométrie, donc ce fragment doit être occlus.

Le but de cette technique est de se débarrasser de l'artefact d'implémentation classique où les faces planes des objets sont grisées.

J'ai la même mise en œuvre avec 2 petits Différencies

  • Je ne suis pas en utilisant une texture de bruit pour faire tourner mon noyau, donc je artefacts baguage, qui est très bien pour l'instant
  • Je ne J'ai accès à un buffer avec des normales Per-pixel, donc je dois calculer ma matrice normale et TBN en utilisant uniquement le buffer de profondeur.

L'algorithme semble fonctionner très bien, je peux voir les fragments étant occlus, mais j'ai toujours mes visages grisés ... OMI il vient de la façon dont je calculer ma matrice TBN. Les normales semblent correctes mais quelque chose doit être faux car mon noyau ne semble pas correctement aligné, ce qui fait que les échantillons finissent dans les faces.

Les captures d'écran sont avec un noyau de 8 échantillons et un rayon de .1. le premier est seulement le résultat du passage SSAO et le second est le rendu de débogage des normales générées.

Voici le code de la fonction qui calcule la normale et TBN Matrice

 


    mat3 computeTBNMatrixFromDepth(in sampler2D depthTex, in vec2 uv) 
    { 
     // Compute the normal and TBN matrix 
     float ld = -getLinearDepth(depthTex, uv); 
     vec3 x = vec3(uv.x, 0., ld); 
     vec3 y = vec3(0., uv.y, ld); 
     x = dFdx(x); 
     y = dFdy(y); 
     x = normalize(x); 
     y = normalize(y); 
     vec3 normal = normalize(cross(x, y)); 
     return mat3(x, y, normal); 
    } 

 

Et le shader SSAO

#include "helper.glsl" 

in vec2 vertTexcoord; 
uniform sampler2D depthTex; 

const int MAX_KERNEL_SIZE = 8; 
uniform vec4 gKernel[MAX_KERNEL_SIZE]; 

// Kernel Radius in view space (meters) 
const float KERNEL_RADIUS = .1; 

uniform mat4 cameraProjectionMatrix; 
uniform mat4 cameraProjectionMatrixInverse; 

out vec4 FragColor; 


void main() 
{ 
    // Get the current depth of the current pixel from the depth buffer (stored in the red channel) 
    float originDepth = texture(depthTex, vertTexcoord).r; 

    // Debug linear depth. Depth buffer is in the range [1.0]; 
    float oLinearDepth = getLinearDepth(depthTex, vertTexcoord); 

    // Compute the view space position of this point from its depth value 
    vec4 viewport = vec4(0,0,1,1);  
    vec3 originPosition = getViewSpaceFromWindow(cameraProjectionMatrix, cameraProjectionMatrixInverse, viewport, vertTexcoord, originDepth); 

    mat3 lookAt = computeTBNMatrixFromDepth(depthTex, vertTexcoord); 
    vec3 normal = lookAt[2]; 

    float occlusion = 0.; 

    for (int i=0; i<MAX_KERNEL_SIZE; i++) 
    { 
     // We align the Kernel Hemisphere on the fragment normal by multiplying all samples by the TBN   
     vec3 samplePosition = lookAt * gKernel[i].xyz; 

     // We want the sample position in View Space and we scale it with the kernel radius 
     samplePosition = originPosition + samplePosition * KERNEL_RADIUS; 

     // Now we need to get sample position in screen space 
     vec4 sampleOffset = vec4(samplePosition.xyz, 1.0); 
     sampleOffset = cameraProjectionMatrix * sampleOffset; 
     sampleOffset.xyz /= sampleOffset.w; 

     // Now to get the depth buffer value at the projected sample position 
     sampleOffset.xyz = sampleOffset.xyz * 0.5 + 0.5; 

     // Now can get the linear depth of the sample 
     float sampleOffsetLinearDepth = -getLinearDepth(depthTex, sampleOffset.xy); 

     // Now we need to do a range check to make sure that object 
     // outside of the kernel radius are not taken into account 
     float rangeCheck = abs(originPosition.z - sampleOffsetLinearDepth) < KERNEL_RADIUS ? 1.0 : 0.0; 

     // If the fragment depth is in front so it's occluding 
     occlusion += (sampleOffsetLinearDepth >= samplePosition.z ? 1.0 : 0.0) * rangeCheck; 
    } 

    occlusion = 1.0 - (occlusion/MAX_KERNEL_SIZE); 
    FragColor = vec4(vec3(occlusion), 1.0); 
} 

Computed Normals from Depth buffer SSAO Pass

U pdate 1

Cette variation de la fonction de calcul TBN donne les mêmes résultats

mat3 computeTBNMatrixFromDepth(in sampler2D depthTex, in vec2 uv) 
{ 
    // Compute the normal and TBN matrix 
    float ld = -getLinearDepth(depthTex, uv); 
    vec3 a = vec3(uv, ld); 
    vec3 x = vec3(uv.x + dFdx(uv.x), uv.y, ld + dFdx(ld)); 
    vec3 y = vec3(uv.x, uv.y + dFdy(uv.y), ld + dFdy(ld)); 
    //x = dFdx(x); 
    //y = dFdy(y); 
    //x = normalize(x); 
    //y = normalize(y); 
    vec3 normal = normalize(cross(x - a, y - a)); 
    vec3 first_axis = cross(normal, vec3(1.0f, 0.0f, 0.0f)); 
    vec3 second_axis = cross(first_axis, normal); 
    return mat3(normalize(first_axis), normalize(second_axis), normal); 
} 

Répondre

1

Je pense que le problème est probablement que vous mélangez des systèmes de coordonnées. Vous utilisez les coordonnées de texture en combinaison avec la profondeur linéaire. Vous pouvez imaginer deux surfaces verticales légèrement tournées vers la gauche de l'écran. Les deux ont le même angle du plan vertical et devraient donc avoir la même normale à droite? Mais imaginons que l'une de ces surfaces soit beaucoup plus éloignée de l'appareil photo. Puisque les fonctions fFdx/fFdy vous indiquent essentiellement la différence avec le pixel voisin, la surface éloignée de la caméra aura une plus grande différence de profondeur linéaire sur un pixel que la surface proche de la caméra. Mais la dérivée uv.x/uv.y aura la même valeur. Cela signifie que vous obtiendrez différentes normales en fonction de la distance de la caméra.

La solution consiste à calculer la coordonnée de la vue et à utiliser la dérivée de celle-ci pour calculer la normale.

vec3 viewFromDepth(in sampler2D depthTex, in vec2 uv, in vec3 view) 
{ 
    float ld = -getLinearDepth(depthTex, uv); 

    /// I assume ld is negative for fragments in front of the camera 
    /// not sure how getLinearDepth is implemented 

    vec3 z_scaled_view = (view/view.z) * ld; 

    return z_scaled_view; 
} 

mat3 computeTBNMatrixFromDepth(in sampler2D depthTex, in vec2 uv, in vec3 view) 
{ 
    vec3 view = viewFromDepth(depthTex, uv); 

    vec3 view_normal = normalize(cross(dFdx(view), dFdy(view))); 
    vec3 first_axis = cross(view_normal, vec3(1.0f, 0.0f, 0.0f)); 
    vec3 second_axis = cross(first_axis, view_normal); 

    return mat3(view_normal, normalize(first_axis), normalize(second_axis)); 
} 
+0

Je suis sûr que je comprends noter ce que vous entendez en disant que je suis échantillonnant une seule profondeur ... Utilisation de la dFdx et dFdy sur mes vecteurs va réellement comparer deux positions de profondeur pour x et deux positions de profondeur pour l'axe y et me renvoyer les dérivées sur ces deux axes. J'ai essayé de modifier ma fonction TBN pour refléter votre commentaire, mais j'ai fini avec le même résultat, voir ** Mise à jour 1 ** –

+0

Désolé. Vous avez raison. Je n'ai pas vraiment beaucoup utilisé les fonctions dFdx/dFdy. Je n'avais pas réalisé qu'ils travaillaient comme ça. Quoi qu'il en soit, je pense toujours que le problème est que vous ne le calculez pas dans l'espace de vue. S'il vous plaît voir mon edit – bofjas