Подтвердить что ты не робот

Как вы пишете z-depth в шейдере?

Этот шейдер (код в конце) использует raymarching для визуализации процедурной геометрии:

shader output

Однако на изображении (выше) куб в фоновом режиме должен частично перекрывать розовое тело; это не из-за этого:

  struct fragmentOutput {
    float4 color : SV_Target;
    float zvalue : SV_Depth;
  };

  fragmentOutput frag(fragmentInput i) {
    fragmentOutput o;
    ...
    o.zvalue = IF(output[1] > 0, 0, 1);
  }

Тем не менее, я не могу на всю жизнь выяснить, как правильно генерировать значение глубины здесь, что позволяет правильно замаскировать твердые тела, чтобы скрыть/не скрыть другую геометрию сцены.

Я знаю, что это возможно, потому что здесь есть рабочий пример: https://github.com/i-saint/RaymarchingOnUnity5 (связанный блог japanese language http://i-saint.hatenablog.com/)

Однако, он на японском языке, и в значительной степени недокументирован, а также чрезвычайно сложный.

Я ищу чрезвычайно упрощенную версию той же вещи, из которой можно построить.

В шейдере я в настоящее время использую строку программы фрагмента:

float2 output = march_raycast(i.worldpos, i.viewdir, _far, _step);

Для отображения входной точки p на квадрате нужна камера (к которой прикреплен этот шейдер), в выходной float2 (плотность, расстояние), где расстояние - это расстояние от квадрата до "точки" на процедурной поверхности.

Вопрос в том, как я могу отобразить это в буфер глубины любым полезным способом?

Полный шейдер здесь, чтобы использовать его, создайте новую сцену с сферой в 0,0,0 с размером не менее 50 и назначьте ей шейдер:

Shader "Shaders/Raymarching/BasicMarch" {
  Properties {
    _sun ("Sun", Vector) = (0, 0, 0, 0)
    _far ("Far Depth Value", Float) = 20
    _edgeFuzz ("Edge fuzziness", Range(1, 20)) = 1.0
    _lightStep ("Light step", Range(0.1, 5)) = 1.0
    _step ("Raycast step", Range(0.1, 5)) = 1.0
    _dark ("Dark value", Color) = (0, 0, 0, 0)
    _light ("Light Value", Color) = (1, 1, 1, 1)
    [Toggle] _debugDepth ("Display depth field", Float) = 0
    [Toggle] _debugLight ("Display light field", Float) = 0
  }
  SubShader {
    Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
    Blend SrcAlpha OneMinusSrcAlpha
    Pass {
      CGPROGRAM

      #pragma vertex vert
      #pragma fragment frag
      #pragma target 3.0

      #include "UnityCG.cginc"
      #include "UnityLightingCommon.cginc" // for _LightColor0
      #define IF(a, b, c) lerp(b, c, step((fixed) (a), 0));

      uniform float _far;
      uniform float _lightStep;
      uniform float3 _sun;
      uniform float4 _light;
      uniform float4 _dark;
      uniform float _debugDepth;
      uniform float _debugLight;
      uniform float _edgeFuzz;
      uniform float _step;

      /**
       * Sphere at origin c, size s
       * @param center_ The center of the sphere
       * @param radius_ The radius of the sphere
       * @param point_ The point to check
       */
      float geom_soft_sphere(float3 center_, float radius_, float3 point_) {
        float rtn = distance(center_, point_);
        return IF(rtn < radius_, (radius_ - rtn) / radius_ / _edgeFuzz, 0);
      }

      /**
       * A rectoid centered at center_
       * @param center_ The center of the cube
       * @param halfsize_ The halfsize of the cube in each direction
       */
      float geom_rectoid(float3 center_, float3 halfsize_, float3 point_) {
        float rtn = IF((point_[0] < (center_[0] - halfsize_[0])) || (point_[0] > (center_[0] + halfsize_[0])), 0, 1);
        rtn = rtn * IF((point_[1] < (center_[1] - halfsize_[1])) || (point_[1] > (center_[1] + halfsize_[1])), 0, 1);
        rtn = rtn * IF((point_[2] < (center_[2] - halfsize_[2])) || (point_[2] > (center_[2] + halfsize_[2])), 0, 1);
        rtn = rtn * distance(point_, center_);
        float radius = length(halfsize_);
        return IF(rtn > 0, (radius - rtn) / radius / _edgeFuzz, 0);
      }

      /**
       * Calculate procedural geometry.
       * Return (0, 0, 0) for empty space.
       * @param point_ A float3; return the density of the solid at p.
       * @return The density of the procedural geometry of p.
       */
      float march_geometry(float3 point_) {
        return
          geom_rectoid(float3(0, 0, 0), float3(7, 7, 7), point_) +
          geom_soft_sphere(float3(10, 0, 0), 7, point_) +
          geom_soft_sphere(float3(-10, 0, 0), 7, point_) +
          geom_soft_sphere(float3(0, 0, 10), 7, point_) +
          geom_soft_sphere(float3(0, 0, -10), 7, point_);
      }

      /** Return a randomish value to sample step with */
      float rand(float3 seed) {
        return frac(sin(dot(seed.xyz ,float3(12.9898,78.233,45.5432))) * 43758.5453);
      }

      /**
       * March the point p along the cast path c, and return a float2
       * which is (density, depth); if the density is 0 no match was
       * found in the given depth domain.
       * @param point_ The origin point
       * @param cast_ The cast vector
       * @param max_ The maximum depth to step to
       * @param step_ The increment to step in
       * @return (denity, depth)
       */
      float2 march_raycast(float3 point_, float3 cast_, float max_, float step_) {
        float origin_ = point_;
        float depth_ = 0;
        float density_ = 0;
        int steps = floor(max_ / step_);
        for (int i = 0; (density_ <= 1) && (i < steps); ++i) {
          float3 target_ = point_ + cast_ * i * step_ + rand(point_) * cast_ * step_;
          density_ += march_geometry(target_);
          depth_ = IF((depth_ == 0) && (density_ != 0), distance(point_, target_), depth_);
        }
        density_ = IF(density_ > 1, 1, density_);
        return float2(density_, depth_);
      }

      /**
       * Simple lighting; raycast from depth point to light source, and get density on path
       * @param point_ The origin point on the render target
       * @param cast_ The original cast (ie. camera view direction)
       * @param raycast_ The result of the original raycast
       * @param max_ The max distance to cast
       * @param step_ The step increment
       */
      float2 march_lighting(float3 point_, float3 cast_, float2 raycast_, float max_, float step_) {
        float3 target_ = point_ + cast_ * raycast_[1];
        float3 lcast_ = normalize(_sun - target_);
        return march_raycast(target_, lcast_, max_, _lightStep);
      }

      struct fragmentInput {
        float4 position : SV_POSITION;
        float4 worldpos : TEXCOORD0;
        float3 viewdir : TEXCOORD1;
      };

      struct fragmentOutput {
        float4 color : SV_Target;
        float zvalue : SV_Depth;
      };

      fragmentInput vert(appdata_base i) {
        fragmentInput o;
        o.position = mul(UNITY_MATRIX_MVP, i.vertex);
        o.worldpos = mul(_Object2World, i.vertex);
        o.viewdir = -normalize(WorldSpaceViewDir(i.vertex));
        return o;
      }

      fragmentOutput frag(fragmentInput i) {
        fragmentOutput o;

        // Raycast
        float2 output = march_raycast(i.worldpos, i.viewdir, _far, _step);
        float2 light = march_lighting(i.worldpos, i.viewdir, output, _far, _step);
        float lvalue = 1.0 - light[0];
        float depth = output[1] / _far;

        // Generate fragment color
        float4 color = lerp(_light, _dark, lvalue);

        // Debugging: Depth
        float4 debug_depth = float4(depth, depth, depth, 1);
        color = IF(_debugDepth, debug_depth, color);

        // Debugging: Color
        float4 debug_light = float4(lvalue, lvalue, lvalue, 1);
        color = IF(_debugLight, debug_light, color);

        // Always apply the depth map
        color.a = output[0];

        o.zvalue = IF(output[1] > 0, 0, 1);
        o.color = IF(output[1] <= 0, 0, color);
        return o;
      }
      ENDCG
    }
  }
}

