Sign in to follow this  
CDProp

Implementing basic lighting shader, using more than one light source...

Recommended Posts

How many light sources do you think is appropriate for a modern 3d game? Let me tell you what I'm doing. I am using effect files with DirectX 9.0c. I thought it might be useful to have a couple directional lights, a couple of point lights (for things like explosions), and an ambient light. Each light would have a color. The directional lights would have a direction, and the point lights would have a position. I also have a set of bool values for turning the above lights on and off, so that I can check a single value before deciding if lighting calculations should be done for that light. The shader is being designed for diffuse and specular. There is one single global value for specularity (the brightness of the specular highlight), diffuse (the brightness of the scattered light), and specular sharpness (size of the highlight). This affects all of the above lights, so if an object has any specularity at all, then it will have separate highlights for each of the active lights. Where I'm running into trouble is I'm using a lot more shader constants and interpolants than I'm used to using for my basic shaders, and I'm wondering if this is normal. I think I might be missing out on some approximations and/or optimizations that would make this easier. For instance, I'm using 17 global variables for the lighting calculations alone. The ambient itself has two (the "active" bool and the color), the other lights each have three ("active" flag, color, direction/position), and then three values that apply to all lights (diffuse amount, specular amount, specular sharpness). And since I'm doing this per pixel, I have to calculate half vectors for the 4 non-ambient lights (thus using 4 TEXCOORD interpolants), plus light direction vectors for the two point lights (using 2 more TEXCOORD interpolants), plus the vertex color and normal. I could get rid of the half vectors by passing the eye vector down (eyePos - vertPos), thus exchanging 4 interpolants for 1, and then use the Phong model, but then I have to calculate reflections for all four lights in the pixel shader, and I'm not sure if it's worth the trade-off. This, of course, gets more complicated when you include transformation, texturing, normal mapping, environment mapping, etc.

Share this post


Link to post
Share on other sites
I'd go with an array of light data for each light type, and a for loop in the shader for each light type. That way the light count is flexible, and you don't need a boolean per light - just one light count constant per light type.

You can either compile one version of the shader for each different combination of light counts (0-N for each type), or have one shader with variable loop counts.

The light count for a game will vary significantly depending on the game type. For example directional lights are much more useful outdoors, and an indoor scene may well have more than two point lights affecting one object.

For your ambient light don't worry about the active boolean. Just set the colour to black when it's off. Testing the boolean will be more expensive than just adding zero.

Share this post


Link to post
Share on other sites
Thanks, guys. That seems to help cut down on the code quite a bit. What about pixel shader inputs, though? My current plan is to do per-pixel lighting. Should I limit the number of per-pixel lights to 1?

The reason I ask is that it seems that pixel shader inputs will get used up very quickly, or else I'll have to do a lot of calculation in the pixel shader, neither of which seem desirable for a lighting model (but I have no frame of reference for this).

For instance, for point lights, I can't simply use a global direction value. I can have a global position value, of course, but the light's position relative to the pixel being rendered has to be known. I can either calculate this "light vector" on a per-vertex basis, and send it through to the pixel shader to be interpolated across the face, or I can calculate it on a per-pixel basis. The former solution requires the use of a shader input (one per point light), and the latter solution doesn't, but the latter solution requires per-pixel calculation of the light vector, which doesn't seem like a good tradeoff.

So my instinct is to use the first approach. I will need MAX_POINT_LIGHTS pixel shader inputs reserved for this purpose. Which, if isn't bad if MAX_POINT_LIGHTS is two or three.

But then wait a second, what about the specular highlight for each of my lights? For that, I need to calculate a half vector. This half vector is, again, not constant for the entire mesh. It is dependent on the location of the pixel being rendered. This time, it will do me no good to defer computation of the half vector to the pixel shader because either way, I'm going to need another pixel shader input (that is, I either calculate the half vector for each light in the vertex shader and send it to the pixel shader to be interpolated, or I calculate the "eye" vector in the vertex shader for each light, and send it to the pixel shader to be interpolated...I already have the "light" vector from the previous step...for the point lights, at least).

So I guess another approach would be to drop the Blinn-Phong and just go with regular Phong. I still need the eye vector, but I can ditch the half vector calculation. But then, I need a reflect calculation...is that really any better?

It seems that, no matter how I look at it, I'm looking at MAX_LIGHTS additional pixel shader inputs for the specular stuff, on top of the MAX_POINT_LIGHTS inputs for doing the diffuse calculations for the point lights.

I guess one approach I could take is to treat the point lights like directional lights. That is, before drawing the object, find the (LightWorldPos - ObjWorldPos) vector, and then set that as the direction for one of the directional lights in the shader. I might not be able to notice that it's not "true" point lighting for most meshes.

And maybe I'm being unreasonable to think that every light in the scene has to be per-pixel. If I think about it, there's usually one key light to illuminate an object. The other lights are usually weaker, just to soften the shadows a bit, and so perhaps it would be worth it to do the per-pixel and specular (I don't want to do per-vertex specular because I think that looks terrible) for just the key light in the scene, and per-vertex lighting for all others?

Share this post


Link to post
Share on other sites
It depends a bit on what hardware you're working with, but in general it's probably going to be a win if you trade passes for arithmetic instructions. Modern GPU's tend to have plenty of ALU power to spare, but bandwidth runs up quick. This makes the prospect of blending a mesh several times into the backbuffer a somewhat unappealing prospect. Plus don't forget you'll be repeating all the work of pulling vertices out of the vertex buffer, transforming them, setting up triangles, and calling DrawPrimitives.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this