2009-07-20 5 views
4

Je lis actuellement d'une texture de profondeur dans une profondeur de post-traitement des shaders de champ utilisant le code GLSL suivant:Conversion d'un échantillon de texture de la profondeur à une distance

vec4 depthSample = texture2D(sDepthTexture, tcScreen); 
float depth = depthSample.x * 255.0/256.0 + 
       depthSample.y * 255.0/65536.0 + 
       depthSample.z * 255.0/16777216.0; 

Et puis en convertissant la valeur de la profondeur à un espace de vue distance basée sur les distances d'avion proches et lointains:

float zDistance = (zNear * zFar)/(zFar - depth * (zFar - zNear)); 

tout cela semble fonctionner assez bien, mais je suis intéressé de savoir comment faire le calcul ci-dessus basée uniquement sur la matrice de projection actuelle sans avoir besoin zNear etséparésvaleurs.

Ma première tentative implique la multiplication (vec4(tcscreen.x, tcScreen.y, depth, 1.0) * 2.0 - 1.0) par l'inverse de la matrice de projection, en divisant le résultat par par w, en prenant la valeur z résultante comme la distance, mais cela ne semble pas fonctionner. Quelle est la bonne approche ici?

En outre, lors de l'utilisation de l'écrêtage tronconique oblique pour déplacer le plan proche sur un plan de découpage choisi, la distance de plan proche est-elle potentiellement potentiellement différente pour chaque pixel? Et si c'est le cas, cela signifie-t-il que tous les shaders qui calculent une distance à partir d'une texture en profondeur doivent être conscients de ce cas et ne pas supposer une distance proche du plan constant?

Merci!

+2

J'avais en fait le problème inverse, donc votre énoncé de problème a fini par être ma solution. Merci, – imallett

Répondre

2

Il s'est avéré que j'avais oublié d'annuler la valeur Z finale pour obtenir une distance positive devant le plan proche (la caméra OpenGL regarde vers le bas -Z). Pour référence ultérieure le code GLSL pour obtenir la distance en face du plan proche est:

float depth = /* sampled from depth texture as in the original question */ ; 

vec4 screenPos = vec4(tcScreen.x, tcScreen.y, depth, 1.0) * 2.0 - 1.0; 
vec4 viewPosition = projectionMatrixInverse * screenPos; 

float z = -(viewPosition.z/viewPosition.w); 

Si vous vouliez une position mondiale de l'espace au lieu (comme SuperPro utilisait), alors que l'on retrouve en combinant la vue et projection matrices, puis en utilisant l'inverse de cette matrice plutôt que d'utiliser simplement la matrice de projection inverse. Étant donné que seuls les composants Z et W de viewPosition sont nécessaires, le GLSL ci-dessus pour le calcul viewPosition peut être légèrement simplifié. Deux produits de points suffisent au lieu d'une multiplication de matrice complète, et il n'y a pas besoin d'alimenter la matrice de projection pleine inverse dans le shader que seules les deux rangées inférieures sont nécessaires:

vec2 viewPositionZW = vec2(
    dot(projectionMatrixInverseRow2, screenPos), 
    dot(projectionMatrixInverseRow3, screenPos) 
); 

float z = -(viewPositionZW.x/viewPositionZW.y); 

La performance de c'est un peu moins bien que d'utiliser les distances proches et lointaines, sans doute à cause des produits scalaires supplémentaires, j'ai obtenu une réduction de ~ 5%. Les mathématiques proche et lointain peuvent également être optimisées en alimentant (zNear * zFar) et (zFar - zNear) en tant que constantes, mais je n'ai vu aucune amélioration mesurable en faisant cela.Fait intéressant, lorsque vous combinez ce qui précède avec une matrice de projection qui utilise un tronc de tronc de cône oblique, je ne peux rien obtenir de sensible, mais j'obtiens une sortie raisonnable lorsque j'utilise l'équation de près et de loin avec la même projection matrice, mais avec ce qui semble être une certaine distorsion des valeurs de profondeur (bien que cela puisse simplement être dû à la perte de précision de profondeur inhérente à l'écrêtage du tronc de cône oblique). Si quelqu'un peut faire la lumière sur ce qui se passe exactement mathématiquement ici, je l'apprécierais, même si cela devrait peut-être être dans une autre question.

1

J'utilise le code suivant dans un éclaireur, afin de calculer la direction de la foudre. La position Wold est également calculée en multipliant la position de l'écran par l'inverse de la matrice de projection.

Malheureusement HLSL:

float depth = tex2D(DepthMapSampler, PSIn.TexCoord).r; 

float4 screenPos; 
screenPos.x = PSIn.TexCoord.x*2.0f-1.0f; 
screenPos.y = -(PSIn.TexCoord.y*2.0f-1.0f); 
screenPos.z = depth; 
screenPos.w = 1.0f; 

float4 worldPos = mul(screenPos, xViewProjectionInv); 
worldPos /= worldPos.w; 

fonctionne très bien, donc je suppose que Worldposition est correct!

+0

Merci de m'avoir confirmé que j'étais sur la bonne voie :-), j'ai posté ma solution avec le code GLSL ci-dessous. –

Questions connexes