BloodRayne@Posted: Wed Dec 29, 2004 12:26 pm :
Many, many people... even experienced modders and mapmakers continually support the urban legend that the Doom3 engine cannot handle ambient lighting at all. I read daily from people that an area that's not lit in the D3 engine is by definition pitchblack. I hear claims that well-lit area's will create considerable loss of FPS and that the only way to make ambient light is by either using the flawed ambient lights or cheap tricks such as hud-shaders.

I've been taking some time to come up with a fix for these limitations in the Doom3 engine and I think I have found one. Before I continue with this tutorial --which is far more simple than you might expect-- I have to establish some things first though.

Some people say that any ambient light setup is a 'just a trick' to make the D3 engine show ambient lights and that this is not 'true ambient lighting', regardless of how it looks! In my opinion such arguments are inherantly flawed because all game-engines out there today are basically a big bunch of tricks pulled together to create a virtual world for a player to behold.

Anything from lighting to water to particles are just basic approximations and systems of simple hacks put together to create illusions. We, modders, are in the art of making illusions, we are in effect illusionists. Using hacks and tricks we try to create a virtual world which is believable within it's setting.

A good example of this is particle systems. A well set up particle can create the illusion of smoke, fire or water. A particle system doesn't even come close to real world water, fire or smoke. Technically, a particle system is just a bunch of sprites that move from point a to point b using mathematical predetermined paths. The technique of transparent sprites goes back to the commodore 64 and the first gaming consoles and has in essence not changed since.

Lightmapping as we know it from the older type of engines such as the quake3 engine is basically a method of adjusting the brightness of textures at places which have been predefined by the author of a map. Using the several available shader stages in D3 materials, it's relatively easy to use that same method in making ambient light work in the D3 engine.

Taking that into account, anything you see in any game engine is basically a trick to create the illusion of something. What does this mean? This means that as long as you get a desired effect, the means you used to get that effect aren't important at all. It just doesn't matter as long as you get the desired effect.

On to the tutorial
As a mapper, at this point you will propably be familiour with the material and shader system which is utilized in the D3 engine. Brian from ID software has taken the time to write a complete shader and material reference on the iddevnet website. At this point I would like you to go over there and take a look at his material and shader documentation if you have not done so in the past to get aquinted on how to make shaders and materials.

Let's take a look at a basic material setup for a common texture in Doom3, called textures/hell/cbrick2. You can find this material shader in the hell.mtr file in pak0001.pk4 in your base directory.

(Find this file now and unzip it into your mod directory or base directory, because we will be making changes to this file.)

This is what the code for this shader looks like:
Code:
textures/hell/cbrick2
{
   qer_editorimage      textures/hell/cbrick2.tga
   stone
   diffusemap      textures/hell/cbrick2.tga
   bumpmap          addnormals( textures/hell/cbrick2_local.tga, heightmap( textures/hell/breakage1_h.tga, 3 ) )
   specularmap       textures/hell/cbrick2_s.tga
}


As you can see there are several stages in this material. A diffusemap, specularmap and a bumpmap stage. For this technique we will be adding a new stage later in this tutorial.

First, let's take a look at how this texture looks in game. I will make 2 screenshots, one with the lights on, and one with all lights removed from the map using the 'clearLights' console command.

This first shot has 3 lights which are active. The most important one is a huge light intended to light up the general area. Allthough this gives some effect if I were to turn the shadows on for this light (shadows are off for it at the moment) this would certainly impact the FPS a lot. As you can see there are several pitch black places in this screen (the wall to the left). This is the way that most mappers try to set up ambient lighting and this is where their frustration comes from in lighting scenes. Not only that, the scene is still pretty dark.
Image

In this next shot I have used the 'clearLights' console command, which deletes all the lights from a scene. As you can see, everything is now pitch black. This shows that any unlighted surface in the engine is pitch black.
Image

Ok. Now that we know what the problems are, on to the fix!
For my fix, I tend to keep the over all sky light on. This means that I have one huge light set up that will create some light for me. I've done this so that playermodels and enemies and other models will light up accordingly.

Let's take a look now at another command called the 'reloadSurface' console command. It's best to bind some key to this command at this point of the tutorial because you will be using this command a lot.

Let's go back to the material files and the several stages that we can use in these files. We will be adding a new stage to every shader, in every part of your map that you will want to have ambient lighting for. This stage will be a special stage that uses the additive mode to add some ambient light to all of the shaders that I will use in my map.

