Cześć,

Próbuje zaimplementować deferred decal renderer. Mam już napisany deferred renderer dla zwykłych obiektów, działa on poprawnie dlatego nie będę się tu na nim rozwodził.

Dostałem, myślę że, dość szczegółową instrukcję jak mogę zaimplementować decal renderer:

  1. instancja decal ma 2 macierze decalToWorld i worldToDecal
  2. decals są renderowane z front face culling
  3. vertex shader wykonuje przekształcenie z decal space do clip space
  4. w pixel shader użyj clip space XY do samplowania depth mapy, potem użyj inverseViewProjection do zamiany na pozycję w world space i worldToDecal do zamiany na pozycję w decal space
  5. usuń pixele, które leżą poza decal space XYZ unit cube

jest jeszcze pare punktów, ale poległem na tych.

Do spawnowania decali używam przycisku G i wysyłam promień po prostu na wprost kamery, znajduje punkt uderzenia z jakimś obiektem i w tym miejscu spawnuje decal. Działa to poprawnie, dlatego też nie będę się nad tym rozdzowidzł.
Pseudokod

void SpawnDecal(glm::mat4 decalModel, glm::mat4 parentModel);

if(KeyClicked(Key_G)) {
  const Ray ray = getRayForward();
  IntersectionQuery query{};

  if(FindIntersection(ray, query)) {
      const glm::vec3 hitPoint = ray.At(query.hitDistance);
      const glm::mat4 decalModel = glm::translate(hitPoint);
      SpawnDecal(decalModel, query.hitObjectModel);
  }
}

No i teraz przechodząc do punktów z instrukcji:
Punkt 1.

struct GPUInstance
{
  glm::mat4 decalToWorld;
  glm::mat4 worldToDecal;
};

void UpdateDecalInstanceData()
{
  GPUInstance& dst = getDecalGPUInstance();
  dst.decalToWorld = parentModel * decalModel;
  dst.worldToDecal = glm::inverse(dst.decalToWorld);
}

Punkt 2.

D3D11_RASTERIZER_DESC rasterizerDesc{};
rasterizerDesc.CullMode = D3D11_CULL_FRONT;
... reszta tych ustawień

D3D11_DEPTH_STENCIL_DESC dsDesc{};

dsDesc.DepthEnable = false; //może być też true, bo DepthWriteMask jest ustawione na Read
dsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
... reszta tych ustawień

Do tego punktu dorzuciłem też, że decals nie są renderowane do depth mapy

Punkt 3.

struct VertexInput
{
    float3 position : POSITION;
    
    //I_ -> instanced
    nointerpolation float4 decalToWorld1 : I_DECAL_TO_WORLD_ONE;
    nointerpolation float4 decalToWorld2 : I_DECAL_TO_WORLD_TWO;
    nointerpolation float4 decalToWorld3 : I_DECAL_TO_WORLD_THREE;
    nointerpolation float4 decalToWorld4 : I_DECAL_TO_WORLD_FOUR;
    
    nointerpolation float4 worldToDecal1 : I_WORLD_TO_DECAL_ONE;
    nointerpolation float4 worldToDecal2 : I_WORLD_TO_DECAL_TWO;
    nointerpolation float4 worldToDecal3 : I_WORLD_TO_DECAL_THREE;
    nointerpolation float4 worldToDecal4 : I_WORLD_TO_DECAL_FOUR;
};

struct VertexOutput
{
    float4 position : SV_POSITION;
    
    float4x4 decalToWorld : DECAL_TO_WORLD;
    float4x4 worldToDecal : WORLD_TO_DECAL;
};

cbuffer PerFrame : register(b0)
{
    row_major float4x4 cameraModel;
    row_major float4x4 view;
    row_major float4x4 projection;
    row_major float4x4 viewProjection;
    row_major float4x4 inverseProjection;
    row_major float4x4 inverseViewProjection;
};

