Cómo recuperar la posición del espacio de visualización dado el valor de la profundidad del espacio de visualización y ndc xy

Estoy escribiendo un shader diferido, y estoy tratando de empacar mi gbuffer con más fuerza. Sin embargo, no puedo calcular la posición de vista dada la profundidad de espacio de vista correctamente

// depth -> (gl_ModelViewMatrix * vec4(pos.xyz, 1)).z; where pos is the model space position // fov -> field of view in radians (0.62831855, 0.47123888) // p -> ndc position, x, y [-1, 1] vec3 getPosition(float depth, vec2 fov, vec2 p) { vec3 pos; pos.x = -depth * tan( HALF_PI - fov.x/2.0 ) * (px); pos.y = -depth * tan( HALF_PI - fov.y/2.0 ) * (py); pos.z = depth; return pos; } 

La posición calculada es incorrecta. Lo sé porque todavía estoy almacenando la posición correcta en el gbuffer y probando usando eso.

Logré hacerlo funcionar al final, ya que es un método diferente desde arriba lo detallaré para que cualquiera que vea esto tenga una solución.

  • Pase 1: almacene el valor de profundidad en el espacio de vista en el gbuffer
  • Para volver a crear la posición (x, y, z) en el segundo pase:
  • Pase el campo de visión horizontal y vertical en radianes al sombreador.
  • Pase la distancia del plano cercano (cerca) al sombreador. (distancia de la posición de la cámara al plano cercano)
  • Imagine un rayo desde la cámara hasta la posición del fragmento. Este rayo corta el plano cercano en una posición determinada P. Tenemos esta posición en el espacio ndc y queremos calcular esta posición en el espacio de visualización.
  • Ahora, tenemos todos los valores que necesitamos en el espacio de vista. Podemos usar la ley de triangularjs similares para encontrar la posición del fragmento real P ‘

     P = P_ndc * near * tan(fov/2.0f) // computation is the same for x, y // Note that by law of similar triangles, P'.x / depth = P/near P'.xy = P/near * -depth; // -depth because in opengl the camera is staring down the -z axis P'.z = depth; 

3 Soluciones para recuperar la posición del espacio de visualización en proyección en perspectiva

La matriz de proyección describe el mapeo de los puntos 3D de una escena, a los puntos 2D de la ventana gráfica. Se transforma del espacio de vista (ojo) al espacio de recorte, y las coordenadas en el espacio de clip se transforman en las coordenadas de dispositivo normalizadas (NDC) dividiendo con el componente w de las coordenadas del clip. Los NDC están en rango (-1, -1, -1) a (1,1,1).

En Perspective Projection, la matriz de proyección describe el mapeo de los puntos 3D en el mundo tal como se ven desde una cámara estenopeica, hasta los puntos 2D de la ventana gráfica.
Las coordenadas del espacio visual en la cámara troncocónica (una pirámide truncada) se asignan a un cubo (las coordenadas del dispositivo normalizado).

proyección en perspectiva

Matriz de proyección de perspectiva:

 r = right, l = left, b = bottom, t = top, n = near, f = far 2*n/(rl) 0 0 0 0 2*n/(tb) 0 0 (r+l)/(rl) (t+b)/(tb) -(f+n)/(fn) -1 0 0 -2*f*n/(fn) 0 

sigue:

 aspect = w / h tanFov = tan( fov_y * 0.5 ); prjMat[0][0] = 2*n/(rl) = 1.0 / (tanFov * aspect) prjMat[1][1] = 2*n/(tb) = 1.0 / tanFov 

En Perspective Projection, el componente Z se calcula mediante la función racional :

 z_ndc = ( -z_eye * (f+n)/(fn) - 2*f*n/(fn) ) / -z_eye 

La profundidad ( gl_FragCoord.z y gl_FragDepth ) se calcula de la siguiente manera:

 z_ndc = clip_space_pos.z / clip_space_pos.w; depth = (((farZ-nearZ) * z_ndc) + nearZ + farZ) / 2.0; 

1. Campo de visión y relación de aspecto

Dado que la matriz de proyección se define por el campo de visión y la relación de aspecto, es posible recuperar la posición de la ventana gráfica con el campo de visión y la relación de aspecto. Siempre que se trate de una proyección en perspectiva simétrica y de las coordenadas del dispositivo normalizado, se conocen la profundidad y el plano cercano y lejano.

Recupere la distancia Z en el espacio de visualización:

 z_ndc = 2.0 * depth - 1.0; z_eye = 2.0 * n * f / (f + n - z_ndc * (f - n)); 

Recupere la posición del espacio de visualización por las coordenadas XY del dispositivo normalizado:

 ndc_x, ndc_y = xy normalized device coordinates in range from (-1, -1) to (1, 1): viewPos.x = z_eye * ndc_x * aspect * tanFov; viewPos.y = z_eye * ndc_y * tanFov; viewPos.z = -z_eye; 

2. Matriz de proyección

Los parámetros de proyección, definidos por el campo de visión y la relación de aspecto, se almacenan en la matriz de proyección. Por lo tanto, la posición de la vista se puede recuperar mediante los valores de la matriz de proyección, desde una proyección en perspectiva simétrica.

Tenga en cuenta la relación entre la matriz de proyección, el campo de visión y la relación de aspecto:

 prjMat[0][0] = 2*n/(rl) = 1.0 / (tanFov * aspect); prjMat[1][1] = 2*n/(tb) = 1.0 / tanFov; prjMat[2][2] = -(f+n)/(fn) prjMat[3][2] = -2*f*n/(fn) 

Recupere la distancia Z en el espacio de visualización:

 A = prj_mat[2][2]; B = prj_mat[3][2]; z_ndc = 2.0 * depth - 1.0; z_eye = B / (A + z_ndc); 

Recupere la posición del espacio de visualización por las coordenadas XY del dispositivo normalizado:

 viewPos.x = z_eye * ndc_x / prjMat[0][0]; viewPos.y = z_eye * ndc_y / prjMat[1][1]; viewPos.z = -z_eye; 

3. Matriz de proyección inversa

Por supuesto, la posición de la ventana gráfica puede recuperarse mediante la matriz de proyección inversa.

 mat4 inversePrjMat = inverse( prjMat ); vec4 viewPosH = inversePrjMat * vec3( ndc_x, ndc_y, 2.0 * depth - 1.0, 1.0 ) vec3 viewPos = viewPos.xyz / viewPos.w; 

Ver también las respuestas a la siguiente pregunta:

  • ¿Cómo se puede representar la profundidad de forma lineal en OpenGL moderno con gl_FragCoord.z ​​en fragment shader?

Escribí un shader diferido y usé este código para recalcular el posicionamiento del espacio de la pantalla:

 vec3 getFragmentPosition() { vec4 sPos = vec4(gl_TexCoord[0].x, gl_TexCoord[0].y, texture2D(depthTex, gl_TexCoord[0].xy).x, 1.0); sPos.z = 2.0 * sPos.z - 1.0; sPos = invPersp * sPos; return sPos.xyz / sPos.w; } 

donde depthTex es la textura que contiene información de profundidad, y invPersp es una matriz de perspectiva inversa precalculada. Usted toma la posición del fragmento de la pantalla y la multiplica por la matriz de perspectiva inversa para obtener las coordenadas de la vista modelo. Luego se divide por w para obtener coordenadas homogéneas. La multiplicación por dos y la resta por uno es escalar la profundidad desde [0, 1] (como se almacena en la textura) a [-1, 1].

Además, dependiendo del tipo de MRT que esté utilizando, el resultado recalculado no será exactamente igual a la información almacenada, ya que pierde la precisión de flotación.