on Apr 23, 2009
Now it’s time to round out the scene with lights. But in order to place the streetlights, I need to know where the streets are. In an earlier step I reserved street space, but now I’ll take that space and divide it up into sidewalks and specific lanes.
That done, I can have it scan over the world and look for places that need streetlights.
I add another render pass for doing bloom lighting. The effect is of course striking, but unrealistic.
I don’t want to embrace or discard the feature, so I make it so the effect can be toggled off and on. Naturally enabling it causes a performance hit, but I can’t really appraise how bad it is until I get some of the more egregious slowdowns fixed and the overall performance optimized.
Now for cars. Since I have the lanes defined, I can just drop cars randomly onto lanes and let them figure out which way to go. They then select a speed and drive in a straight line until they get to the edge of the map, and then they randomly appear in a new location and repeat the process. The program draws a simple 2d panel at a car’s location. If the car is heading away, it draws it red, otherwise, white. This would look crazy if you were looking straight down on the scene, but up here in the News 5 chopper it looks okay.
I thought it might be cool to have the cars engage in more elaborate behavior. So I wrote a bunch of code to have them switch lanes if they find themselves behind a dawdler, and randomly make turns when they come to an intersection. I spent over an hour fussing with a couple of intersections and tuning the car behavior to make lane changes and cornering look natural. It was still a little stiff, but I was confident that with a bit more tuning I could make it work.
Then I pulled back to the city-wide view and found that all that work was almost completely unnoticeable. It was actually hard to spot a car doing something interesting. On the rare occasions where I was looking when a car made a turn, the robotic movement actually hurt the verisimilitude of the scene instead of helping. Both turns and lane changes actions are very complex. Turns are not linear. Cars decelerate before the turn, increase the rate of turn as they go, and begin to accelerate again before the turn is completed. This is aside from the fact that steering with the front wheels produces some fairly noticeable differences between the movement of headlights and taillights. Worse, the tiny bit of AI I’d written was slowing the program down. It was small and simple, but multiplied by hundreds and hundreds of cars it was causing a measurable performance hit.
So… I wasted a bunch of time writing code that slowed down the program and looked terrible when you noticed it at all.
Click & drag over text. Press delete.
This is a classic case of oversimulation. The programmer (me, in this case) sits down and thinks “oooh! I know how this system works, so I’ll write a simulation of it!” It’s a deadly trap, because once you start simulating one thing (cars changing lanes) then you suddenly need to simulate other things (making visually believable lane-changing movements for automobiles) which requires still other things (turning behavior of a car) and pretty soon you’re coding some sort of time-devouring boondoggle when all you wanted was to write a few simple rules about going around slow people.
John Carmack talked about this at Quakecon in 2004. (That article is long. The relevant passage is about halfway down.) During development of Doom 3, they had a programmer write a complex audio / acoustics engine that would try to propagate sounds realistically. It was supposed to make big rooms echo, dampen sounds in other areas, and so on. In the end, the result was worse than the simplistic system it was supposed to replace. It was actually easier to have artists manually specify how things ought to sound and how far particular sounds could travel than to have the game try to intuit it. They scrapped the audio simulation and replaced it with more conventional videogame sound behavior. Luckily for me, my losses were only a bit over an hour. Theirs was months. Then again, I might have incurred the worse losses as a percentage of total budget. (An hour and a half is about 5% of my budget, assuming I stick to it. (Although I’ve lost track of how many hours I’ve sunk into it. Weekends do that, particularly since I had a lot of Left 4 Dead mixed in with the coding hours. Still I’m going to take a wild guess and say I have perhaps three or four hours left before I hit my 30 hour goal. Maybe.))
I pull back and assess the view now that all of the key details are in place, and I’m not happy with the result. I’d looked at a lot of reference pictures where the streets were pure black, and only streetlights and car lights were visible. But the same effect is not working here in my city. I look down in the blank areas between the buildings and I expect to see a street. The black void looks unnatural.
Looking at the reference photographs again, I realize that the “black street” effect is something you only get at extreme distances. Looking down from above, the streets are always brightly lit. As a test, I make a cheap texture map to slip under the city. It’s a single huge texture that just shows all of the streets. It’s of such low detail that a lane of traffic is just one pixel wide.
The street is awful, cheap, lo-res, and doesn’t even fit in with the buildings. And yet it still looks better than the pitch black. My original concept for the streets is unacceptable. I need to somehow depict the streets in this world.
|Cheap, 1-pixel lanes, no lines on the road, no surface detail, nothing. But it still looks better than black.|
But this is almost dragging us back to the problems that come with oversimulation. I wanted to just hint at the streets. The last thing I wanted was to draw the eye down there with a bunch of detail. Making streets that are illuminated is fundamentally different from my intent, which was to leave things in shadow and get by using smoke & mirrors. If I make a well-lit street, then this will draw the attention of the viewer. My light-source cars will no longer look right, because it won’t be black cars on a black background, it will be invisible cars on a light background. If I add little cars, then the user will probably notice that the streets are barren – no traffic lights, lines, or crosswalks. If I add all of that, then my high-detail streets will look out of place next to the silhouette buildings. If I add a bunch of storefront-type detail, then those blank areas around buildings will stand out more. What will I do then? Add little parks and parking lots?
This sounds like a lot of fun, actually, but adding all of that detail would obliterate my time budget. In fact, each of those elements could turn into a week-long project in itself, all to simulate something I did not want the user to see in the first place!
I need to think about this.
I think the next step should be to begin optimizing performance. I’m down to 30 frames a second (varies) at this point, and so this needs to be done. (Remember my budget is ~100 FPS.) Once I have that finished, I’ll know how many CPU / GPU cycles I have to waste on street details. And maybe I’ll get some good ideas in the meantime.
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.