I use the textures/hell/cbrick2 texture a lot in my map so I will take this material as an example but the principle remains the same for any other shader that you will use for your maps. The first thing I did was to start up the game, open my map and walk in to a wall that uses this shader. Then I used the reloadSurface console command to see which texture it was. Guess which one it was? You guessed right, textures/hell/cbrick2 ;)

Then, after knowing which shader would need modification I open up hell.mtr in notepad and find the shader. I then add this new special stage to the code. This stage uses the diffusemap of the current shader that I'm working on. In this case textures/hell/cbrick2.tga.

Code:
textures/hell/cbrick2
{
   qer_editorimage      textures/hell/cbrick2.tga
   stone

   diffusemap      textures/hell/cbrick2.tga
   bumpmap          addnormals( textures/hell/cbrick2_local.tga, heightmap( textures/hell/breakage1_h.tga, 3 ) )
   specularmap       textures/hell/cbrick2_s.tga
   {
             blend   add //gl_one, gl_dst_alpha
      map   textures/hell/cbrick2.tga
      rgb 0.3
   }
}

Notice that the 'blend add' and the 'rgb' are the most important things here. These are the means by which you can control your ambient light. The RGB value goes from 0 to 1. 0. Zero (0) meaning that no extra lighting is used at all and one (1) meaning that this texture is 'full bright'.

For this particular map I have decided that an rgb value of 0.3 looks best. But for say, urban settings or the Doom 3 can do it too project, you could easilly use values up to 0.5 or even 1... depending on how light you want your map to be. For the broad daylight setup at the end of this tutorial I use a value of 0.9!

Now at this point I imagine that I am a monk and I go through all the textures of my map and add this stage to each of them that I want to have ambient lighting. In my case there were only 10 textures so this was a total of not even 3 minutes work. I would say that's still quicker than waiting 1 to 2 hours for lightmaps to be calculated... but hey that's just me. :wink:

After doing so, I came with this result. I will again make two screenshots, one with lights and one without so that you can see that this ambient light is truly ambient and does not rely upon any lights whatsover. This is the huge advantage of this method. It will not kill your FPS.

Notice that there is not one completely dark area left now in these screenshots, eventhough the second one has no lights whatsoever.

Image

All that's left in this shot is the ambient, after using the clearLights command. I've not implemented the fix on the mapobjects in this map so you can see the difference between the ambient lighted textures and the ones that weren't.
Image

Now, since the map I've used for this is set during the night, you may think I'm cheating.. so. I set up an daylight scene (broad daylight) with the same technique for you. The first screenshot utilizes ONE light that casts full shadows. I've used the clearLights console command again to show just the ambient in the second screenshot. You can see the ambient effect particularly well on the ground. The first two shots are WITH the effect, the second two shots are WITHOUT this effect. In these last shots of this effect I have used a really high ambient setting of 0.9, I did this just to show you how far you can go in making the dark spots of your map look brighter. With just a little bit of tweaking you can get some amazing ambient light effects in game.

I can assure you that the next shot only uses ONE light. If you don't see a world of difference between these shots then you need glasses! :P

With the ambient effect:
Image

Without the ambient effect:
Image

clearLights With ambient effect. Remember, there are NO lights in this map at all:
Image

clearLights without ambient effect:
Image

Conclusion:
It's no hard leap to go on to claim this is not a cost effective method of setting up ambient lights for mapping in terms of time but to be honest, once you have a basic setup done and once you've made the textures you want to use in other maps you are done. You only have to give your textures this special stage once.

When I get time I will continue on this method and show you how you can change the ambient light setup realtime by the use of shaderparms in scripts. This is the method that we will be using for the Hexen:Edge Of Chaos mod. Each level will have a simple setting during scriptload that will tell all shaders which ambient lightlevel to use.

If you still feel that this method is too timeconsuming then I suggest you try the following: Load up any lightmapped engine such as the Source engine, make your map, place your lights, compile it, wait for two hours for it to finish. Find out there are glitches, replace the lights, adjust some settings, wait another 2 hours for the compiling to finish and repeat that process for 4-8 times before you can call your map 'finished', then come back here and we'll have a discussion about timeconsuming mapping methods. :P:P



MortalWombat@Posted: Wed Dec 29, 2004 2:47 pm :
You, sir, are a saint! Great stuff! Thanks



xwarpedx@Posted: Wed Dec 29, 2004 3:21 pm :
you have an interesting approach to the problem although i have to disagree on the manner of adding ambient lighting. with your method a texture will have the same ambient color no matter where it is on a map, which isnt always true. obviously your method works tho so its just a matter of preference whether to apply a global ambient or add it where its needed. the two of us should bump heads some time and hammer out this problem :)



