Преобразование образца текстуры глубины в расстояние

StackOverflow https://stackoverflow.com/questions/1153114

  •  18-09-2019
  •  | 
  •  

Вопрос

В настоящее время я считываю текстуру глубины в шейдере глубины резкости после обработки, используя следующий 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 значение как расстояние, но это, похоже, не сработало.Каков здесь правильный подход?

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

Спасибо!

Это было полезно?

Решение

Оказывается, я забыл отрицать конечное значение 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) в качестве констант, но я не увидел какого-либо заметного улучшения, сделав это.

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

Другие советы

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

К сожалению, 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 верен!

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top