D2X-XL Worklog
Notes on features and problems from the development of D2X-XL, newest first.
Lighting and Color |
|
Lights The old renderer picked a handful of lights for every wall face. Now every segment of a level keeps its own list of the lights that reach it, and every pixel of a wall, a robot or a ship in that segment is lit by the lights on that list. The lamps of a level that never change are static lights. They are ranked by how much light they give to a segment, and the strongest ones make it on the list. Their light is baked into lightmaps, so it costs next to nothing while you play. Lamps that flicker, can be shot out or are switched by triggers stay live, and they really go dark. Then there are the lights that come and go: shots, flares, explosions. Some places on every list are reserved for them, so a room full of lamps still has room for the glow of your lasers. If more of them show up than there are places, the brightest ones win. A light only gets on the list of a segment it can be seen from, so light does not shine through walls. And lamps shine as the rectangles they are, not as points. Light Portals Something Descent never could do is let light fall through a door. The lightmaps of a level are computed with all doors shut, so the light of a room stays in that room, even when its door opens. D2X-XL now has light portals. For every wall that can open, the game keeps track of how brightly each of its two sides is lit: by the baked light of the lamps around, and by whatever happens there while you play, like a flare being fired or a lamp being shot out. What falls on the back of a door comes out at its front, in proportion to how far the door is open. So an opening door starts to glow like a window. The light of the room behind it falls through the opening and lights the floor, the walls, the robots and your ship in front of it. It has the color of the light behind the door, it gets brighter as the door slides open and fades out again when the door closes. The opening is a window and not a lamp: the light gets weaker with distance as if the lamps of the room themselves were shining through the door, and it only falls into the space in front of the opening. The solution is quite flexible. It works for doors, for destructible and cloaking walls and for walls that are opened or removed by triggers; a door with holes in its texture lets some light through even when it is shut. Light arriving at a second door is handed on, so it travels through a row of open doors. And it follows what happens behind the door: fire a flare in the room, and the light in the corridor changes with it. GPU Lightmap Bake D2X-XL used to compute its lightmaps on the CPU, which took hours at high and highest quality, even for levels of average size. And every face got a lightmap with the same number of pixels, no matter how large it was, so the detail of the lighting was completely irregular: fine on small faces, coarse on large ones. Now the graphics card paints the light of every static lamp into light textures for the walls, lamp by lamp, using the same formula the game uses for live lights. Four things are stored:
The pixels of the lightmaps now have the same size everywhere in a level, so the detail of the lighting is even, and the result looks very good even at medium quality. Parts of the level no light reaches take up no space. The bake runs when a level is loaded for the first time; after that its result comes from a cache. What is not baked is the gloss. A highlight is the lamp mirrored in a surface, and it moves when you move, so it cannot be stored in a texture. The highlights of static lamps are therefore computed while you play. To keep this cheap, the game skips the work wherever no highlight can show: when the lamp is too far away, when the spot lies behind the lamp or the lamp behind the surface, or when the highlight would be too faint to notice from where you are looking. Hardware Raytracing Where the graphics card can trace rays, D2X-XL uses this to answer one simple question: is anything between these two points? It is enabled by setting lighting quality to ultra high in the render options, which is only offered on hardware that supports it. The lightmap bake benefits most. Without rays it has to render the surroundings of a lamp from every point on the lamp it checks; with rays a single pass per lamp does the job, which speeds the bake up enormously. Rays are also used for the shadows of ships and robots, and they let live lamps and big explosions cast shadows of walls, doors and objects onto the level; a door stops casting its shadow the moment it opens. Shadows D2X-XL used to render the shadows of ships and robots with shadow volumes: the shadow was a volume pushed away from the light, and everything inside it got darkened. That volume didn't care about walls, so shadows were stamped right through them into the room next door. This never really worked out for a scene like a Descent level. With hardware raytracing, shadows are now traced: for every pixel, a ray to the lamp tells whether the object is in the way. Without it, or when it is switched off, shadow maps are used: the object is drawn from the lamp's point of view into a small depth picture, and every pixel on the screen is compared against that. Either way a shadow ends at the first wall, fades with the distance from the lamp and has soft edges. A Descent level often has a lot of lamps, and if each of them casts a shadow of every object around, the result is a clutter of shadows that is more irritating than anything else. So I chose a subtle solution: an object only casts shadows from the two lamps that affect it most, and the shadows are pale. When a third lamp gets more influence on an object than one of those two, it casts a shadow as well, which grows stronger while it replaces the weaker lamp, whose shadow fades away. Shots and flares cast no shadows, or every shadow in a room would swing around with each shot. Only the really big explosions do. Volumetric Halos The halos around lamps used to be flat sprites that were placed on the lamp and tilted towards the viewer. A halo is now a cushion of glowing haze standing in front of the lamp and reaching a little into the room. For every pixel the game works out how much of that cushion your line of sight passes through: a look straight through its thick middle is bright, a graze at its rim is faint. The halo has the color of the lamp's light, it is cut off by anything standing in front of it, and since it is a real volume it looks right from the side, where the lamp itself can no longer be seen. Only lamps that are lit have a halo; a lamp that has been shot out or switched off loses it. |
![]() |
|
Color Pipeline D2X-XL supports two color pipelines. In sRGB mode, the colors of textures are decoded into amounts of light first, all lighting is computed with these, and the picture is encoded again at the very end. In linear mode, texture colors are used the way they are stored, as D2X-XL has always done it. The same lights add up differently in the two modes, so everything that had been tuned by eye exists twice: the strength of lamps and ambient light as well as that of halos, shields, sprites and lightning. Lightmaps are baked and cached separately for each mode, too. The question was which of the two suits the game better. In the end it was a design decision, and it went to linear; sRGB can still be selected with the command line switch -colorspace. |