pbmax@Posted: Wed Dec 29, 2004 3:28 pm :
great job, blood. awesome mapping skills...



BloodRayne@Posted: Wed Dec 29, 2004 3:40 pm :
xwarpedx wrote:
you have an interesting approach to the problem although i have to disagree on the manner of adding ambient lighting. with your method a texture will have the same ambient color no matter where it is on a map, which isnt always true. obviously your method works tho so its just a matter of preference whether to apply a global ambient or add it where its needed. the two of us should bump heads some time and hammer out this problem :)


For that typical hexen feeling I prefer a global illumination but you can use this technique very locally as well. You can easilly create different versions of the same texture and color the light according to it's surroundings using the RGB values. RGB is just a shortcut for Red, Green and Blue. So the code for a blueish tint would look like this:

Code:
{
   blend    add
   map textures/hell/cbrick2.tga
   red 0.1
   green 0.1
   blue 0.5
}


You can play with the color values to create different tints in different parts of your map.



xwarpedx@Posted: Wed Dec 29, 2004 4:06 pm :
true, true. i just think its alot of work going through and re-scripting your textures especially if you have alot of them. either way great work man

(i am in no way trying to put your method down just trying to get a good discussion going as it usually results in great ideas :D )



BloodRayne@Posted: Wed Dec 29, 2004 4:11 pm :
xwarpedx wrote:
true, true. i just think its alot of work going through and re-scripting your textures especially if you have alot of them. either way great work man

(i am in no way trying to put your method down just trying to get a good discussion going as it usually results in great ideas :D )


I know, but mapping allways is a lot of work. I just took an hour to optimize one of the maps for the Hexen mod with this method and I have to say that the amount of control that I have over the ambient lighting in this way is really helpfull. And we will be able to use the setup in all maps now that the groundwork is done.



T0r1t0@Posted: Wed Dec 29, 2004 4:22 pm :
How would you apply this technique to emulate the everchanging luminosity of ambient lighting in a day simmulation, from sunrise to sunset? I use ambient lights and the crossFadeEnt function to change the color and luminosity according to the time of the day. Unfortunately, as you say fps drop a lot.



BloodRayne@Posted: Wed Dec 29, 2004 4:40 pm :
T0r1t0 wrote:
How would you apply this technique to emulate the everchanging luminosity of ambient lighting in a day simmulation, from sunrise to sunset? I use ambient lights and the crossFadeEnt function to change the color and luminosity according to the time of the day. Unfortunately, as you say fps drop a lot.

By using shaderparms and scripts. You can assign a shaderparm to change the rgb values. An example would be:

Code:
textures/hell/cbrick2
{
   qer_editorimage      textures/hell/cbrick2.tga
   stone

   diffusemap      textures/hell/cbrick2.tga
   bumpmap          addnormals( textures/hell/cbrick2_local.tga, heightmap( textures/hell/breakage1_h.tga, 3 ) )
   specularmap       textures/hell/cbrick2_s.tga
   {
             blend   add //gl_one, gl_dst_alpha
      map   textures/hell/cbrick2.tga
      rgb Parm4
   }
}


Then by script you can change the Parm4 value. I am implementing this for our maps and I will show in part two of this tutorial how to implement this precisely because it's taking some figuring out. :wink:



Tequila Joe@Posted: Wed Dec 29, 2004 5:51 pm :
Question: Since you are changing the textures themselves and not used any lights for the ambient effect, what will be the result once monsters are added?

Like in the daylight scene, I would assume monsters in that room would have very black shadows when facing away from the light, whild the bricks would be ambient lit? Would code need to be added to the monsters skins too?



BloodRayne@Posted: Wed Dec 29, 2004 6:20 pm :
Tequila Joe wrote:
Question: Since you are changing the textures themselves and not used any lights for the ambient effect, what will be the result once monsters are added?

Like in the daylight scene, I would assume monsters in that room would have very black shadows when facing away from the light, whild the bricks would be ambient lit? Would code need to be added to the monsters skins too?


This method also makes use of one light setup for lighting general models. As for their shadows, this method is basically the exact same as lightmapping, instead of letting a program lightmap the textures for us, we do it by hand. It is possible to add a shader stage to the monsters, but certainly not necessary.

The only thing that this ambient light method does is make those area's which are normally pitch black have some ambient light. You can use this method to light up everything by lighting up all shaders as well as monstershaders and I don't doubt that somebody will make a mod as such. But in this instance, where we use it for ours we will only use it to light up the pitch black area's of the map without the need of extra lights which would screw up the FPS for the maps.

