Как визуализировать глубину линейно в современном OpenGL с gl_FragCoord.z ​​во фрагментном шейдере?

Я прочитал много информации о получении глубины с фрагментным шейдером.

такие как

http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=234519

но я до сих пор не знаю, gl_FragCoord.z является линейным.

Спецификация GLSL гласит, что его диапазон составляет [0,1] в экране, не говоря уже о том, линейный он или нет.

Я думаю, что линейность очень важна, так как я буду использовать визуализированную модель для сопоставления карты глубины от Kinect.

Тогда, если он не линейный, как линеаризовать его в мировом пространстве?

4 ответа

но я до сих пор не знаю, является ли gl_FragCoord.z ​​линейным или нет.

Будь то gl_FragCoord.z является линейным или не зависит от матрицы проекции. В то время как для орфографической проекции gl_FragCoord.z линейный, для перспективной проекции он не линейный.

В общем, глубина (gl_FragCoord.z а также gl_FragDepth) рассчитывается следующим образом (см. GLSL gl_FragCoord.z ​​Расчет и настройка gl_FragDepth):

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

Матрица проекции описывает отображение от трехмерных точек сцены к двухмерным точкам области просмотра. Он преобразуется из пространства глаза в пространство клипа, а координаты в пространстве клипа преобразуются в нормализованные координаты устройства (NDC) путем деления на компонент w координат клипа.

Ортографическая проекция

В ортогональной проекции координаты в глазном пространстве линейно отображаются на нормализованные координаты устройства.

Ортографическая проекция

Ортографическая матрица проекции:

r = right, l = left, b = bottom, t = top, n = near, f = far 

2/(r-l)         0               0               0
0               2/(t-b)         0               0
0               0               -2/(f-n)        0
-(r+l)/(r-l)    -(t+b)/(t-b)    -(f+n)/(f-n)    1

В ортогональной проекции компонент Z вычисляется по линейной функции:

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

Ортогональная функция Z

Перспективная проекция

В перспективной проекции матрица проекции описывает отображение из трехмерных точек мира, как они видны с камеры-обскуры, в двухмерные точки видового экрана.
Координаты пространства глаза в усеченной области камеры (усеченная пирамида) отображаются в куб (нормализованные координаты устройства).

Перспективная проекция

Матрица перспективной проекции:

r = right, l = left, b = bottom, t = top, n = near, f = far

2*n/(r-l)      0              0               0
0              2*n/(t-b)      0               0
(r+l)/(r-l)    (t+b)/(t-b)    -(f+n)/(f-n)    -1    
0              0              -2*f*n/(f-n)    0

В перспективной проекции Z-компонент вычисляется рациональной функцией:

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

Функция перспективы Z

Глубина буфера

Поскольку нормализованные координаты устройства находятся в диапазоне (-1,-1,-1) - (1,1,1), координата Z должна быть сопоставлена ​​с глубиной, находящейся в диапазоне [0,1]:

depth = (z_ndc + 1) / 2 


Тогда, если оно не линейно, как линеаризовать его в мировом пространстве?

Чтобы преобразовать глубину буфера глубины в исходную Z-координату, необходимо знать проекцию (ортогональную или перспективную), а также ближнюю и дальнюю плоскости.

Ортографическая проекция

n = near, f = far

z_eye = depth * (f-n) + n;

Перспективная проекция

n = near, f = far

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

Если матрица перспективной проекции известна, это можно сделать следующим образом:

A = prj_mat[2][2]
B = prj_mat[3][2]
z_eye = B / (A + z_ndc)

Смотрите также ответ на

Как восстановить положение пространства вида с учетом значения глубины пространства просмотра и ndc xy

Как только он проецируется, он теряет свою линейность, gl_FragCoord.z не является линейным.

Для возврата к линейному режиму необходимо выполнить 2 шага:

1) преобразовать переменную gl_FragCoord.z на нормализованные координаты устройства в диапазоне [-1, 1]

z = gl_FragCoord.z * 2.0 - 1.0 

2) Применить обратную матрицу проекции (IP). (Вы можете использовать произвольные значения для x и y) и нормализовать для последнего компонента.

unprojected = IP * vec4(0, 0, z, 1.0)
unprojected /= unprojected.w

Вы получите точку в пространстве обзора (или пространство камеры, назовите его), у которого есть линейная z между znear и zfar.

Будь то gl_FragCoord.z является линейным или нет, зависит от вашей матрицы преобразования. gl_FragCoord.z определяется путем вычислений gl_Position.z / gl_Position.w для всех вершин вашего треугольника, а затем интерполировать результат по всем фрагментам этого треугольника.

Так gl_FragCoord.z является линейным, когда ваша матрица преобразования присваивает постоянное значение gl_Position.w (что обычно происходит с матрицами ортопроекции) и является нелинейным, когда gl_Position.w зависит от x, y, или же z координата вашего входного вектора (что происходит с матрицами перспективной проекции).

Вам решать, хотите ли вы линейный Z или нет, все зависит от вашей матрицы проекции. Вы можете прочитать это:

http://www.songho.ca/opengl/gl_projectionmatrix.html

Что очень хорошо объясняет, как работает проекционная матрица. Может быть лучше иметь нелинейный Z, чтобы иметь лучшую точность на переднем плане и меньше на заднем плане, артефакты глубины менее заметны вдали...

Другие вопросы по тегам