svenskmand {l Wrote}:Your assumption about our graphics goal sounds about right.
Thank you for your reply
. I will continue illustrating the lighting system, feel free to tell me your thoughts once you have read it (incompatibilities you foresee with your engine, if your confused, additional features, ideas, etc etc).
So, you could have four "priorities" or "types" of lights (1, 2, 4, 8). 1 refers to lights that are static and used to pre-calculate the lighting via lightmaps. 2 being lights used for real-time bumpmapping. 4 for bloom/hdr. 8 for scattering. Do note how I am referring to them via powers of two, so this can be implemented using bitflags (so a single light can have multiple priorities).
Here are the best combinations:
1 & 4 would be a good combination. The pre-calculated lighting adds their effects to the terrain, while the bloom makes them seem real as they shine around objects (consider a static light that doesn't move, and whose intensity and color remains constant, but it looks more real because it shines around objects as if illuminating them).
2 & 8 is also a good combination (consider a spotlight that moves back-and-forth, shining its rays around walls and casting a spotlight). Use it sparingly (on special lights) because its expensive (max of 4 lights per-camera view).
4 works well by itself for large numbers of lights, especially when the lights must be dynamic (ex: headlights on a car, or a torch carried by a goblin).
If performance is an issue on some machines, add options to disable any and/or all of the lights by their priority. If a player disables scattering, you can still render 2 & 8 lights with just bumpmapping. If the player disables bloom, you can still render 1 & 4 lights with just pre-calculated lighting. Or visa-versa, etc.
Building on top of the previous pseudo-code, here is some more pseudo-code to render bump-mapped objects in a dynamically lit world with unlimited lights:
- {l Code}: {l Select All Code}
class Light; // wrapper to the OpenGL lighting system (use the Ogre equivalent).
class Shader; // wrapper to the OpenGL GLSL extension (use the Ogre equivalent).
class Objects; // an object to be rendered (use the Ogre equivalent).
#define MAX_LIGHTS 8 // the maximum number of lights supported by the minimum target hardware
Shader * bumpMappingShaders[MAX_LIGHTS + 1]; // contains a GLSL shader for each lighting scenario (bumpMappingShaders[0] implements 0-light bumpmapping, bumpMappingShaders[1] implements 1-light bumpmapping, bumpMappingShaders[2] implements 2-light bumpmapping, etc). aka: its a performance hack.
enum lightFlags {
lf_NONE = 0x00000000, // this light does nothing
lf_PRECALC = 0x00000001, // this light is pre-calculated
lf_BUMPMAPPING = 0x00000002, // this light uses real-time bumpmapping
lf_BLOOM = 0x00000004, // this light is rendered into the Bloom FBO.
lf_SCATTER = 0x00000008, // this light is rendered into the Scatter FBO.
// additional types of lights go here
};
list<Light *> sceneLights; // contains all the lights in the scene
list<Objects *> sceneObjects; // contains all the objects in the scene
// loop through each object to render
foreach(sceneObjects, currentSceneObject) {
Light * lights[MAX_LIGHTS]; // will contain the highest priority real-time lights affecting this object
u32 index = 0;
// loop through the lights and find the highest priority ones (please accelerate this using a octree!)
foreach(sceneLights, currentSceneLight) {
// if this light is affecting this object
if(distance(currentSceneLight, currentSceneObject) <= currentSceneLight->radius) {
// if an extra light slot is open
if(index < MAX_LIGHTS) {
lights[index++] = currentSceneLight;
} else {
// find the lowest priotity light currently affecting this object
Light * lowestPriority = NULL;
u32 clindex = 0;
foreach(lights, currentLight) {
if(lowestPriority) {
lowestPriority = currentLight;
clindex = currentLightIndex;
} else {
// if the current light is lower priority
if((currentLight->intensity / distance(currentLight, currentSceneObject)) < (lowestPriority->intensity / distance(lowestPriority, currentSceneObject))) {
lowestPriority = currentLight;
clindex = currentLightIndex;
}
}
}
// see if this light is higher priority than the lowest priority light currently affecting this object
// if so, then replace the lower priority with the higher
if((lowestPriority->intensity / distance(lowestPriority, currentSceneObject)) < (currentSceneLight->intensity / distance(currentSceneLight, currentSceneObject))) {
lights[clindex] = currentSceneLight;
}
}
}
}
// enable the appropriate shader (relative to how many lights is needed)
Shader * cShader = bumpMappingShaders[index];
cShader->enable();
cShader->uniformTexture("texture_0", currentSceneObject->textureSlot0); // diffuse map
cShader->uniformTexture("texture_1", currentSceneObject->textureSlot1); // normal map
cShader->uniformTexture("texture_2", currentSceneObject->textureSlot2); // specular map
cShader->uniformTexture("texture_3", currentSceneObject->textureSlot3); // ambient occlusion map
// bind the textures
currentSceneObject->textureSlot0->bind(0);
currentSceneObject->textureSlot1->bind(1);
currentSceneObject->textureSlot2->bind(2);
currentSceneObject->textureSlot3->bind(3);
// enable the lights
// foreach(item, index, max)
foreach(lights, clindex, index) {
currentLight->enable(clindex);
}
// render the object (verts + tcoords + normals + tangents)
currentSceneObject->render();
foreach(lights, clindex, index) {
currentLight->disable(clindex);
}
currentSceneObject->textureSlot3->unbind(3);
currentSceneObject->textureSlot2->unbind(2);
currentSceneObject->textureSlot1->unbind(1);
currentSceneObject->textureSlot0->unbind(0);
cShader->disable();
}
As you can see it is extremely simple, and you should be able to just plop it into Ogre's rendering system (+ the needed GLSL shaders). Took me about 30 minutes to write it, I would estimate an hour or two to implement the bumpmapping shaders (charlie: how am I doing on time so far? --said in a friendly, teasing manner
).
.