Получение истинного значения z из буфера глубины - PullRequest
46 голосов
/ 11 июля 2011

Выборка из буфера глубины в шейдере возвращает значения от 0 до 1, как и ожидалось. Как рассчитать истинное значение z в этой точке, т. Е. Расстояние от камеры?

Ответы [ 2 ]

53 голосов
/ 12 июля 2011

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

[править] Итак, вот объяснение (с 2 ошибками, см. Комментарий Кристиана ниже):

Матрица перспективы OpenGL выглядит следующим образом: from songho.ca

Когда вы умножаете эту матрицу на однородную точку [x, y, z, 1], она дает вам: [все равно, все равно, Az + B, -z] (сA и B 2 большие компоненты в матрице).

Затем OpenGl выполняет деление перспективы: он делит этот вектор на свой компонент w.Эта операция выполняется не в шейдерах (кроме особых случаев, таких как shadowmapping), а в аппаратных средствах;Вы не можете это контролировать.w = -z, поэтому значение Z становится -A / z -B.

Теперь мы находимся в нормализованных координатах устройства.Значение Z находится между 0 и 1. По какой-то глупой причине OpenGL требует, чтобы он был перемещен в диапазон [-1,1] (точно так же, как x и y).Применяются масштабирование и смещение.

Это окончательное значение затем сохраняется в буфере.

Приведенный выше код делает прямо противоположное:

  • z_b - это необработанныйзначение, сохраненное в буфере
  • z_n, линейно преобразует z_b из [-1,1] в [0,1]
  • z_e по той же формуле, что и z_n = -A / z_e -B, норешено для z_e вместо.Это эквивалентно z_e = -A / (z_n + B).А и В должны быть вычислены на ЦП и отправлены в форме, кстати.

Противоположная функция:

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;
}
9 голосов
/ 01 ноября 2015

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

Это основано на ответе @ Calvin1602.Они работают в GLSL или обычном старом 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;
}
...