Войти
ПрограммированиеФорумОбщее

Unity: UNITY_REVERSED_Z разрешен при normal depth

#0
0:31, 14 апр. 2017

Привет всем!
Работаю на шейдером, который использует буффер глубины, Unity 5.6.0f.
Вот часть кода:

float depth;
        #if UNITY_REVERSED_Z// DirectX
      float a = depthMain;// 1-
      float b = depthBall;
      if(a > b){
        b1 = true;
        depth = b; 
      }else{
        b1 = false;
        //depth = 1-depthBall; 
      }
    #else// OpebGL
      //bool b1 = depthMain > depthBall;// My mac
      if(depthMain > depthBall){
        b1 = true;
        depth = depthBall; 
      }else{
        b1 = false;
        //depth = depthMain; 
      }
    #endif

По идее UNITY_REVERSED_Z (на сколько я понял) true, когда буффер глубины инвертирован.
Так вот, код представленный выше был запущен на компах с directx и OpenGL.
Причем #if UNITY_REVERSED_Z вызывается для directx, a #else для OpenGL.

Я пришел к тому, что UNITY_REVERSED_Z вызывается при неинвертированном буффере для directx. Почему?

И еще что:

float u = i.uv.x;
float v = i.uv.y;
#if defined(UNITY_UV_STARTS_AT_TOP) //&& !defined(SHADER_API_MOBILE)
  v = 1.0 - v;
#endif
Вот этот код запускался на directx и инвертирует картинку неверно. Почему???


#1
2:26, 14 апр. 2017

Alerr
> #if UNITY_REVERSED_Z// DirectX
Я не уверен что в шейдере эта строчка будет выполняться правильно, тут проверка true/false, а не define, а должно быть:

#if defined(UNITY_REVERSED_Z)

А откуда ты значение глубины берешь?
Если из _CameraDepthTexture то там достаточно:

float depth = UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture, screen));

Alerr
> #if defined(UNITY_UV_STARTS_AT_TOP) //&& !defined(SHADER_API_MOBILE)
А вот тут как раз наоборот надо использовать так:

#if !UNITY_UV_STARTS_AT_TOP
Потому что UNITY_UV_STARTS_AT_TOP всегда определена.
UNITY_UV_STARTS_AT_TOP  Always defined with value of 1 or 0. A value of 1 is on platforms where Texture V coordinate is 0 at the “top” of the Texture. Direct3D-like platforms use value of 1; OpenGL-like platforms use value of 0.
#2
2:31, 14 апр. 2017

Alerr
> Причем #if UNITY_REVERSED_Z вызывается для directx, a #else для OpenGL.
На OpenGL инвертировать буфер глубины не так тривиально. Из-за клип спейса [-1;1] в OpenGL инвертирование буфера не дает никакого профита. Для этого есть всякие костыли, которые от версии к версии меняются. Так по честному проблему можно решить только в OpenGL 4.1 версии через glDepthRangef. На более ранних версиях приходится юзать glDepthRangedNV, но только для NVidia, либо заводить клипплейны, и тягать их аж через все шейдеры.
Поэтому подозреваю, что в Юнити эту проблему просто не стали решать, а забили, и считают, что если у тебя OpenGL, то буфер глубины не инвертирован.

Alerr
> Вот этот код запускался на directx и инвертирует картинку неверно. Почему???
Потому что на DirectX не надо картинку инвертировать. Он изначально рисует в текстуру адекватно в левый верхний угол и не перевернуто, в отличие от OpenGL

#3
2:49, 14 апр. 2017

MrShoor
> Поэтому подозреваю, что в Юнити эту проблему просто не стали решать, а забили,
> и считают, что если у тебя OpenGL, то буфер глубины не инвертирован.
В юнити для этого есть специально _ZBufferParams в шейдере и функция:

// Z buffer to linear 0..1 depth
inline float Linear01Depth( float z )
{
  return 1.0 / (_ZBufferParams.x * z + _ZBufferParams.y);
}
// Z buffer to linear depth
inline float LinearEyeDepth( float z )
{
  return 1.0 / (_ZBufferParams.z * z + _ZBufferParams.w);
}
#4
3:07, 14 апр. 2017

Alerr
Я делал эффект тумана, без глубины это просто вот такая плоскость

+ Показать

А с глубиной вот такой вот красивый обтекающий туман.
groundfog0001 | Unity: UNITY_REVERSED_Z разрешен при normal depth
Только это все без дефайнов и для всех платформ
+ Показать
#5
5:37, 14 апр. 2017

foxes
> В юнити для этого есть специально _ZBufferParams в шейдере и функция:
Только это ломает early depth test. И линейная глубина не то же самое что обратная, хотя в пиксельном можно и обратную глубину сделать.

#6
9:34, 14 апр. 2017

foxes
Красивый туман. Вершинную функцию я описал так(vert_img):

SubShader
    {
        Pass {
            ZTest Always 
      Cull Off 
      ZWrite Off
      Fog { Mode off }

            CGPROGRAM
            #pragma vertex vert_img//<<<<<<<
            #pragma fragment frag
            //#pragma target 3.0
            ENDCG
        }
    }

Написал в шейдере так:

#if !UNITY_UV_STARTS_AT_TOP
  v = 1.0 - v;
#endif
и на OpenGl фрагментный шейдер стал выдавать невернеые результаты. Похоже на то, что vert_img учитывает инверсию экрана и буффера глубины. Иначе почему все работает без учетов особенностей с моей стороны.

И вот это работает без инферсий буффера глубины:

float depthMain = Linear01Depth(UNITY_SAMPLE_DEPTH(tex2D(_MainDepth, i.uv))); //tex2D(_MainDepth, i.uv);
float depthBall = Linear01Depth(UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture, i.uv)));

Посмотрел вершинную функцию:

v2f_img vert_img( appdata_img v )
{
  v2f_img o;
  o.pos = UnityObjectToClipPos (v.vertex);
  o.uv = v.texcoord;
  return o;
}
Но тут ничего такого, что учитывает инверсию буфферов нет.

#7
11:28, 14 апр. 2017

Alerr
> #if !UNITY_UV_STARTS_AT_TOP
> v = 1.0 - v;
> #endif
> и на OpenGl фрагментный шейдер стал выдавать невернеые результаты.
Мне так кажется что тебе в принципе можно спокойно эти строчки убрать.

* if one want to compare a "fetched depth" to one from a projection matrix both value need to be in the same "direction", and i feel that it would be unclear to ask people to revert the result from UNITY_SAMPLE_DEPTH because it was already reverted under the hood in that case.

- Make UNITY_SAMPLE_DEPTH deal automatically with reversed z-buffer.

Alerr
> o.uv = v.texcoord;
Вот эта часть, если используется для координат буфера глубины то считается так:

float4 screen =  ComputeScreenPos(o.pos);
o.uv = screen.xy / screen.w;
Но в примерах в основном вижу такой вариант:
o.screen =  ComputeScreenPos(o.pos);
...
float depthBall = Linear01Depth(UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture, UNITY_PROJ_COORD(i.screen))));
#8
12:03, 14 апр. 2017

Спасибо

ПрограммированиеФорумОбщее

Тема в архиве.