We’re here to work on shaders, but we can’t very well just start writing code without blathering on for a thousands words about random things. I mean, I can’t. How you program is your business.
I’ve written all this before. In Project Octant I made a block world.
I will say that to a certain extent “code re-use” is overrated. Not to say that it’s not incredibly important. It’s just not the end-all-be-all of software engineering. Sure, it’s nice to be able to make use of code you wrote in the past. But it’s also really nice to be able to write a more perfect version of something. Every time you attack a problem you understand it a little better, and every solution is just a little lighter and cleaner than the last. Yes, you can gradually improve something you’ve already written, but there’s nothing like a re-write to let you really fix structural problems.
I’ve discovered that re-writes aren’t nearly as expensive as they seem. They’re mentally daunting and often boring because it feels like busywork, but if you wrote the previous code and you’ve still got the structure rolling around inside your noggin, then re-writing isn’t that much worse than re-typing.
Of course, all of this is only true to a point. If I was working on some behemoth like the Crysis engine I wouldn’t be able to get away with re-writing. The job is too big, too interconnected, and it’s so complicated I’d never have the whole thing in my head at once anyway.
Still, I have to say that re-writes have a lot of good points. This new version of the cube-based world is shorter, cleaner, and clearer than my last attempt. And I’d be lying if I said that didn’t feel good.I guess re-writes are kind of decadent: The work of someone who is able to polish their code instead of being obliged to ship something useful.
A Brief History of Shadows
Let’s talk about why I’m doing this.
Back in Ye Olden Thymes, when buxom barmaids served flaggons of ale to world-weary pilgrims as they traveled from kingdom to kingdom, we had games with really terrible graphics. I mean, they didn’t even have shadows. It was a stark, ugly existence where everything was out of scale and the pixels were so big you couldn’t tell the giblets from the bullets.
I’ll be using Minecraft here to demonstrate what I’m talking about. Just to be clear, all of this is photoshopped. This is easier than digging up and playing all the old games and finding screenshots to illustrate what I’m talking aboutAnd then I resort to that later anyways..
The problem with not having shadows is that everything seems kind of floaty and it’s difficult to judge scale. Eventually we discovered that if you draw a simple black circle under a character it roughly approximates having a shadow.
By putting a shadow directly under his feet we can suddenly see that the little guy is actually slightly off the ground in mid-jump, and not larger and further in the distance, which is what your eye might assume. This trick seemed to work well, even when the light source wasn’t directly overhead. Our eyes are just used to the idea that we’ve always got shadows around the base of things. Tables, chairs, people’s legs: It’s usually darker underneath stuff. I mean, duh.
But the little blob shadow has its drawbacks. It’s kind of cartoony and you’re missing out on some really interesting gameplay. (Shadows falling across doorways in a stealth game is a good example.)
There were a lot of ways to half-ass this. Here is one of many:
Between frames you take something that should be casting shadows and draw it into a texture. Draw it from the point of view of the light, pure black.
Then stick it on a panel, and position the panel so that it’s projected away from the light.
The bad things about this system:
- How many little textures do you need to draw? You need
number of lights×
number of thingsto cast shadows. That scales poorly, particularly when it means a lot of fiddling around to get those panels positioned properly.
- The larger the shadow, the more texture memory you need. This sucks, because there’s no upper limit on how long a shadow can be. If we’re casting a shadow at an extreme angleLike at sunset, for example., then we can either devour tons of memory, chop off the shadow at some arbitrary cutoff point, or lower the resolution of the texture, making the edges more pixelated.
- No matter how much texture memory you use, the edges of the shadow are always a little ugly
- This technique is really only useful for stand-alone objects. You would get very poor results (bordering on disastrous) if you tried to use it to have the interior walls of a building cast shadows.
- The shadows “stack”. So if I’m standing in the shadow of a large object, I will cast an even darker shadow on top of it.
- The shadows really only appear on fixed surfaces like floors and walls. One character isn’t going to cast a shadow on another, and the item on the table will sometimes cast this odd shadow under the table.
There are ways to mitigate these problems, but only at the expense of more processing. And no matter what you do, it always feels like a bit of a hack. I experimented with shadows like this way back in the early aughts, and I hated the entire system. For all this fiddling around, there are still situations where they look more ugly and incorrect than those cheap and easy blob shadows of yesteryear.
This is just one shadow solution of many. And to be fair, I think this was the worst one. It’s certainly the worst one that I tried.
For static stuff we could bake shadows in. The Quake games had shadows that were created by the level editor. They looked amazing for the time period. The drawback was that the shadows couldn’t move. If I put a shadow on some bit of moving wall or platform, then it would carry the same lighting with it whenever it moved. If I had a lift shaft that was brightly lit at the bottom and dark at the top, then it would look horrible no matter what. Either the platform would be lit at the bottom position and then seem to glow when it was at the top, or it could be lit at the top position and be jarringly dark when it got to the bottom.
|This is a secret wall in Quake. The level is lit with the panel fitted seamlessly into the wall, where lights can’t reach that edge. So when the panel slides open, the edge remains black.|
John Carmack called this the “Hanna-Barbera effect”. All animators suffered from this to some degree. Even Disney. But HB – who were pretty aggressive and inventive with their cost-cutting – were particularly bad about it. The moving scene details would be flatter and simpler than the more lavishly drawn static backgrounds, which made them stand out. You could tell ahead of time that the precipice of rock was going to fall away when someone stepped on it, because the rock didn’t match the rest of the scene in terms of lighting and color.
|What is going on with this wooden floor? Is it supposed to be polished? (It’s not a reflection.) Or are those shadows? (They’re too bright.) Or are the colored patterns supposed to be marks on the floor? (They don’t look like it.)
The rake in the foreground is drawn flat-color while the background is drawn using fancier shading. This makes the rake stand out, so it’s obvious it will be a moving object in the upcoming scene.
So you’ve got all these different shadow and lighting techniques. One is good for small stuff that moves. Another is good for static walls. Another is great for objects that need to receive light but don’t need to cast shadows. So you’d have all these different lighting systems, and they didn’t really interact intuitively.
|I know it’s popular to dump on this game, but I still really dig the first half or so.|
One of the breakthroughs of Doom3 was the unified lighting model: It was designed to do away with these various hacks and tricks. There wasn’t one technique for walls and another for furniture (with a special side-case of usable items) and another for people. There was one pipeline for everything.
This has an interesting effect on complexity. The unified lighting system was at least as complicated as what came before, but all of that complexity, once managed, could be shoved into a box and you could forget all about it. You didn’t have special case-checking right in the middle of the game code, and the artists just had to concern themselves with a few basic rulesLimits on the number of lights shining on a particular surface, for example. and not have to be aware of the more technical systems underneath. Art was as challenging as ever and the code was as complex as ever, but the two were no longer mixed together in a big soup of compromises, exceptions, and special-case uses.
I really like this, and I think it’s a good place to start messing around with advanced shaders. The techniques and theory are pretty well documented, so I shouldn’t need to invent anything new.
 And then I resort to that later anyways.
 Like at sunset, for example.
 Limits on the number of lights shining on a particular surface, for example.
There are two major schools of thought about how you should write software. Here's what they are and why people argue about it.
Grand Theft Auto Retrospective
This series began as a cheap little 2D overhead game and grew into the most profitable entertainment product ever made. I have a love / hate relationship with the series.
Deus Ex and The Treachery of Labels
Deus Ex Mankind Divided was a clumsy, tone-deaf allegory that thought it was clever, and it managed to annoy people of all political stripes.
A programming project where I set out to make a Minecraft-style world so I can experiment with Octree data.
Push the Button!
Scenes from Half-Life 2:Episode 2, showing Gordon Freeman being a jerk.