Multiple Lights [HLSL][Direct3D9]

Started by
13 comments, last by leet bix 15 years ago
Hey, I'm trying to create a system where I can render numerous lights at the same time, as well as lights of different types (directional, point and spot lights). I've read numerous ways, but are unable to implement, the only one that I was able to was using multiple render targets, then merging all the images created from every light into a single frame, I didn't like this as it is very slow to use multiple render targets, video memory intensive and not all hardware supports it. Ok, so I'll keep it simple for now, and say, how would I render the following scenario? 1 directional light - sun 1 point light - lamp
Advertisement
Hi,

There're different techniques you can use:

1 - Declare seperate light parameters in your shader, compute light illumination for each of'em (c1 for directional and c2 for point). Then just add c1 to c2.

2 - Use a full-screen quad render target texture. Using both SrcBlend and DestBlend as "One" and BlendOp as "add", you can render each light illumination to this texture (and this will be automatically summed with previous lighting result).

3 - Use deferred shading. This will be the best for all types of light; also serves better performance for numerous lights.

I recommend 3. 1 and 2 "eats" the performance because of rendering each model "num_of_lights" times and this is not good.

You can find a lot of document for Deferred Shading.

Hope this helps.
There's no "hard", and "the impossible" takes just a little time.
I'm curious about your second method, could you explain this in a little more depth?
I've heard there's a few issues with deffered shadding including the inability to easily implement transparency.
I recommend technique 2. But it can be done easier. You don't need to render to a separate texture. Just use the existing backbuffer.
You could do it like this:
1. Render a depthpass (z-test and z-writen enabled), including ambient lighting if you want to.
2. Enable alpha-blending as programci_84 suggests and disable z-writes.
3. Render the scene for each light
4. Finished


Deferred shading has benefits, but in my experience you need quite complex scenes with a lot of lights to really make it worthwhile. For the scenario you describe I would suggest to start with the easier method. It probably will render faster, too.
Hi,

Quote:I'm curious about your second method, could you explain this in a little more depth?


Of course.

* Create a texture same size as back-buffer:
D3DXCreateTexture (...);


* Assign this texture's level0 surface as device's render target #0:
LPDIRECT3DSURFACE9 pSurf, pSurfBackup;RTTex->GetSurfaceLevel (0, &pSurf);pD3DDevice->GetRenderTarget (0, &pSurfBackup);pD3DDevice->SetRenderTarget (0, pSurf);


* Begin the scene, render for each light
pD3DDevice->SetRenderState (D3DRS_ALPHABLENDENABLE, TRUE);pD3DDevice->SetRenderState (D3DRS_SRCBLEND, D3DBLEND_ONE);pD3DDevice->SetRenderState (D3DRS_DESTBLEND, D3DBLEND_ONE);pD3DDevice->BeginScene();//maybe you want to clear  For each light  {     add light contribution via your shader;     drawObjects;  }pD3DDevice->EndScene();pD3DDevice->SetRenderState (D3DRS_ALPHABLENDENABLE, FALSE);


* Restore the original backbuffer
pD3DDevice->SetRenderTarget (0, pSurfBackup)


B_old says: "you don't need to render to a separate texture"
In this way, you can just render light pass. But if you want to add some post effects (HDR, motion blur, DoF etc.), you have to render to a seperate texture ;)

Hope this helps.

Regards,
Rohat.
There's no "hard", and "the impossible" takes just a little time.
Great thanks heaps for the help!! I think I'll go with the second option, as B_old said, there benefits are only worthwhile if you have a number of complex lights.

thanks =]
Just one more question, do I need to transform the scene in the vertex shader for every pass? it seems very expensive.
Re-rendering each object per light affecting it is the simplest approach. Light accumulation is additive, and provided the frame buffer is in a linear color space, it will just work.


Pros:
- only need to implement 4 light shaders per material type at first (global/ambient, point, spot, directional), then 3 more if you need to handle shadows

Cons :
- z fighting issues can come up since the vertex shaders will occasionally not be perfectly exact between each pass.
- overdraw can rapidly get bad and will be view dependant
- terrain needs to be thought out so you don't render too much of it for small spot and point lights


If you have a common configuration of lights in a scene its worth making a shader that does that (say ambient and a directional light). Another set of shaders to handle common cases like 1/2/3/4 point lights all at once can help perf quite a bit as well.

Its possible to write a single shader that does all 3 (point/spot/directional) with static branching, but they will not perform as well as specialized shaders/


A deferred rendering approach has a huge up front cost but they payoff is more stability and predictability in the performance with lights. Translucent stuff basically has to fall back to the traditional forward rendering approach.
http://www.gearboxsoftware.com/
Quote:Original post by leet bix
Just one more question, do I need to transform the scene in the vertex shader for every pass? it seems very expensive.


Probably, and unless its doing something complex like rendering a 100,000 poly skeletal mesh with 4 bone influences per vertex I wouldn't worry about it a whole lot. You should always try to put as much math into the vertex shader as possible, so that the pixel shader doesn't have to. Now the real trick is doing that and also using as few interpolators as possible :)
http://www.gearboxsoftware.com/
Alright, one more question. So I have say 3 different effects, Directional light, point light and spot light, should I have 3 different effect files, or 3 different techniques within a single effect file?

This topic is closed to new replies.

Advertisement