Привет всем!
Есть годот-шейдер, который работает с текстурой буффера глубины.
Пытаюсь повторить шейдер в юнити, не получается.
В обоих проектах задал одинаковые Near & Far clip planes, FOV.
Насколько понимаю, _CameraDepthTexture содержит нелинейное значение буффера глубины. Оно находится в диапазоне от [0,1], но оно нелиейно.
То есть 0.5 не соответствует половине расстояния между Far & Near clip plane.
float notLinDepth = tex2D(_CameraDepthTexture, SCREEN_UV).r;
LinearEyeDepth and Linear01Depth - эти методы позволяю линеаризовать значение.
LinearEyeDepth - это дает линейный WorldScaled Depth
Linear01Depth - это делает из нелинейного depth линейное.
В шейдере годота глубина вычисляется так:
float depth = textureLod(depth_tex, SCREEN_UV, 0.0).r; depth = pow( depth, 30.); ALBEDO.xyz = vec3( depth,depth,depth);
На экране затемненный CUBE_G
Аналогичная операция в Юнити дает другой результат
float depth = tex2D(_CameraDepthTexture, SCREEN_UV).r; depth = pow( depth, 30.); return float4( depth, depth, depth, 1.)
Сравнение:
Нашел в сети такую операцию: return zNear * zFar / (zFar + depth * (zNear - zFar));
Не понимаю что она делает, но она вроде как в связана с мировым пространством и я выразил из нее depth и переписа так:
float linear_depth_to_notlinear (float srcDepth) { return ( _ProjectionParams.y * _ProjectionParams.z / srcDepth - _ProjectionParams.z) / ( _ProjectionParams.y - _ProjectionParams.z); }
Далее сделал так:
float depth = LinearEyeDepth(tex2D( _CameraDepthTexture, SCREEN_UV).r ); depth = linear_depth_to_notlinear( depth); depth = pow( depth, 30.); return float4( depth, depth, depth, 1.);
Результат:
Теперь результат ближе к тому, что в Годоте, но все равно видно что не тот.
Ребят, кто видит в чем проблема? Все перепробовал, не могу понять почему в годоте глубина выглядит иначе.
Нормально. В Юнити слегка темнее, чем в годоте, а так одно и тоже.
Можно в юнити подобрать число в место 30
depth = pow(depth, 34.); или 28
Или depth = pow(depth, 30.)*1.25;
ronniko
> В Юнити слегка темнее, чем в годоте, а так одно и тоже.
А почему оно темнее-то? Колор спейс тот же.
Похоже я где-то не так считаю потому, что далее отклонения становятся значительнее.
Например далее в годоте идет:
vec4 upos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, depth, 1.0); ALBEDO.xyz = upos.xyz;
Перенес в юнити:
Inverse:
float4x4 INV_PROJECTION_MATRIX = Inverse(UNITY_MATRIX_P); float4 upos = mul( INV_PROJECTION_MATRIX, float4( SCREEN_UV * 2.0 - 1.0, depth, 1.0) ); return upos;
Видно, что вразнос все пошло:
А если вывести:
float3 pixel_position = upos.xyz / upos.w; return float4(pixel_position, 1);
То вот разница:
Может кто-то знает что здесь вычисляется:
vec4 upos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, depth, 1.0); vec3 pixel_position = upos.xyz / upos.w;
Насколько понимаю, SCREEN_UV переводится из [0,1] => [-1,1]. То есть это Projection Space, и 1.0 - означает, что вектор рассматривается как позиция.
Далее, насколько понимаю, из пространства проекции происходит отображение в пространство ViewSpace (то есть пространство камеры).
И судя по тому что в годоте и в юнити в pixel_position содержатся разные значения, это говорит о том, что проблема в depth.
Есть какие-то идеи? Я уже все высосал из пальца, не понимаю почему значения в юнити и в годоте получаются разные.
А не линейную глубину пробовал сравнить от Godot c Unity? Возьми пиксель например где нибудь в середине текстуры который отображает глубину для куба и посмотри значение, мб изначально глубина не линейная в годоте записывается как-то иначе от Unity?
Ещё попробуй все-таки принудительно сам полученный результат в твоем втором скрине глубины от юнити перевести в гамму:
А ты уверен, что матрица проектирования одна и та же в обоих случаях? Может разные плоскости отсечения задаются, отсюда и разный оттенок карты глубины..
Target
> Ещё попробуй все-таки принудительно сам полученный результат в твоем втором скрине глубины от юнити перевести в гамму:
Спасибо!
Попробовал и, кажется, помогло!
AMM1AK
> А ты уверен, что матрица проектирования одна и та же в обоих случаях?
не уверен, сейчас пробуюс дальше, смотрю...
Update 1:
Да... В общем годот завешивает мне весь комп так, что его от кнопки приходится отключать.
Сейчас заметил, что у меня проект в юнити сбился(камера настроена как попало, еще гамма спейс...) Дурдом...
Update 2:
AMM1AK
> А ты уверен, что матрица проектирования одна и та же в обоих случаях?
Похоже, что эта матрица нужна: unity_CameraInvProjection
Target
Не пойму, после вызова FloatLinearToGamma(depth); значение глубины становится как в годоте, но это же только для виуализации?
Глупый вопрос, но почему в юнити нужно просчитывать гамму?
До этого момента код вроде удалось перенести (есть небольшие различия вроде как, но вроде одинаковые картинки):
fixed4 frag(v2f i) : SV_Target{ i.VIEW = normalize( i.VIEW); // return float4(i.VIEW, 1); float2 SCREEN_UV = i.screenPos.xy / i.screenPos.w;//+ // 0 - close // 1 - far float depth = LinearEyeDepth( tex2D( _CameraDepthTexture, SCREEN_UV).r );//Linear01Depth LinearEyeDepth depth = linear_depth_to_notlinear( depth); // FloatLinearToGamma(depth); // depth = pow(depth, 30.); return float4(depth, depth, depth, 1.);//<<<<<<<<< //<< INV_PROJECTION_MATRIX <=> unity_CameraInvProjection --- ? // Clip space => Obj space float4x4 INV_PROJECTION_MATRIX = unity_CameraInvProjection;// Inverse(UNITY_MATRIX_P); float4 upos = mul( INV_PROJECTION_MATRIX, float4( SCREEN_UV * 2.0 - 1.0, depth, 1.0) ); //FloatLinearToGamma(upos.x); return float4( upos.xyz / upos.w, 1); ... }
Код годота:
На выходе так:
Но теперь в годоте:
vec4 wpos = INV_VIEW_MATRIX * (upos / upos.w);
В юнити прописал так:
float4 wpos = mul(UNITY_MATRIX_I_V, ( upos / upos.w));
Сравнение:
Выхдит, что UNITY_MATRIX_I_V не та матрица?
UNITY_MATRIX_I_V
Типа матрица номер 4
Пробуй остальные 9 матриц :)
Я знал, что Юнити ненормальные писали, но не думал что прям такие психи.
ronniko
Ну емое, говорящей название на деле не совсем то. Почитал про матрицу, она на что-то там ещё домножена...
ronniko
> Пробуй остальные 9 матриц :)
Где они есть? Яздесь нашел только эти:
юнидок
и тут:
юнигит
Попробовал так получить матрицу I_V:
float4x4 INV_VIEW_MATRIX = mul(UNITY_MATRIX_I_V, Inverse( unity_ObjectToWorld))
Как-то криво работает, информацию нашел в сети (не факт что она верная):
> built-in matrix provided by Unity's shader system, and it specifically represents the inverse of the view matrix
> multiplied by the inverse of the unity_ObjectToWorld matrix. In other words, it combines both the view and
> world space transformations.
Unity использует соглашения о матрицах представлений OpenGL и они переверорачивают выходное z-значение m.SetRow(2, -m.GetRow(2)); Как там в годоте - я хз. Возможно в этом дело и тебе нужно просто учесть в шейдере этот момент. Не уверен но попробуй, а так можешь сам составить IV матрицу. Более того, важно учесть различие матриц в годоте и Unity, row ordered или column ordered может быть и потому умножение нужно делать не матрицы не вектор а вектора на матрицу (либо в коде перед передачей матрицы в шейдер делай транспонирование матрицы) Т.е. вместо
float4 wpos = mul(UNITY_MATRIX_I_V, ( upos / upos.w));
делаешь так
float4 wpos = mul((upos / upos.w), UNITY_MATRIX_I_V);
Если бред выходит - сам попробуй построить матрицу:
Matrix4x4 p = GL.GetGPUProjectionMatrix(camera.projectionMatrix, true); Matrix4x4 v = camera.worldToCameraMatrix; Matrix4x4 vp = p * v; Matrix4x4 vpi = vp.inverse; ... тут уже передаешь в свой шейдер полученную матрицу, только в шейдере уже умножай матрицу на вектор
В целом менять местами умножение матриц не нужно, т.к. ты все равно пробуешь юзать стандартные матрицы от юнити и порядок там верный
Alerr
> Не пойму, после вызова FloatLinearToGamma(depth); значение глубины становится
> как в годоте, но это же только для виуализации?
Хз почему, но от значения глубины, что ты перевел в гамму, будут зависеть другие полученные результаты вычислений. Я делал перенос не одного шейдера и юзаю этот метод, без него результаты совсем не те что ожидаешь выходят. Почему так происходит я так и не разобрался (не хотел разбираться)
P.S. инвертирование матрицы тяжелая операция если что, я бы не стал её юзать в шейдере пофрагментно
Target
Пробовал и строки и столбцы UNITY_MATRIX_I_V на -1 умножать - это не помогло.
Это тоже не помогло:
float4x4 INV_VIEW_MATRIX = Inverse (UNITY_MATRIX_V); float4 wpos = mul( INV_VIEW_MATRIX, ( upos / upos.w));
Вроде как это ViewSpace матрица:
var invViewMatrix = cam.transform.worldToLocalMatrix; if (SystemInfo.usesReversedZBuffer) { invViewMatrix.m20 = -invViewMatrix.m20; invViewMatrix.m21 = -invViewMatrix.m21; invViewMatrix.m22 = -invViewMatrix.m22; invViewMatrix.m23 = -invViewMatrix.m23; }// */ Shader.SetGlobalMatrix( "_IV", invViewMatrix.inverse);
Target, вы наверно точно знаете когда правильно инвертировать ряд (до или после инвертации). Я верно инвертировал до?
Этот код создает верную матрицу, но... Знатоки Unity, встроенная инверсная вью матрица существует или нужно самому кидать ее в шейдер?
> P.S. инвертирование матрицы тяжелая операция если что, я бы не стал её юзать в шейдере пофрагментно
Да, спасибо, знаю. Просто сам все хочу руками просчитать чтобы перед глазами было. потом буду оптимизирвать.
Тема в архиве.