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);
}
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);
}
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 ** –
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