on Apr 14, 2009
I should have given these details in the initial post:
- The project is bound to Windows right now. Several people made great suggestions yesterday for wxWidgets, Qt or SDL. I don’t have time to examine those now (the interface is already done and I’m anxious to move forward) but I am making a note to make sure my next project is written on something portable.
- I’m using OpenGL for rendering. The API seems more quaint every year, but it still gets the job done without getting in the way.
- The look I’m going for is a helicopter-level view of a city, not a street-level view.
- The city is going to be pretty basic. 30 hours is not much time, and I’m aiming for simple-yet-effective as opposed to profound and feature-rich. While a lot can be done with a procedural city, I’m not going to take this idea very far to begin with. I just want something that’s fun to view for a couple of minutes.
As I stated in my original post, my goal is to make a program that generates all of its own art assets. The first asset I’ll need is a texture map for on the buildings.
The surfaces of buildings are typically complex. Brick? Concrete? Classical engraved stone? Steel? Making all of those surfaces, their colors, their shapes, and making those details line up with the building itself would be a huge undertaking. We’re not making GTA IV here and I don’t have an army of developers and artists at my command. We’ve got one guy, store-brand coffee, and 30 hours. So the smart thing to do is to make this a stylized low-detail night scene and make the buildings themselves pitch black. I’ll hint at a detailed building using the light coming from the windows.
Since I’m dealing entirely with windows to define a building, I grab some reference images of nighttime cityscapes and study them to see what kind of details and effects I can devise.
I set up a texture map. It’s 512×512 pixels (a nice mid-size texture) and I divide it into a grid of 64 x 64 windows. This means each window gets just 32×32 pixels. That’s a very small space, and the goal here is to make the most of it. The old Douglas Adams quote comes to mind, “Little expense had been spared to create the impression that no expense had been spared.” That’s sort of the approach I want to take in regards to the details in these windows. Here is part of the full 512×512 texture:
|This is the upper-left quarter of the texture. There’s just no point in making you download the whole thing.|
Note that this texture will never appear in its entirety on a single building. Instead, many buildings will share this texture and each will take a randomly chosen section of it. Now, so far this doesn’t look very interesting. Obviously some lights are turned off at night. But even in windows where the lights are off, there’s often a little bit of light coming from other nearby interior rooms. So few windows are truly black. And in rooms where the lights are on, the amount of light visible outside varies a great deal based on the placement of the lights and the positioning of light-blocking furniture within the room. Taking this into account, I make a texture that looks a lot more plausible:
|Again, this is just 25% of the texture.|
This texture is randomly generated when the program is run, so the pattern of windows changes each time.
Looking at the reference photographs of buildings at night, I note that a lot of them have dark clutter / shadows along the bottom of the windows, which is either furniture or shadows cast by furniture. This presents an interesting challenge, which is how to create this effect with just 32×32 pixels. (And since I need a 1-pixel border around the window I actually only have 30 pixels to work with.)
I try adding some random grayscale noise to the windows. It helps make the windows look less artificial, but it doesn’t really convey the impression of stuff being in the rooms. I try adding a few randomly generated vertical lines across the bottom of each window, but then the windows sort of look like tiny little bar graphs. Eventually I realize that since I’m trying to clutter up the window to give the impression of furniture, I should aim for making dark patches in the lower half of the texture. Also, the lights in a window tend to be brighter at the top, so I create a slight fade that darkens the lower half of a window. The result:
That’s closer, but the windows still seem a little flat. I try adding some random color noise and all of a sudden the windows pop to life. The changes in color make the brain think this is a far-off view of a cluttered, busy scene.
This might seem like a lot of silly fussing around over individual pixels, but these little squares of light will be all we have to go on and they will need to sell the rest of the building.
This scattered look for the lit windows doesn’t apply to all buildings. Some are lit by floors or sections of floors. Sometimes people are working late on one floor while the workers in another office have sensibly gone home for the night. So I add a random chance that sometimes a building texture will light its windows in groups instead of scattershot.
That should create some much needed variety. Now that I have a basic system for making little squares with “lighting” and “furniture”, the last step is to just make a whole bunch of different window shapes. Some will be long horizontal windows. Some will be slightly rounded. Some will be slightly narrow. Etc. I manage to come up with 8 different varieties of window that look acceptable, which is less than I was hoping for. (There are only so many window shapes you can make with 30 pixels!) If I don’t have enough different textures then the constant repetition will be obvious to the viewer. The human brain is infuriatingly good at detecting repeating patterns.
Here is a full view of one of the 512×512 textures. Note that it’s 64×64 windows, so if a building was 65 stories tall then the top floor would have the same window pattern as the bottom. Not that anyone would notice, but I don’t plan on making any that tall.
And just because I can’t stand to wait any longer to see if all of this work is worthwhile, I turn off the green development background and slap the textures onto a big row of cubes:
|Two thousand frames per second. I tell my graphics card not to get too cocky. We’ll see how badass it thinks it is once I hand it a few thousand of these suckers to draw at once.|
Pretty cheap, but it looks good enough so far. Next time: Buildings!
EDIT: As has been pointed out below, 64×32 does not equal 512. I changed the parameters of how textures would work several times, and I wasn’t careful to make sure I updated the post to reflect these changes. For the record, it’s 512×512 pixels, 64×64 windows, and 8×8 pixels per window in the screenshots you see in the post. I’m still tweaking them from time to time to see how it looks at different settings. It actually looks worse when using more pixels per window. With 32 pixels per window, the detail hinted at is revealed to be nothing more than a sprinkle of color and the illusion is broken. It could still be done, but the furniture simulation would need to be more sophisticated.)
Shamus Young is an old-school OpenGL programmer, author, and composer. He runs this site and if anything is broken you should probably blame him.