5
\$\begingroup\$

While I have a lot of experience with coding SW&HW 3D rasterizers across multiple platforms and eras, I never got to coding a 2D renderer for a non-polygonal racing game with multiple rolling hills (with correct perspective and occlusion). Say, something like a Lotus from 1990.

Of course, the roadside props and cars are all sprites, but I'm curious as to how the road, especially with rolling hills is done. My uneducated guess is that it's composed from separate horizontal lines (that have the terrain and road texels), which are simply stacked above each other with zero horizontal offset (for the straight road) and once you approach the curve, the horizontal offset for the given scanline is taken from a look-up table (for a given curve). Given enough RAM we could have a lot of different curves. Or perhaps if the curve equation is simple (not some Bezier stuff), then it could be even calculated at run-time (integer / fixed-point, of course). This should supply the illusion of curved road.

Now, for the single rolling hills, we would probably merely introduce a vertical offset to our road scanlines, so this could give an illusion of going up/down the rolling hills, as all scanlines would move up/down on the screen.

What I don't quite understand is how the multiple rolling hills with a perspective are done, especially if they are overlapping correctly. This is probably hard to visualize, but imagine there is a second hill in the distance, and you are climbing up the hill in front of it. While climbing up, you can see it in the distance, and when you get to the top of the hill, the valley between two hills appears. With polygonal gfx, this is super easy and you don't even think about it - the valley is just back-face culled polygons, so it's obvious they did not get rendered when climbing up the first hill. Meaning - the valley is transformed, clipped and discarded thanks to back-face culling.

But how would you do that with the 2D horizontal scanlines ? Perhaps using SW Z-Buffering on each scanline ? One idea could be that you separate the scanlines for the hill up and the hill down - e.g. you only start rendering those scanlines when you get to the top of the hill- but I doubt it would work flawlessly.

I suspect, as with everything in game coding, once you start prototyping it, a solution would simply be reached by multiple iterations/prototypes. I'm in the middle of some other project now, so I don't want to distract myself with another project, hence this question.

EDIT: StackOverflow supplied few links, right before I submitted the question, and one of the threads contained this link: http://www.extentofthejam.com/pseudo/ It explains in a great amount of detail various techniques for this type of rendering. I am in the middle of it right now. From what I can see, the early games did actually use the FOV projection! That explains why their 'feeling' of perspective was so exact, as you can't really hack it properly (especially for a 3D-trained eye).

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

The idea for the rolling hills is as follows: you use the painters algorithm to draw your road; so basically it means you start at some distance in front of the the car and work your way back to the car. I'll try to explain the process with some ACII art :-)

Consider this as a hill profile (seen from the side):

        __
___    /
   \__/
0123456789

The car is at 0. You start by section 9 and work backwards to 0.

For each segment you draw keep track of the vertical position of the segment on the screen. Section 9 is a flat section in the distance and will be drawn as a triangle let's say screenY 100-102. We keep track of the 102, it is the screenY coordinate of the nearest part of the road section you're drawing.

9   /\  screeny=102

Section 8 is flat, so your perspective calculation that section would end up at scanlines 102-104.

9   /\  
8  /  \ screeny=104

Now come the tricky bits. The road goes down in sections 7 and 6. For both sections perspective calculations will make the scanlines go down further, it's just a bit steeper. So after drawing section 6: we're at, let's say, scanline 110. Next bit comes up.

9     /\  
8    /__\
7   |    |
6   |____| 
5  /      \
4 /        \ screeny=200

It becomes iteresting at section 3: this is where the hill goes back up (remember we're working back to front; from the car's perspective this section goes DOWN and therefore is hidden from view). For this section the calculated vertical position is actually ABOVE the scanline we're at. Thus it is culled and not drawn. We still however keep track of the scanline that results from the perspective calculation.

9     /\  
8    /__\
7   |    |
6   |____| 
5  /      \
4 /        \ 3 is not drawn but screeny=180 

Now at section 2 the road is flat again and should appear. The perspective calculation should come up with a screenY below the previous one, thus the section is drawn on the screen.

9       /\  
8      /__\
7     |    |
6     |____| 
5  __/______\__   4 is drawn over by section 2,
2 /            \   due to painters algorithm
1/              \ 

Keep in mind that the roadside objects are also drawn using the painter's algorithm, the screenY you kept recording should help you clip the sprites at the right point for objects that are just behind the apex of an hill.

Hope this helps :-)

Check the javascript code by Jake Gordon, he explains a lot of the math that is going on here: http://codeincomplete.com/posts/2012/6/22/javascript_racer/

\$\endgroup\$
0
\$\begingroup\$

To close out this question, like I mentioned in my edit, the Stackoverflow supplied the link with detailed description of the algorithm and a link to site with javascript examples, that fully answered my query.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .