Screenspace Decal

i try to implement this, but im not quite sure how it work. Do i ned to recontruct depth buffer before i draw the decal ?

“Note that depth testing is turned off on this box - even though half of the box is behind the wall we can still see it. This is a workaround for a “feature” of XNA4 managing your depth buffers for you; depth buffers in XNA are bound to a texture and cannot be used again without reusing the same texture which is inconvenient here as we’ll see later. My workaround for this is basically to do the depth test myself in the pixel shader but you don’t need to do this, you could use depth testing (read ON, write OFF) just as you usually do.”

But if you reconstruct you can save some fill rate, at the cost of reconstructing. Probably only worth with a lot of decals

I think the implementation of this is done in XNA and the author invites to look at the code, I’m sure you can find good answers there :slight_smile:

can someone help pls? i still cant get it work

just do some cleanup on my code, my problem is i cant get the decal clip correctly. Hope any one can help me figure out what is the problem.

here is the shader

float3 CalculateObjectPosition(in float4 positionCS, in float4 positionVS)
float2 screenPos = positionCS.xy / positionCS.w;
float2 texCoord = float2(
(1 + screenPos.x) / 2 + (0.5 / 1024),
(1 - screenPos.y) / 2 + (0.5 / 768)
float3 n;

//Sample the depth buffer
float sampledDepth = DepthMap.SampleLevel(pointSampler, texCoord, 0).x;

float3 vFrustumRayVS = * (FarClip / -positionVS.z);
float3 vpos = vFrustumRayVS * sampledDepth;
return mul(float4(vpos,1), InverseView).xyz;


VS_Out Decal_VS(VS_In input)

VS_Out Out = (VS_Out) 0;
float4 viewPosition = mul(input.Position, WorldView);
Out.Position = mul(viewPosition, Projection);
Out.PositionCS = Out.Position;
Out.PositionVS = viewPosition;
return Out;


PS_Out Decal_PS(VS_Out input)
PS_Out Out;

float3 oPosition = CalculateObjectPosition(input.PositionCS, input.PositionVS);

clip(float3(0.5, 0.5, 0.5) - abs(;
Out.RT0 = float4(1, 0, 0, 1);

A little simplification in your code: You don’t need the half pixel correction anymore. Was necessary in XNA but is not necessary in MonoGame :slight_smile:

I mean this

(1 + screenPos.x) / 2 + (0.5 / 1024),
   	(1 - screenPos.y) / 2 + (0.5 / 768)

Just write this, it should be fine:

(1 + screenPos.x) / 2,
		(1 - screenPos.y) / 2

What di you mean clipping does not work? How does it look like?

here is box without clip

the blue color region is wat it should look like

but in actual result my box just gone.

Don’t you use the part:

//Convert from world space to object space
float4 objectPosition = mul(float4(worldPosition, 1), InvWorld);

//Perform bounds check
clip(0.5 - abs(;


EDIT: nvm.

i tried, don’t work. im not sure is my reconstruct wordposition wrong or not. Bcoz i was try to reconstruct wordposition from linear depth buffer

What makes me scratch my head is
clip(float3(0.5, 0.5, 0.5) - abs(;

Would not it be better with
clip(0.5 - length(;

I’ve never seen clip using float3 while it accepts it.

It think he uses linear depth too.

i just follow the code from this article

He does

//Perform bounds check
clip(0.5 - abs(;

I dunno how hlsl handles mixing a float with a float3 ? If in object space, length shoud be ok ?

tried, still same…

After reading the tutorial, it seems to be ok as the box is one unit large. It does prevent your box to be drawn if > 0.5 as intended

It’s a box, so you want abs(). Length would work for circles.

is my reconstruct world position from linear depth correct?

no, you forgot to convert to object space

//Convert from world space to object space float4 objectPosition = mul(float4(worldPosition, 1), InvWorld);

After the inverseview right? Actually that my mistake, but after added this line still same. Have u try?

yeah I’ve got it working with no modifications

I have updated the deferred engine to work with that, but i streamlined some stuff.

This is the code now

float4 DecalPixelShader(VertexShaderOutput input) : SV_TARGET
    //read depth, use point sample or load
    float depth = DepthMap.Load(int3(input.Position.xy, 0)).r;

	//Basically extend the depth of this ray to the end of the far plane, this gives us the position of the sphere only
	float3 cameraDirVS = * (FarClip / -input.PositionVS.z);

    //compute ViewSpace position
	float3 positionVS = depth * cameraDirVS;

	//Transform to box space
	float3 positionOS = mul(float4(positionVS, 1), InverseWorldView).xyz;

	clip(1 - abs(;

	float2 textureCoordinate = (positionOS.xy + 1) / 2;

	return DecalMap.Sample(AnisotropicSampler, textureCoordinate);

InverseWorldView is inverseView * InverseWorld

is the lineardepth?

InverseWorldView is inverseView * InverseWorld
is same as
inverseWorldView = inverse(view * world)

yes linear depth, and no they are not the same

1 Like