Pobieranie prawdziwej wartości z z bufora głębi

Pobieranie próbek z bufora głębi w cieniowaniu zwraca wartości z zakresu od 0 do 1, zgodnie z oczekiwaniami. Biorąc pod uwagę bliskie i dalekie płaszczyzny klipu kamery, jak obliczyć rzeczywistą wartość z w tym punkcie, tj. odległość od kamery?

Author: Hannesh, 2011-07-11

2 answers

Od 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));
}

[edytuj] oto Wyjaśnienie (z 2 błędami, patrz komentarz Christiana poniżej):

Macierz perspektywy OpenGL wygląda tak: od songho.ca

Gdy pomnożymy tę macierz przez jednorodny punkt [x, y, Z, 1], otrzymamy: [don 't care, don' t care, Az+B, - z] (Z A i B dwoma dużymi składnikami w macierzy).

OpenGL next robi podział perspektywy: dzieli ten wektor przez jego składową W. Ta operacja nie jest wykonywana w shaderach (z wyjątkiem specjalnych przypadków, takich jak shadowmapping), ale w sprzęcie; nie można jej kontrolować. w = - z, więc wartość Z staje się-a / z-B.

Jesteśmy teraz w znormalizowanych współrzędnych urządzenia. Wartość Z wynosi od 0 do 1. Z jakiegoś głupiego powodu, OpenGL wymaga przeniesienia go do zakresu [-1,1] (tak jak x i y). Stosuje się skalowanie i przesunięcie.

Ta końcowa wartość jest następnie przechowywana w bufor.

Powyższy kod robi dokładnie odwrotnie:

  • z_b jest wartością surową przechowywaną w buforze
  • z_n przekształca liniowo z_b z [-1,1] na [0,1]
  • z_e jest tym samym wzorem co z_n=-A/z_e-B, ale rozwiązane dla z_e. Jest odpowiednikiem z_e = - A / (z_n+B). A i B powinny być obliczane na CPU i wysyłane jako uniformy, btw.

Odwrotną funkcją jest:

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;
}
 50
Author: Calvin1602,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-27 17:29:51

Wiem, że to stare, stare pytanie, ale znalazłem się tutaj więcej niż raz przy różnych okazjach, więc pomyślałem, że podzielę się moim kodem, który wykonuje konwersje do przodu i do tyłu.

To jest oparte na odpowiedzi @Calvin1602. Działają one w GLSL lub zwykłym starym kodzie C.
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;
}
 9
Author: david van brink,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-01 18:30:37