Pregunta

Actualmente estoy leyendo desde una profundidad de textura en una profundidad de postprocesado de sombreado de campo utilizando el siguiente código de GLSL:

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;

Y a continuación, convertir el valor de la profundidad a una distancia vista de espacio en función de las distancias del plano de cerca y de lejos:

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

Todo esto parece que funciona bastante bien, sin embargo, estoy interesado en saber cómo hacer el cálculo anterior basado sólo en la matriz de proyección actual sin necesidad valores zNear y zFar separadas.

Mi intento inicial involucrado multiplicando (vec4(tcscreen.x, tcScreen.y, depth, 1.0) * 2.0 - 1.0) por la inversa de la matriz de proyección, dividiendo el resultado a través de por w, a continuación, tomando el valor z resultante como la distancia, pero esto no parece funcionar. ¿Cuál es el enfoque correcto aquí?

Además, al utilizar tronco de cono oblicuo de recorte para cambiar el avión cerca sobre un plano de recorte elegido es la distancia avión cerca ahora potencialmente diferente para cada píxel? Y si es así, ¿significa esto que los shaders que calculan una distancia desde una profundidad de textura deben ser conscientes de este caso y no asumir una distancia constante de avión cerca?

Gracias!

¿Fue útil?

Solución

Resulta que me había olvidado de negar el valor Z final para obtener una distancia positiva en la parte delantera del avión cerca (OpenGL cámara mira hacia abajo -Z). Para futuras referencias del código GLSL para conseguir la distancia frente al avión cerca es:

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 querías una posición en el espacio mundo en lugar (como SuperPro estaba usando), entonces eso se puede encontrar mediante la combinación de la vista y de proyección matrices y luego utilizando la inversa de la matriz en lugar de sólo mediante la inversa de la matriz de proyección.

Debido a que sólo se necesitan la Z y los componentes W de viewPosition la GLSL anteriormente para viewPosition computación puede simplificarse un poco. Dos productos de punto será suficiente en lugar de una multiplicación de la matriz completa, y no hay necesidad de alimentar a la matriz completa proyección inversa en el shader se necesitan como sólo las dos filas inferiores:

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

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

El rendimiento de este es un poco peor que el uso de las distancias cercanas y lejanas, presumiblemente a causa de los productos de punto extra, me dieron una reducción de ~ 5%. La matemática distancia cerca y lejos también se puede optimizar mediante la alimentación (zNear * zFar) y (zFar - zNear) en tan constantes, pero no vi ninguna mejora medible al hacer esto.

Es interesante que, cuando se combinan lo anterior con una matriz de proyección que utiliza cono truncado oblicuo recorte no puedo conseguir algo sensato fuera de él, pero yo sí obtener una salida razonable al utilizar la ecuación de cerca y de lejos la distancia con la misma matriz de proyección , aunque con lo que parece ser una cierta distorsión de los valores de profundidad (aunque esto podría ser simplemente debido a la pérdida de precisión inherente a la profundidad recorte de cono truncado oblicuo). Si alguien puede arrojar algo de luz sobre lo que está pasando exactamente en matemáticamente aquí lo agradecería, aunque quizás eso debería estar en una pregunta diferente.

Otros consejos

utilizo el siguiente código en un shader rayo, con el fin de calcular la dirección del rayo. posición Wold también se calcula multiplicando la posición de la pantalla con la inversa de la matriz de proyección.

Desafortunadamente 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;

funciona bien, así que supongo Worldposition es correcta!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top