Преобразование образца текстуры глубины в расстояние - PullRequest
4 голосов
/ 20 июля 2009

Я сейчас читаю текстуру глубины в шейдере глубины после обработки, используя следующий код GLSL:

vec4 depthSample = texture2D(sDepthTexture, tcScreen);
float depth = depthSample.x * 255.0 / 256.0 +
              depthSample.y * 255.0 / 65536.0 +
              depthSample.z * 255.0 / 16777216.0;

И затем преобразование значения глубины в расстояние пространства обзора на основе расстояний ближней и дальней плоскости:

float zDistance = (zNear * zFar) / (zFar - depth * (zFar - zNear));

Кажется, что все это работает довольно хорошо, однако мне интересно знать, как выполнить вышеуказанные вычисления, основываясь только на текущей матрице проекции, без необходимости использования отдельных значений zNear и zFar.

Моя первоначальная попытка состояла в том, чтобы умножить (vec4(tcscreen.x, tcScreen.y, depth, 1.0) * 2.0 - 1.0) на обратную матрицу проекции, разделить результат на w, а затем взять полученное значение z в качестве расстояния, но, похоже, это не сработало. Какой правильный подход здесь?

Кроме того, при использовании наклонного усечения усеченного конуса для смещения ближней плоскости на выбранную плоскость отсечения расстояние в ближней плоскости теперь потенциально различается для каждого пикселя? И если так, то означает ли это, что любые шейдеры, которые вычисляют расстояние от текстуры глубины, должны знать об этом случае и не предполагать постоянное расстояние около плоскости?

Спасибо!

Ответы [ 2 ]

2 голосов
/ 21 июля 2009

Оказывается, я забыл отменить окончательное значение Z, чтобы получить положительное расстояние перед ближней плоскостью (камера OpenGL смотрит вниз -Z). Для дальнейшего использования код GLSL для определения расстояния перед ближней плоскостью:

float depth = /* sampled from depth texture as in the original question */ ;

vec4 screenPos = vec4(tcScreen.x, tcScreen.y, depth, 1.0) * 2.0 - 1.0;
vec4 viewPosition = projectionMatrixInverse * screenPos;

float z = -(viewPosition.z / viewPosition.w);

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

Поскольку необходимы только компоненты Z и W viewPosition, вышеуказанный GLSL для вычисления viewPosition может быть несколько упрощен. Достаточно двух точечных произведений вместо полного умножения матрицы, и нет необходимости подавать полную матрицу обратной проекции в шейдер, так как нужны только две нижние строки:

vec2 viewPositionZW = vec2(
    dot(projectionMatrixInverseRow2, screenPos),
    dot(projectionMatrixInverseRow3, screenPos)
);

float z = -(viewPositionZW.x / viewPositionZW.y);

Производительность при этом немного хуже, чем при использовании ближнего и дальнего расстояний, предположительно из-за продуктов с дополнительными точками, я получил снижение на ~ 5%. Математика ближнего и дальнего расстояния также может быть оптимизирована путем ввода (zNear * zFar) и (zFar - zNear) в качестве констант, но я не увидел какого-либо ощутимого улучшения при этом.

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

1 голос
/ 20 июля 2009

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

К сожалению, HLSL:

float depth = tex2D(DepthMapSampler, PSIn.TexCoord).r;

float4 screenPos;
screenPos.x = PSIn.TexCoord.x*2.0f-1.0f;
screenPos.y = -(PSIn.TexCoord.y*2.0f-1.0f);
screenPos.z = depth;
screenPos.w = 1.0f; 

float4 worldPos = mul(screenPos, xViewProjectionInv);
worldPos /= worldPos.w;

Работает нормально, поэтому я полагаю, что Worldposition правильное!

...