Decal renderer does not discard pixels properly

31 views Asked by At

I'm trying to implement the deferred decals renderer.

I was provided with a few instruction points:

  1. decal instance has 2 matrices: decalToWorld and worldToDecal
  2. decals are rendered with front face culling
  3. vertex shader transforms unit cube vertices to the clip space
  4. pixel shader uses clip space XY to sample the depth texture, then it transforms it to the world space using inverseViewProjection and then to the decal space using worldToDecal
  5. On the DecalSpace position we perform clipping test: if X, Y or Z coordinate lies outside of the unit cube, then the pixel is discarded

I spawn the decal on key G clicked in position of the intersection point with the parent object. Thus I've got 2 matrices: decalModel and parentModel. Using these matrices I can implement the first point

1.

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

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

second point is straigth forward: 2.

D3D11_RASTERIZER_DESC rasterizerDesc{};
rasterizerDesc.CullMode = D3D11_CULL_FRONT;
rasterizerDesc.FrontCounterClockwise = TRUE;
... other settings

D3D11_DEPTH_STENCIL_DESC dsDesc{};

dsDesc.DepthEnable = false;
... other settings

I've also disabled depth testing (I've also tried with depth testing enabled but in read-only mode)

Point 3. is also easy, I guess:

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;
}

and points 4 and 5 are where the problems begin:

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;
}

since decals are rendered in the same pass as a normal geometry, I have to copy the depth texture because it's not possible to write and sample it at the same time. t_copiedDepthMap is a copy of G Buffer's depth texture, it does not contain the decal unit cube. SV_TARGET0 is the G Buffer's albedo texture.

Result:

enter image description here

Something is wrong, I believe some pixels should be shaded blue.

I've found this question: Deferred Screenspace Decals in Metal

and I've tried using the code from it:

PixelOutput PSMain(PixelInput input)
{
    const float depth = t_copiedDepthMap.Load(float3(input.position.xy, 0.0f));
 
    const float2 depthCoordinate = input.position.xy / resolution.xy;   
    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;
}

Now, it looks fine:

enter image description here

Why in the first screenshot the pixels are not shaded correctly?

I've found this article: https://mtnphil.wordpress.com/2014/05/24/decals-deferred-rendering/

In this article, the author, also uses objectPosition which is decalSpacePosition in my implementation.

Also, as a side question: I've tried sampling a decal color map using the method from Deferred Screenspace Decals in Metal

float2 textureCoordinate = localPosition.xy + 0.5;
float4 color = t_baseColor.Sample(g_linearClamp, textureCoordinate);

output.color = color;

and it does not look good:

enter image description here enter image description here

why is that?

0

There are 0 answers