VertexOutput VSMain(VertexInput input)
{
    VertexOutput output;
    
    output.decalToWorld = float4x4(input.decalToWorld1, input.decalToWorld2, input.decalToWorld3, input.decalToWorld4);
    output.worldToDecal = float4x4(input.worldToDecal1, input.worldToDecal2, input.worldToDecal3, input.worldToDecal4);

    float4 worldPosition = mul(float4(input.position, 1.0f), output.decalToWorld);
    
    output.position = mul(worldPosition, viewProjection);

    return output;
}

punkt 4. i 5.

cbuffer PerFrame : register(b0)
{
    row_major float4x4 cameraModel;
    row_major float4x4 view;
    row_major float4x4 projection;
    row_major float4x4 viewProjection;
    row_major float4x4 inverseProjection;
    row_major float4x4 inverseViewProjection;
};

struct PixelInput
{
    float4 position : SV_POSITION;
    
    float4x4 decalToWorld : DECAL_TO_WORLD;
    float4x4 worldToDecal : WORLD_TO_DECAL;
};

struct PixelOutput
{
    float4 color : SV_TARGET0;
};

Texture2D<float> t_copiedDepthMap : register(t0);

PixelOutput PSMain(PixelInput input)
{
    const float depth = t_copiedDepthMap.Load(float3(input.position.xy, 0.0f));
        
    const float4 clipSpacePosition = float4(input.position.x, input.position.y, depth, input.position.w);
    const float4 worldSpacePosition = mul(clipSpacePosition, inverseViewProjection);
    const float4 decalSpacePosition = mul(worldSpacePosition, input.worldToDecal);
    
    //clip(0.5f - abs(decalSpacePosition.xyz));
    
    PixelOutput output;    

    if(abs(decalSpacePosition.x) > 0.5 || abs(decalSpacePosition.y) > 0.5 || abs(decalSpacePosition.z) > 0.5)
        output.color = float4(1.0f, 0.0f, 0.0f, 1.0f);
    else
        output.color = float4(0.0f, 0.0f, 1.0f, 1.0f);

    return output;
}

wydaje mi się, że zaimplementowałem już wszystko, jednak wynik jest taki:

screenshot-20240327120905.png

a wydaje mi się, że te piksele "w środku" czerwonego powinny być niebieskie. tak jak jest napisane w shaderze.

Co robię źle? Dlaczego to nie działa poprawnie?

Znalazłem w necie takie coś:
https://stackoverflow.com/questions/66442226/deferred-screenspace-decals-in-metal

No i pozmieniałem swój kod tak aby pasował do tego co koleś tutaj pokazuje:

PixelOutput PSMain(PixelInput input)
{
    const float2 depthCoordinate = input.position.xy / resolution.xy;
    const float depth = t_copiedDepthMap.Load(float3(input.position.xy, 0.0f));
    
    const float3 screenPosition = float3((depthCoordinate.x * 2 - 1), -(depthCoordinate.y * 2 - 1), depth);
    const float4 worldPosition = mul(float4(screenPosition, 1), inverseViewProjection);
    const float4 objectPosition = mul(worldPosition, input.worldToDecal);
    const float3 localPosition = objectPosition.xyz / objectPosition.w;
    
    PixelOutput output;

    if(abs(localPosition.x) > 0.5 || abs(localPosition.y) > 0.5 || abs(localPosition.z) > 0.5) {
        output.color = float4(1.0f, 0.0f, 0.0f, 1.0f);
    } else {
        output.color = float4(0.0f, 0.0f, 1.0f, 1.0f);
    }
    
    return output;
}

No i to już wygląda lepiej:

screenshot-20240327120542.png

ale dlaczego mój pierwszy sposób nie działa?

w tym artykuje: https://mtnphil.wordpress.com/2014/05/24/decals-deferred-rendering/ też jest zrobione tak jak ja zrobiłem na początku