dealing with more than 1 light?

Started by
4 comments, last by eschan01 16 years, 11 months ago
Hi, hope someone can point me to the right direction on this. I'm new to directx. I'm currently learning how to shade an object with the fx framework. So far, I can shade an object with 1 light present. I'd like to eventually be able to define an arbitrary number of lights in the C++ code, pass the light parameters into the shader, and shade the object with multiple lights. First, I need to figure out how to deal with 2 lights, and I'm running into a brick wall. I've searched the net for info. on dealing with multiple lights, but I haven't found anything that's helpful. Most of the info. I can find is too vague. The process is usually described as "for each light - shade the object." While the concept seems straight forward, how exactly can it be done? I have tried this. I have 2 passes in the shader (in the same technique). The first pass shades the object against the first light, and the second pass shades the object against the second light. However, with the 2 passes being independent of each other, only the effect of the second pass can be seen. In effect, only the second light is showing up in the scene. C++ code looks something like this ------------------------------------ effect->BeginPass(0); drawObject(); effect->EndPass(); effect->BeginPass(1); drawObject(); effect->EndPass(); ------------------------------------ The fx tech looks something like this -------------------------------------- technique lightTech { pass p1 { vertexShader = compile vs_2_0 light1vs(); pixelShader = compile ps_2_0 light1ps(); } pass p2 { vertexShader = compile vs_2_0 light2vs(); pixelShader = compile ps_2_0 light2ps(); } } where light1vs and light1ps simply perform shading with the first light's parameters, and light2vs and light2ps perform the exact same calculation but with the second light's parameters. I know this is wrong, but I'm just not sure what the right thing to do is. Anyone able to describe what I need to do in order to deal with 2 lights? Links to tutorials and the sorts on the subject would be helpful as well. Any help is greatly appreciated.
Advertisement
Most tutorials skip this because it's complicated and there isn't a good answer, yet.

You can use your multipass method, just enable alphablending for additive blends in the second pass. Using these two passes you can do any number of lights. Render the first light with the first pass, and all other lights with the second pass. This is probably the simplest way to do things.

Because fill rate is expensive, the simple way isn't the fastest. It's better to perform the math for more lights inside one shader pass. The simplest is to choose a maximum number of lights, perform the math for each, summing the results, then mixing with textures, etc. Any unused lights can be set to black, essentially eliminating them... of course they still take cycles to run.

There are various approaches to including just the lights you need and each as advantages and disadvantages. You can use many techniques, one per combo of lights. You can do runtime shader creation and compilation. You may opt for branching and loops. How you approach these depends on your needs. Do you have many, many lights? Perhaps deferred shading is what you want. Do you have shadow casting lights? Perhaps this limits you to one or two lights per pass because of the extra texturing requirements.

The many techniques idea fails because of the sheer number of possibilties?
No lights, one light, two lights, three lights? (4 techniques)
Oh, and each of those can be directional, point, or spot? (3*3*3+1 = 28 techniques)
They may or may not have expensive specular calculations? (6*6*6+1 = 217 techniques)
May be shadowed? (12*12*12+1 = 1223 techniques)
Might be affected by normal maps? ((12*12*12)*2+1 = 2445 techniques)
Might be fogged? (4890 techniques)
For the FX system where a pixel and vertex shader are both part of the technique you also run into needing all those techniques per choice of vertex shader (no bones, 1, 2, 3, or 4 bones per vertex, setup for normal mapping, texture coord transforms, etc.)

This is where choosing runtime compilation comes in (or just choosing to just do one light per pass for that matter). You create the shaders you need at runtime. You won't need anywhere near all the possible combinations. You can use the effect compiler interface to get the compiled shaders you do use and save them to disk, saving compilation time for the end users.

Branching requires SM3 hardware to be effective, and certain branching schemes are only effective on ATI cards. SM4 hardware may solve all these problems, I haven't got any to test with so I haven't really looked into it.

I chose runtime compliation back on SM1 hardware and on the original XBox, building the appropriate vertex shader based on world, mesh, and light states for a particular pixel shader. On newer hardware, I'm not 100% certain which way to turn... nobody is really, hence the lack of tutorials on it.
I think that currently you are best off with a static number of lights for each vertex.. say 6 per-pixel and maybe 12 more per-vertex, with and LOD based on distance.. in other words, your six nearest lights to the vetex are done per-pixel, than you can fill in the farther ones with simple vertex lights.. you can fade in the bump mapping and specular so the transition is not very noticeable.

In ps 2.0 you can basically do a maximum of 2 per-pixel lights per pass.. you can do about 4 with ps 3.0.. so basically a two or 3 pass shader is needed for 6 lights..

Also, if a light is totally dark, the hardware will optimize out some of the pixel operations and they will not be so slow to calculate..

If you're dealing with per-vertex lights, ATI's "fixed function shader" can serve as a good starting point.

If that's not applicable, your multi-pass idea should work, even if it won't be optimal. All you need to do is enable blending, using ONE for both source and dest, so that the pixel values are added together. (See D3DRS_ALPHABLENDENABLE, D3DRS_SRCBLEND and D3DRS_DESTBLEND under D3DRENDERSTATETYPE in the DX docs.)
Quote:Original post by Matt Aufderheide
In ps 2.0 you can basically do a maximum of 2 per-pixel lights per pass.. you can do about 4 with ps 3.0.. so basically a two or 3 pass shader is needed for 6 lights..


That's certainly not a rule set in stone. I've developed a SM3 shader without much thought that allows me to use 16 per-pixel point lights per pass. Granted on my Geforce 6200 the result is not exactly speedy, but it is much faster than 1 pass per light. I can comfortably handle about 8 without too much slowdown.

.
Thank you, Namethatnobodyelsetook, for providing a broad perspective. Thank you, Matt Aufderheide and ET3D, as well for providing couple ideas on how to do this. I think I'll try out the one-light-per-pass-with-alpha-blending approach just to get something working so I can see the effect of multiple lights. Doing the calculation for multiple lights in one shader pass is probably what I'd try next. Thanks again, I appreciate everyone's advice.

[Edited by - eschan01 on May 21, 2007 12:16:22 PM]

This topic is closed to new replies.

Advertisement