Всем привет.
Реализовываю deffered shading на юнити, надо получить мировые координаты из координат экрана и буфера глубины.
Тема обсуждена миллион раз, тривиальна и даже спрашивать как-то неудобно. Но что-то у меня идёт не так:
Вот что стандартный выдаёт шейдер если выводить в качестве выходного света
float4(i.pos_world.y, 0, 0, 1.0)
Тут всё как надо.
Теперь пытаемся повторить то же самое с получением координаты из карты глубины в пост процесс шейдере:
1. Передаём в него обратные projective и view матрицы:
Matrix4x4 PI = cam.projectionMatrix.inverse; Matrix4x4 VI = cam.worldToCameraMatrix.inverse; material.SetMatrix("PI", PI); material.SetMatrix("VI", VI);
2. Вычисляем, собственно, мировую позицию в шейдере:
float3 screenToWorld(float2 uv) { float depth = tex2D(_CameraDepthTexture, uv).x; float z = depth * 2.0 - 1.0; float4 clipSpacePosition = float4(uv.x * 2.0 - 1.0, uv.y * 2.0 - 1.0, z, 1.0); float4 viewSpacePosition = mul(PI, clipSpacePosition); viewSpacePosition /= viewSpacePosition.w; float4 worldSpacePosition = mul(VI, viewSpacePosition); return worldSpacePosition.xyz; }
Вместо результата как на первой картинке получается просто залитый красным экран.
Текстуру глубины проверил - там вроде всё в порядке.
Матрицы тоже посмотрел. Не знаю в чём причина.
Спасибо заранее за ваше время.
https://forum.unity.com/threads/world-position-from-depth.151466/
Такой пример смотрел?
KaronatoR
> Matrix4x4 PI = cam.projectionMatrix.inverse;
> Matrix4x4 VI = cam.worldToCameraMatrix.inverse;
Достаточно одной матрицы. Вычисляешь луч от позиции камеры и умножаешь его на глубину.
В surface шейдере вычисляется worldPos автоматом, через него без матриц можно посчитать worldDirection. Осталось только найти такую возможность в пост обработке. Также есть уже готовые обратные матрицы.
В принципе тоже самое без мудрежа с матрицами.
foxes
Так работает.
Но что не так было с моим кодом?
Просто мой код понятен, а вот что тут происходит:
var p = GL.GetGPUProjectionMatrix(cam.projectionMatrix, false); p[2, 3] = p[3, 2] = 0.0f; p[3, 3] = 1.0f; var clipToWorld = Matrix4x4.Inverse(p * cam.worldToCameraMatrix) * Matrix4x4.TRS(new Vector3(0, 0, -p[2, 2]), Quaternion.identity, Vector3.one); material.SetMatrix("clipToWorld", clipToWorld);
так сходу и не скажешь. Да и не элегантно как-то вручную значения в матрицах перебивать (
Причём я попробовал вместо вот "твоей" матрицы передать вот это:
Matrix4x4 clipToWorld = (cam.projectionMatrix * cam.worldToCameraMatrix).inverse;
material.SetMatrix("clipToWorld", clipToWorld);
Ничего хорошего из этого не вышло.
KaronatoR
> Просто мой код понятен, а вот что тут происходит:
проекционная матрица тут как таковая не используется полностью за ненадобностью, поскольку ее задачу полностью заменяет луч из камеры. Из проекционной матрицы берутся только угол зрения и пропорции экрана остальное "обнуляется".
KaronatoR
> new Vector3(0, 0, -p[2, 2]),
Дополнительно создается матрица которая инвертирует одну ось.
От этого всего можно избавиться, я уже написал как. Тебе нужна только worldViewDir она же worldDirection.
> float3 worldPos = mul(_Object2World, vertex).xyz;
Вот это не совсем понятно. У нас же нету вертекса. Мы его ищем как раз.
KaronatoR
> float4 clip = float4(o.vertex.xy, 0.0, 1.0);
У тебя же как то этот пример заработал
float4 clip = float4(o.vertex.xy, 0.0, 1.0);
Пробуй это
Супер, всё работает =)
Не совсем понял вот эту строчку:
positionCS = half4(uv * 2 - 1, depth, 1) * LinearEyeDepth(depth);
Зачем мы тут домножаем на LinearEyeDepth(depth)?
По идее это расстояние от камеры до объекта.
То есть мы берём "точку на экране", в компоненту Z записываем значение из буфера глубины.
И потом ещё домножаем это на расстояние. Есть подозрение, что так мы компенсируем перспективное искажение но я не уверен.
KaronatoR
> Зачем мы тут домножаем на LinearEyeDepth(depth)?
Здесь все сложнее чем просто домножение, поскольку есть еще матрица _MatrixHClipToWorld где также есть преобразование данной величины.
Во вторых это не домножение, а скорее деление
// Z buffer to linear depth inline float LinearEyeDepth( float z ) { return 1.0 / (_ZBufferParams.z * z + _ZBufferParams.w); }
В процессе вычисления проекции получаются координаты не только XYZ но еще и W, на нее все делиться. И если W=Z (не столько равно сколько пропорционально) то получается перспективное искажение. Если посмотришь в 11 ячейку проекционной матрицы то там будет -1. Для ортогональной проекции это будет 0.
W=x*_matrix[3]+y*_matrix[7]+z*_matrix[11]+_matrix[15]; или в упрощенном виде, исключая преобразования поворота и смещения модельной и видовой матриц. W=-z;
KaronatoR
> Есть подозрение, что так мы компенсируем перспективное искажение
В общем так и есть, и еще возвращает масштаб глубины от [0..1] в [near..far]
Из за того что глубина, в процессе все этих преобразований, получается "(z*a+b)/z" ее приходиться вот так переворачивать.
foxes
Большое спасибо, всё понял.
Ты очень помог, ещё раз спасибо.
Тема в архиве.
Тема закрыта.