How to improve DirectX lights dramatically

Blitz3D Forums/Blitz3D Programming/How to improve DirectX lights dramatically

JoshK(Posted 2004) [#1]
The major problems with DX (or GL) lights are as follows:

1) There can only be 8.

2) They have an unlimited range, more or less.

The solution to both, which I have implemented in OpenGL, is as follows: For each object, calculate the closest 8 light entities to it, and enable the 8 or less DX lights you need for that object. Give the DX lights infinite range, and just darken them according to distance from the current object, so as to give the lights a definite inverse square or linear falloff. It's easy to do, and improves the quality of the dynamic lighting dramatically. The same can be done with vertex lights, but DX light handling is so much more optimized. As of now, DX lights are pretty much useless for dynamic lighting of characters and items, which is what they are usually used for.


Rob Farley(Posted 2004) [#2]
Josh,

I showed a demo of exactly this about 6 months back now. Although it only used the 2 lights closest to the player rather than 8.


smilertoo(Posted 2004) [#3]
The 8 light limit doesnt quite hold up, after 5 geforce cards take a big performance hit.


JoshK(Posted 2004) [#4]
Rob, unfortunately this can only be implemented by Mark, since it involves changes during the rendering process. I'm sure you know that, but I am just stating it for anyone reading.


RetroBooster(Posted 2004) [#5]
I've been using exactly the same setup in OpenGL ever since I started using it, in blitz reducing the scene lights to the 8 most vital ones (or less) works quite well too however.


AntonyWells(Posted 2004) [#6]
Per pixel lighting is the best method for lighting.

What you do is go through each poly in the scene, map it as you would if it were a lightmapper, only for each texel you've got mapped into u,v space, get the normalized vector from it's world space position into the centre of the surface.
flip this vector, and paint it as a dot3 compatible normal into the lightmap.

Now, when you move your light you get some absolutely awesome glowing per-pixel lights.


Shambler(Posted 2004) [#7]
DX lights do have a 'range' parameter. It acts like a cutoff point and beyond this the light has no effect at all, i.e no lighting calculations are carried out by the gpu. Unfortunately Blitz does not expose this parameter to us so we have 'infinite' lights.


JoshK(Posted 2004) [#8]
Blitz has a LightRange() command.

If DX is like OpenGL the light range actually has no point at which lighting is zero, and the falloff seems to be even less than linear.

This is an extremely simple solution that could be introduced in a few lines of code.


Rob(Posted 2004) [#9]
I think vert lighting is pretty cool too. You can set this up yourself.


JoshK(Posted 2004) [#10]
But it isn't hardware optimized, so it is much slower.


Shambler(Posted 2004) [#11]
The Blitz LightRange() command just modifies the attenuation parameters. The range parameter of a DirectX light is something else. It looks like Blitz by default sets the range parameter to the C++ #defined 'FLT_MAX' i.e. the maximum value that a float can hold. Thats why the lighting is calculated pretty much to infinity. The range parameter of a DirectX light overrules any attenuation curve falloff and all vertices beyond the range receive no lighting calculations at all.If you set the range to a value where the lighting is still visible you get a sharp cutoff of lighting i.e. a black edge around the light.

Here's the DX9 struct for a hardware light.

It has more parameters than the DX7 version but you can see the separate attenuation and range parameters.

typedef struct _D3DLIGHT9 {
D3DLIGHTTYPE Type;
D3DCOLORVALUE Diffuse;
D3DCOLORVALUE Specular;
D3DCOLORVALUE Ambient;
D3DVECTOR Position;
D3DVECTOR Direction;
float Range;
float Falloff;
float Attenuation0;
float Attenuation1;
float Attenuation2;
float Theta;
float Phi;
} D3DLIGHT9;

Members

Type
Type of the light source. This value is one of the members of the D3DLIGHTTYPE enumerated type.
Diffuse
Diffuse color emitted by the light. This member is a D3DCOLORVALUE structure.
Specular
Specular color emitted by the light. This member is a D3DCOLORVALUE structure.
Ambient
Ambient color emitted by the light. This member is a D3DCOLORVALUE structure.
Position
Position of the light in world space, specified by a D3DVECTOR structure. This member has no meaning for directional lights and is ignored in that case.
Direction
Direction that the light is pointing in world space, specified by a D3DVECTOR structure. This member has meaning only for directional and spotlights. This vector need not be normalized, but it should have a nonzero length.
Range
Distance beyond which the light has no effect. The maximum allowable value for this member is the square root of FLT_MAX. This member does not affect directional lights.
Falloff
Decrease in illumination between a spotlight's inner cone (the angle specified by Theta) and the outer edge of the outer cone (the angle specified by Phi).
The effect of falloff on the lighting is subtle. Furthermore, a small performance penalty is incurred by shaping the falloff curve. For these reasons, most developers set this value to 1.0.

Attenuation0
Value specifying how the light intensity changes over distance. Attenuation values are ignored for directional lights. This member represents an attenuation constant. For information about attenuation, see Light Position, Range, and Attenuation. Valid values for this member range from 0.0 to infinity. For non-directional lights, all three attenuation values should not be set to 0.0 at the same time.
Attenuation1
Value specifying how the light intensity changes over distance. Attenuation values are ignored for directional lights. This member represents an attenuation constant. For information about attenuation, see Light Position, Range, and Attenuation. Valid values for this member range from 0.0 to infinity. For non-directional lights, all three attenuation values should not be set to 0.0 at the same time.
Attenuation2
Value specifying how the light intensity changes over distance. Attenuation values are ignored for directional lights. This member represents an attenuation constant. For information about attenuation, see Light Position, Range, and Attenuation. Valid values for this member range from 0.0 to infinity. For non-directional lights, all three attenuation values should not be set to 0.0 at the same time.
Theta
Angle, in radians, of a spotlight's inner conethat is, the fully illuminated spotlight cone. This value must be in the range from 0 through the value specified by Phi.
Phi
Angle, in radians, defining the outer edge of the spotlight's outer cone. Points outside this cone are not lit by the spotlight. This value must be between 0 and pi.

[Edit] OpenGL does not appear to have the equivalent.


sswift(Posted 2004) [#12]
I actually read about this technique a week or so ago, but I thought Blitz must already have it implemented and I just didn't notice in my tests because I tested on a single floor grid once a long time ago.

Are you SURE it's not the case in Blitz, Halo?

I don't see what you mean about it making everything look better, because even if the 8 closest lights to an obejct were used, that would still not help much with lighting a level, where one room is bound to be only a few objects. In fact, it might be worse than whattever method Blitz is using if it is not already doing this. Imagine if a user made one big level mesh, and Blitz picked the 8 closest lights to the center of the level instead of the 8 closest to the camera... If a user stuck a lot of lights in the level they might not see lights anywhere until they got near the middle of the level.

I know this is not the optimal way to light a level, or build a level, but Mark can't assume all his users will do stuff in the more efficient manner.


Paolo(Posted 2004) [#13]
Talking about DX lights...

Why a lightmapped level can not be affected by a DX light?
I have my levels with lighmaps but when I need a dynamic lighting effect I have to disable it.
Actually it could look amazing if I could combine both techniques...

Any suggestion...

Paolo.


MadJack(Posted 2004) [#14]
Because your level mesh/es are probably set to Entityfx 1 - fullbright.


Mustang(Posted 2004) [#15]
Paolo, lightmaps are precalculated lighting and adding real lights on top of it will screw the effect. So with lightmaps you can't light the geometry without it looking funny (unless you can burn away lightmaps etc but that's not what we are talking about here).


Ruz(Posted 2004) [#16]
I have my level mesh textures set to multiplyX2 and the dx lighting makes the lighting then looks really good. I Am using 3 dx lights and there is no slowdown.
combined with That i am manually altering vertex colours in max.


Rob Farley(Posted 2004) [#17]
Maybe I didn't explain myself well...

What my demo did was take the 2 lights closest to the player (from a light mapped level) plonk a DX light at those 2 positions, colour the DX lights so they were the same colour and darken them based on their distance from the player.

It worked really well...

Drawbacks:
Everything was based around the player rather than anything else in the game.
I wasn't checking if lights were behind walls so you would sometimes be lit from lights on the other side of walls.

Other than that it looks cool.


Rob(Posted 2004) [#18]
Spotlights are the preferred method I think. They have a basic directional setup with falloff.


sswift(Posted 2004) [#19]
"Paolo, lightmaps are precalculated lighting and adding real lights on top of it will screw the effect. So with lightmaps you can't light the geometry without it looking funny (unless you can burn away lightmaps etc but that's not what we are talking about here)."

If your lightmap is a seperate mesh or surface from your textures then you can light those instead of the textures to burn them away...

And... MAYBE with a single surface with textures and lightmaps you CAN also achieve lightmaps + lights.

Here's what I'm thinking.

First you have the lit model. Normally, you set this model to fullbright, so this first color pass is always white. Then in the second texture channel you have the textures set to multiply, and then the lightmap, also set to multiply. This gives the result:

(((Polygon color) * texture color) * lightmap color)

But what if instead you set it up so that the first texture layer was the lightmap, set that to add, and made the object affected by light?

Then you would have:
(((polygon color) + lightmap color) * texture color)

Now, because the object is affected by light it's color would be black unless a light source is in the area. The result is that the lightmap would be added to a black object, unless a light source is in the area in which case it would be added to a lighter color, thus lightening parts of the lightmap which are less than fullbright. And then when you multiply the texture with it, set to multiply x2 if you so desire, you'll get a world which is both lit by DX lights and by lightmaps, all in a single pass.


JoshK(Posted 2004) [#20]
This would be useful for lighting entities, which is what vertex lights are for, in my opinion. It wouldn't help much with vertex-lighting a map or large mesh, but that's what lightmaps are for.

I wonder if it's possible to find and modify the DX structure, although it would be a million times easier for Mark to do.

Additionally, an entityvisible() command should be used internally to check if the entity is lit by the light.