Question

Je lis actuellement d'une texture de profondeur dans une profondeur de post-traitement des shaders de champ utilisant le code GLSL suivant:

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 profondeur à une distance de l'espace de vue sur la base des distances du plan proche et lointain:

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

Cela semble tout 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 de valeurs zNear et zFar séparés.

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 de z résultant de la distance, mais cela ne semble pas fonctionner. Quelle est la bonne approche ici?

En outre, l'utilisation de tronc de cône oblique de détourage pour déplacer l'avion près sur un plan de découpe choisi est la distance d'avion près maintenant potentiellement différent pour chaque pixel? Et si oui ne signifie alors ce que tout shader qui calculent une distance d'une texture de profondeur doivent être au courant de ce cas et ne pas assumer une distance d'avion près constant?

Merci!

Était-ce utile?

La solution

Il se trouve que j'avais oublié de nier la valeur finale Z pour obtenir une distance positive en face du plan près (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 voulez une position mondiale de l'espace au lieu (comme SuperPro utilisait), alors que l'on retrouve en combinant la vue et les matrices de projection, puis en utilisant l'inverse de cette matrice plutôt que d'utiliser simplement l'inverse de la matrice de projection.

Parce que les composants Z et W de viewPosition sont nécessaires GLSL ci-dessus pour viewPosition calcul peut être simplifié un peu. 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 lointains, sans doute à cause des produits de points supplémentaires, je suis un ~ 5% de réduction. Le calcul de la distance près et de loin peut également être optimisé par l'alimentation (zNear * zFar) et (zFar - zNear) en tant que constantes, mais je ne vois aucune amélioration mesurable en faisant cela.

Fait intéressant, lorsque vous combinez ci-dessus avec une matrice de projection qui utilise tronc de cône oblique écrêtage je ne peux pas obtenir quelque chose de sensé sortir, mais j'obtenir une sortie raisonnable lorsque vous utilisez l'équation à distance près ou de loin avec la même matrice de projection , mais avec ce qui semble être une certaine distorsion des valeurs de profondeur (bien que cela pourrait juste être en raison de la perte de précision de la profondeur inhérente à découpage de cône tronqué oblique). Si quelqu'un peut faire la lumière sur ce qui se passe exactement mathématiquement ici, je vous en serais reconnaissant, mais peut-être que cela devrait être dans une autre question.

Autres conseils

J'utilise le code suivant dans un shader de foudre, afin de calculer la direction de la foudre. Wold la position est également calculée en multipliant la position de l'écran à 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!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top