Thursday, May 28, 2009

My First Deferred Shading: Light Prepass Rendering

Ahh the teapot mesh... the always used but pne of many useful mesh for rendering objects. Especially in prototypes which I am currently doing. Now, without further ado, I give you, MY FIRST DEFERRED RENDERING (drum-rolls).

I'm doing a Light PrePass rendering by good'olde Wolfgang. The process is remarkably simple once when you understand it. Similar to Deferred Rendering, (well this IS deferred rendering), which only renders normal/depth in the first pass. The lights are then rendered using the first pass normal and depth buffer. The light pass are accumulated and then applied on the gather pass.

The image above is just a teapot rendered with two point light. Not much I can show right now. But the key thing to do here is how you pack the data in the buffers. Currently, I tested out 3 ways of packing the normal/depth and 2 ways of light accumulation passes. I find Pat Wilson's suggestion on transforming the normals to spherical coordinates you can mind-blowingly pack the 3 floats into a 1 and a partial half(just enough to store the sign of the normal.z). I find Reltham via Drilian's (in suggestion also interesting on how the light accumulation pass is done, which is to do a multiplicative blending, instead of standard alphabend. The colors are sharper and the mid-blend of two lights seems to look more 'realistic'. I haven't done any tone mapping or normal mapping, I'm quite excited on that after I nail the packing of buffers.


Eric said...

I love this method, but I have a question. After you render the light buffer, you do your normal forward-rendering and apply the light buffer to it. But since the lighting will have to be done in screen-space, do you have to forward-render the scene into a texture and then apply the light buffer to that?

vidextreme said...

Hi Eric,

Welcome to my humble blog.

No, you dont need to put the forward rendering to a texture. What you need to do is while your doing the forward rendering you'll sample the light buffer and blend them on the same pass.

Try this code:

float4 pos = mul(input.pos, matWorldViewProj);
output.pos2 = pos.xy/pos.w;

float2 uv = 0.5f * (float2(input.pos2.x,-input.pos2.y) + 1);
float4 light = tex2D(lightbuff, uv);

This looks similar to a texture projection or shadow map formula. I haven't really figure out why I need to process this UV in PS to remove some flickering errors.

In terms of differing materials, what you can do is do not finalize the specular when you're doing the Light accumulation pass. You can apply a specular power map when you're doing the forward rendering making use of the unfinalized specular (which I assume its in your alpha channel of your light buffer).

Mohammad Adil said...

I am looking to implement deferred shading. I have some experience in graphics (mostly ray tracing) and little experience in the graphics pipeline. Can you tell me how to go about implementing deferred shading? I mean what do I need to learn and if there are some good tutorials on the net? I will be very grateful for your help

Mohammad Adil

vidextreme said...

Hi Mohammad,

Actually, deferred rendering is very similar to ray tracing. This is especially closer to the light-prepass approach (by Wolfgang Engel).

In ray-tracing (assuming this is how you are familiar with), the pixel contribution is base from the bounce of light you get from the normal a mesh and how it is pointing to a light source.

In deferred lighting, we also do this. Except we this is how we do it.

(Full Deferred Technique)
1. We render all object we want to see in frame. Into 3 seperate full screen-buffers. We place the albedo on first, normals on the second and depth on the third. The often call this as G-Buffer

2. Each time we render a light, say a omni-directional light, we compute the light contribution into the scene (like a simple NDotL). We can render this in a another seperate buffer or we can render this directly to the albedo buffer. (here you need to decide which suits your requirements)

3. Actually, there is no third process with this Full Deferred approach.

However, you will notice that we are using a lot of full screen buffers. This is especially more memory extensive if we go for floating point textures for HDR approach.

Hence, the previous technique I mentioned, light-prepass deferred rendering.

In this technique, we don't be rendering (and don't need an)albedo full-screen buffer. Instead we just need at least two information on the first pass (this can be in a single buffer or two depending on desired precision).

1. We render the normals and depth in buffer/s.

2. We have a seperate buffer which will call a light accumulation buffer. Here, each light is rendered using the normals and depth information.

3. Once we are done with the accumulation of lights info, we do a 'gather pass' which we render the albedo and lit it with the accumulated buffer. The good thing with approach is we can reuse buffers. At this stage we may not need the normals or depth anymore so we can reuse.

I hope this helps. I'm not sure if I can point you to a good tutorial site. But I'm sure you can get them by searching the terminologies I used here in this reply.


vidextreme said...

Sorry, there is a third process on Full Deferred, this is depending which direction you take on stage 2. Some full-deferred technique have a seperate compose pass which means, putting the albedo and light buffer together.

vidextreme said...

Sorry, there is a third process on Full Deferred, this is depending which direction you take on stage 2. Some full-deferred technique have a seperate compose pass which means, putting the albedo and light buffer together.