Pergunta

Atualmente estou lendo de uma textura de profundidade, em lâmina postprocess de shader campo usando o seguinte código 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;

E, em seguida, converter o valor da profundidade a uma distância vista espaço com base nas distâncias de avião perto e de longe:

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

Isso tudo parece funcionar razoavelmente bem, mas estou interessado em saber como fazer o cálculo acima com base apenas na matriz de projeção atual sem precisar valores zNear e zFar separadas.

A minha tentativa inicial envolveu multiplicando (vec4(tcscreen.x, tcScreen.y, depth, 1.0) * 2.0 - 1.0) pelo inverso da matriz de projeção, dividindo o resultado por meio de w, em seguida, tendo o valor z resultante como a distância, mas isso não parece trabalho. Qual é a abordagem correta aqui?

Além disso, ao usar recorte tronco oblíqua para mudar o próximo avião para um plano de corte escolhido é a distância de avião perto agora potencialmente diferente para cada pixel? E se assim for, então isso significa que qualquer shaders que calculam a distância de uma necessidade profundidade de textura para estar ciente deste caso e não assumir uma constante perto distância avião?

Obrigado!

Foi útil?

Solução

Acontece que eu tinha esquecido de negar o valor final Z para obter uma distância positiva na frente do avião perto (OpenGL câmera olha para baixo-Z). Para referência futura o código GLSL para obter a distância na frente do avião perto é:

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

Se você queria uma posição do espaço do mundo em vez (como SuperPro estava usando), em seguida, que pode ser encontrado através da combinação de vista e de projecção matrizes e, em seguida, usando o inverso do que a matriz, em vez de apenas usando o inverso matriz de projeção.

Porque são necessários apenas o Z e W componentes de viewPosition o GLSL acima para viewPosition computação pode ser simplificado um pouco. Dois produtos de ponto será suficiente, em vez de uma multiplicação de matriz completa, e não há necessidade de alimentar a matriz de projeção inversa cheia no shader como apenas as duas linhas inferiores são necessários:

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

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

O desempenho deste é um pouco pior do que usar os próximos e distantes distâncias, presumivelmente por causa dos produtos de pontos extras, eu tenho uma redução ~ 5%. A matemática perto e de longe distância também pode ser otimizado pela alimentação (zNear * zFar) e (zFar - zNear) em como constantes, mas eu não vi qualquer melhoria mensurável fazendo isso.

Curiosamente, quando você combina acima com uma matriz de projeção que está usando tronco oblíqua recorte Eu não posso tirar alguma coisa sensata dele, mas eu faço obter uma saída razoável quando se utiliza a equação de perto e de longe distância com a mesma matriz de projeção , embora com o que parece ser alguma distorção dos valores de profundidade (embora isso poderia ser apenas devido à perda de precisão profundidade inerente recorte tronco oblíqua). Se alguém pode lançar alguma luz sobre o que exatamente está acontecendo matematicamente aqui que eu aprecio isso, embora talvez que deveria estar em uma pergunta diferente.

Outras dicas

Eu uso o seguinte código em um shader relâmpago, a fim de calcular a direção da luz. Wold posição também é calculado multiplicando a posição da tela com o inverso da matriz de projecção.

Infelizmente 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;

funciona bem, então eu suponho Worldposition está correto!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top