Jump to content

April 2017 »

2324252627 28 29

Recent Comments

- - - - -

Decals, finally!

4: Adsense

I finally have a decal implementation working in the graphics component of my engine project. This really was a simple implementation and I've had this on my to-do list for a while, but it just got buried by a ton of other things I wanted to get out of the way first. Anyway, here's a screenshot using some art I made some time ago (click to enlarge):

Posted Image

Decals have access to all the features of the engine's material system and are completely managed on the GPU, so there's no need to involve the physics engine to calculate decal positions for example.

Both 2D decals and volume decals are supported.

The scene in the screenshot I posted is still a work in progress, hence the absence of shadows, indirect/ambient lighting, anti-aliasing and the crude texturing. I'll probably post a journal entry when it's completed.

Jul 26 2013 07:33 PM

This is great work man! Love to see what you are doing and how you do it. Do you mind sharing where you found out how to get this to work? We attempted it but got stuck. Thanks again

Jul 26 2013 08:35 PM

I based my decal rendering technique on the "Volume Decals" article by Emil Persson in GPU Pro 2, although I had to make some tweaks to get texture lookups for 2D decals to work correctly.


For each decal volume you render it comes down to retrieving the world position of your scene geometry at the fragment which is currently being shaded (you can use your depth buffer to reconstruct this position) and using this position to do a texture lookup in your decal texture(s).


If you're using deferred shading you can just overwrite (or even blend) the attributes stored in your g-buffer where your decal is rendered, which means your decals can have their own material properties, and that they can have normal maps and other cool effects.


If you don't own GPU Pro 2 I could give you a more detailed overview of the implementation if you'd like.

Jul 26 2013 09:36 PM

Thanks for the quick response... this is where we got stuck



Jul 27 2013 12:51 AM

I've read through the thread you posted, and our techniques differ quite a bit.

In my implementation I don't actually do any projective texturing by setting up a projection camera. What I am doing is setting up a 'decal volume' which in my case is a simple cube. I basically look for an intersection plane between my decal volume and the scene geometry. Afterwards I do an orthogonal projection of my decal texture onto this plane within the bounds of the decal volume. The projection direction is determined by the rotation of the decal volume.


The extents of your decal volume can be used to cut off your projected texture in all directions, that way your decal won't project onto surfaces it shouldn't be applied to.


To get your coordinates for sampling your decal texture you can use a reconstructed world position as I mentioned before. Of course you need to transform this position to be local to your decal before it's any useful for texture sampling.

For volume decals I just use the decal-local position for texture sampling. For 2D decals I just ignore the z-component of the sample position, which would be the same as sampling from a volume texture where each depth slice would be identical.


I hope this helps :)

Jul 27 2013 01:10 AM

Again thanks, Ill pass this on to telanor and see what he thinks. Your method may be better, i dont know! all that hoopla is beyond me!

Jul 27 2013 06:40 PM
I didn't realize you could use the volume decal technique for 2d decals. Humus' tutorial seems to be the only one describing the technique on the internet, but how he gets the texcoords to sample the decal don't make much sense to me. His ScreenToLocal matrix seems to be more than just an inverse View*Projection matrix.

Could you explain the transforms you used to convert the screen position + sampled depth into the texcoords? My understanding is that if you multiply the screen position by the inverse viewprojection matrix, you get a world space position, which doesn't seem particularly useful for sampling a texture.
Jul 28 2013 01:28 AM

You do indeed get a world position, but you need to transform this to a position local to your decal volume. I just use my decal volume's inverse world transformation to get this local position.

Jul 28 2013 06:11 PM

Ok that got me closer, I think I just need to scale/offset the resulting coordinates so they're proper texture space coordinates. The usual 0.5 * float2(pos.x, -pos.y) + 0.5 doesn't quite seem to be right. What's the correct way to do this?

Also, if you're using a deferred renderer, how are you handling the transparency in the decal? My GBuffer's alpha channel is used for the specular component, so writing the alpha ends up making the decal shiny.  Figured it out, disabling alpha writes solves it.

Jul 29 2013 09:48 AM

The range conversion from [-1, 1] to [0, 1] works fine for me, it's just a standard (pos.xy + 1.0) * 0.5.


About disabling alpha writes, you might want to keep it enabled and let your decal write out its own material properties, like specular albedo, roughness, normals, etc. For decal alpha I just use an alpha map, but since I'm using deferred shading it is of course limited to either being completely opaque, or completely transparent (ie. I just discard the pixel). Maybe I should try experimenting with additive blending for some material properties, could give some interesting results.

Jul 29 2013 10:19 AM

With your system, have you noticed that when you get a certain distance away the decal offsets? We have this odd thing where if you move past a certain distance the decal changes and offsets by almost half. Thanks for the help with this and thank you for the updates! I owe you one!

Jul 29 2013 10:22 AM

Can't say I have this problem. Could you maybe provide some screenshots?


Could this be related to depth buffer precision? I had some nasty issues with this in the past, so I'm keeping a separate buffer with depth stored linearly from which I reconstruct my positions. It's not an optimal solution IMO, but it does solve a lot of headache inducing issues.

Jul 29 2013 01:59 PM

For anyone interested, here's another render of multiple decals with varying orientations (click to enlarge):


Jul 29 2013 04:08 PM
I'm surprised you're decals look that good with alpha clipping.  This is what ours looks like with alpha clipping and alpha blending.  This is the weird wrapping in the distance.

We're using a log depth buffer, so there shouldn't be any precision problems. This is an output of the sampling coordinates close up and far away. As you can see they don't change, so its a rather weird problem.
Edit:  I found out the distance thing is because of mipmapping.  If I disable mipmap generation when loading the image, the issue goes away.  Of course then the decal looks bad in the distance... any thoughts on why DX is sampling the mipmaps weirdly?
Jul 29 2013 04:20 PM

Wow, that is rather weird indeed. Since the sampling coordinates remain unchanged, could it be a problem with your decal texture's mipmaps?

Jul 29 2013 08:52 PM

Indeed, that is what is causing it. Any idea on what could be done to solve that?

Jul 30 2013 12:39 AM

How are you generating your mips right now? Are you letting your graphics library generate them for you, or do you generate them as a pre-processing step?

Jul 30 2013 12:51 AM
They're generated when calling D3DX11CreateShaderResourceViewFromFile
Jul 30 2013 01:14 AM

I've never used the D3DX library so I couldn't say whether you need to take any precautions with said function related to mipmap generation.


Are the texture's dimensions both powers of two? Normally seeing this shouldn't cause major problems anymore these days, but you never know.


For what it's worth, I can really recommend doing mipmap generation and other texture-processing jobs in a small custom off-line tool. You get a lot more control over the visual quality of your textures, and you reduce your game's loading time. If you'd be interested in developing something like this, a library like nvidia texture tools is a good start.

Jul 30 2013 01:33 AM
Interestingly enough, making it a power of 2 size fixed it, which is strange since DX10+ supports non-power-of-two sizes. Thanks for the help

Edit: If I change the MipFilter from Box to Triangle, it works with non-power-of-two sizes.
Jul 30 2013 01:45 AM

My pleasure :) Glad to hear you got it working

Note: GameDev.net moderates comments.