This method should be used for finalizing the lighting of a map.

There's two ways for using this method:
1) The first part is make a light setup as you normally would in Doom3 and optimizing them as much as possible. After that you go around the map and light up those textures which need to have ambient lighting. With this method you would need to remake the material shaders (copy and pasting them from the original) and then apply them where nescesarry. This is the most advanced way of using this method that gives the best results. In effect this method is a way of manually mapping the ambient light where it's needed.

2) You give all the textures a basic ambient light and then set up the normal lighting as usual afterwards. This is the method we use in EOC. This will make sure that ALL textures in a map will be visible and that there will be no pitchblack textures, unless we assign them to special places such as abysses or dungeons.


The second method works the exact other way around from the first method. There we map the light, with this method we map the darkness back where nescesarry. :wink:



hellstorm27@Posted: Wed Dec 29, 2004 6:56 pm :
Thanks for the tutorial- it will come in particularly handy now that I have upgraded to a Geforce 6600, which causes Doom 3 to display rather different (and harder to manufacture correctly) lighting to what my old Ti 4200 did. In particular, those "black areas" where the texture appears black seem to be more of an issue with a higher-end card.



T0r1t0@Posted: Wed Dec 29, 2004 7:18 pm :
Quote:
Then by script you can change the Parm4 value. I am implementing this for our maps and I will show in part two of this tutorial how to implement this precisely because it's taking some figuring out.


Yes, I did exactly this for the skybox shader. I didn't realize this could be applied to textures as well. It's a bit tricky to syncronize it with the sunlight crossFadeEnt loop, though



Sebultura@Posted: Wed Dec 29, 2004 7:50 pm :
Thank you man for this tutorial, I'm waiting closely for your mod, anyway I know this'll be great !



Tequila Joe@Posted: Wed Dec 29, 2004 8:11 pm :
Thanks BloodRayne, that clears things up. :)

I've noticed that monster shadows can get pretty dark, so that would be good for brightening shadows up without blowing out the monsters.



idiom@Posted: Wed Dec 29, 2004 11:33 pm :
What an ingenious trick you thought up there Bloodrayne :D I was gonna mention a couple of improvements but you seem to have covered them all now. This thread should be a sticky.



djester@Posted: Thu Dec 30, 2004 4:47 am :
how can none of you see how washed out everything becomes. Most of those "black walls" you see despite having large lights are in fact being lit, just not very well. Basically you've created a material file that not only adds an extra pass, but jack's up the brightness on people's monitors for them! I apologize for the demeanor but this is ridiculous, you've reduced the engine worse than Quake 1 lighting.



xwarpedx@Posted: Thu Dec 30, 2004 5:00 am :
djester wrote:
how can none of you see how washed out everything becomes. Most of those "black walls" you see despite having large lights are in fact being lit, just not very well. Basically you've created a material file that not only adds an extra pass, but jack's up the brightness on people's monitors for them! I apologize for the demeanor but this is ridiculous, you've reduced the engine worse than Quake 1 lighting.


i suggest you aquaint yourself with some lightning knowledge before you go slinging that kinda response around...you obviously have no idea what were trying to accomplish nor do you care to take the time to learn some more about the subject before you throw yourself into a conversation with us highly experienced folks.



DaJuice@Posted: Thu Dec 30, 2004 6:53 am :
When you turn up the ambient on a surface that has no real lights hitting it, it will appear as if the surface has no lighting model (as in phong, gourad, etc). If you look at the second to last screenshot you can clearly see this because there is virtually no visual distinction in the area where the roof and pillars connect (or anywhere else) because, yes you guessed it, there is no actual lighting! This method is just bringing out the rgb values of the textures, but without any real lights to show the volume and shape of objects "lit" in this fashion, it will look like poo.

And OMG at that "us highly experienced folks" comment. Some of the shit on this forum just blows my mind.



rich_is_bored@Posted: Thu Dec 30, 2004 6:56 am :
I'll admit he's being frank and some of you may not like what he has to say but he does know what he's talking about.

I noticed the problem with this method when I read this thread earlier today. I should have said something then.

All that's being done here is an additive blend to compensate for the lack of lighting.

You're taking a range of 256 values per channel and squeezing them into a smaller space by adding to them. This would cause a loss of image data because while low color values like 0 0 0 can be added to, values like 255 255 255 cannot.

I may not make any friends saying this but don't be so quick to praise a suggestion or method as gospel. Be critical of people's work, including my own, and look for flaws.

Like it or not, this is a workaround not a solution. No offense BloodRayne, the screens still look very nice.