(Да, я знаю, что это довольно сложно, но очень сложно уменьшить этот вид шейдера в "простой тестовый сценарий", чтобы играть с ним)

Я с удовольствием соглашусь с любым ответом, который является модификацией шейдера выше, что позволяет обрабатывать сплошное тело, чтобы затушевать/скрыть другую геометрию в сцене, как если бы это была "реальная геометрия".

-

Изменить: вы можете получить эту "рабочую" путем явной установки значения глубины на другую геометрию в сцене с использованием той же функции глубины, что и raymarcher:

VYa9z.png

... однако, я все еще не могу заставить это работать правильно с геометрией, используя "стандартный" шейдер. Еще охота за рабочим решением...

4b9b3361

Ответ 1

Посмотрев на проект, с которым вы связались, самая важная разница, которую я вижу, заключается в том, что их функция raycast march использует параметр pass-by-reference для возврата позиции фрагмента называется ray_pos. Эта позиция, по-видимому, находится в пространстве объектов, поэтому преобразует ее с помощью матрицы прогноза зрения, чтобы получить пространство клипа и прочитать значение глубины.

Проект также имеет функцию compute_depth, но выглядит довольно просто.

Ваша функция march_raycast уже вычисляет позицию target_, поэтому вы можете реорганизовать бит, применить ключевое слово out, чтобы вернуть его вызывающему, и использовать его в глубинных вычислениях:

//get position using pass-by-ref
float3 ray_pos = i.worldpos;
float2 output = march_raycast(ray_pos, i.viewdir, _far, _step);

...

//convert position to clip space, read depth
float4 clip_pos = mul(UNITY_MATRIX_VP, float4(ray_pos, 1.0));
o.zvalue = clip_pos.z / clip_pos.w;

Ответ 2

Возможно, возникла проблема с установкой рендеринга.

Чтобы позволить шейдеру выводить глубину на пиксель, его тесты глубины должны быть отключены. В противном случае GPU будет - для оптимизации - предположим, что все глубины ваших пикселей являются интерполированными глубинами из ваших вершин.

Поскольку ваш шейдер не выполняет глубинные тесты, он должен быть визуализирован перед геометрией, которая делает, или просто перезапишет все, что написала другая геометрия в буфере глубины.

Однако он должен иметь запись с глубиной, или вывод глубины вашего пиксельного шейдера будет проигнорирован и не записан в буфер глубины.

Ваш RenderType является прозрачным, который, я полагаю, должен отключить глубину записи. Это будет проблемой. Ваша очередь также прозрачна, и она должна быть рендеринга после всей солидной геометрии и обратно, что также будет проблемой, поскольку мы уже сделали вывод, что мы должны сделать это раньше.

Итак,

  • поместите ваш шейдер в раннюю очередь рендеринга, которая будет отображаться перед сплошной геометрией.
  • включить запись с глубиной
  • отключена проверка глубины.