opengl - health - Obteniendo el verdadero valor z del buffer de profundidad
búfer (2)
Desde http://web.archive.org/web/20130416194336/http://olivers.posterous.com/linear-depth-in-glsl-for-real
// == Post-process frag shader ===========================================
uniform sampler2D depthBuffTex;
uniform float zNear;
uniform float zFar;
varying vec2 vTexCoord;
void main(void)
{
float z_b = texture2D(depthBuffTex, vTexCoord).x;
float z_n = 2.0 * z_b - 1.0;
float z_e = 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear));
}
[edit] Así que aquí está la explicación (con 2 errores, vea el comentario de Christian a continuación):
Una matriz de perspectiva OpenGL se ve así:
Cuando multiplicas esta matriz por un punto homogéneo [x, y, z, 1], te da: [no me importa, no me importa, Az + B, -z] (con A y B los 2 componentes grandes) en la matriz).
OpenGl next hace la división de perspectiva: divide este vector por su componente w. Esta operación no se realiza en sombreadores (excepto en casos especiales como el sombreado) sino en hardware; no puedes controlarlo w = -z, entonces el valor Z se convierte en -A / z -B.
Ahora estamos en las coordenadas del dispositivo normalizado. El valor Z está entre 0 y 1. Por algún motivo estúpido, OpenGL requiere que se mueva al rango [-1,1] (como x e y). Se aplica una escala y un desplazamiento.
Este valor final se almacena en el búfer.
El código anterior hace exactamente lo contrario:
- z_b es el valor en bruto almacenado en el búfer
- z_n transforma linealmente z_b de [-1,1] a [0,1]
- z_e es la misma fórmula que z_n = -A / z_e -B, pero resuelto para z_e en su lugar. Es equivalente a z_e = -A / (z_n + B). A y B deben calcularse en la CPU y enviarse como uniformes, por cierto.
La función opuesta es:
varying float depth; // Linear depth, in world units
void main(void)
{
float A = gl_ProjectionMatrix[2].z;
float B = gl_ProjectionMatrix[3].z;
gl_FragDepth = 0.5*(-A*depth + B) / depth + 0.5;
}
El muestreo de un búfer de profundidad en un sombreador devuelve valores entre 0 y 1, como se esperaba. Teniendo en cuenta los planos de clip de la cámara, ¿cómo calculo el verdadero valor z en este punto, es decir, la distancia desde la cámara?
Sé que esta es una vieja y vieja pregunta, pero me he encontrado aquí más de una vez en varias ocasiones, así que pensé en compartir mi código que hace las conversiones directa e inversa.
Esto se basa en la respuesta de @ Calvin1602. Funcionan en GLSL o en el código antiguo C simple.
uniform float zNear = 0.1;
uniform float zFar = 500.0;
// depthSample from depthTexture.r, for instance
float linearDepth(float depthSample)
{
depthSample = 2.0 * depthSample - 1.0;
float zLinear = 2.0 * zNear * zFar / (zFar + zNear - depthSample * (zFar - zNear));
return zLinear;
}
// result suitable for assigning to gl_FragDepth
float depthSample(float linearDepth)
{
float nonLinearDepth = (zFar + zNear - 2.0 * zNear * zFar / linearDepth) / (zFar - zNear);
nonLinearDepth = (nonLinearDepth + 1.0) / 2.0;
return nonLinearDepth;
}