Сэмплирование из буфера глубины в шейдере возвращает значения от 0 до 1, как и ожидалось. С учетом плоскостей ближнего и дальнего снимка камеры, как вычислить истинное значение z в этой точке, то есть расстояние от камеры?
Получение истинного значения z из буфера глубины
Ответ 1
От 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] Итак, вот объяснение (с двумя ошибками, см. комментарий Христиана ниже):
Перспективная матрица OpenGL выглядит следующим образом:
Когда вы умножаете эту матрицу на однородную точку [x, y, z, 1], она дает вам: [все равно, не важно, Az + B, -z] (с A и B 2 больших компонента в матрице).
OpenGl next выполняет разделение перспективы: он делит этот вектор на его w-компонент. Эта операция выполняется не в шейдерах (кроме особых случаев, таких как теневое копирование), а в аппаратных средствах; вы не можете контролировать это. 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). A и B должны быть вычислены на CPU и отправлены как униформа, btw.
Противоположная функция:
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;
}
Ответ 2
Я знаю, что это старый, старый вопрос, но я неоднократно встречался здесь несколько раз, поэтому я решил поделиться своим кодом, который делает обратные и обратные преобразования.
Это основано на ответе @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;
}