Algorithm description The algorithm I'm going to illustrate requires perfect sorting like
the painter's algorithm (z-buffer doesn't require that) but computes
the final image much faster, avoiding to compute data about back
polygons (for z-buffer every pixel of every polygon is always
considered, at least for the depth check).
Note: | There's another algorithm for which the inventor claims to be my
friend Paul Nette (Midnight) that's called "s-buffer".
That algorithm doesn't require any sorting (like z-buffer) and is
a big improvement over z-buffer for simple scenes (small number of
polygons). However complexity increases when more polygons are added
and in my opinion, excluding a few very special situations, that
s-buffer solution isn't a good choice. The algorithm described in
this text is NOT the one described by Paul Nette.
|
The main idea is to draw the polygons on the screen starting from
the nearest and finishing with the farthest one, keeping track of
which areas of the screen have been already covered by a polygon and
which ones are still available.
This map of available areas is maintained in a list of "span" (intervals)
for each scanline:
/*
** Every span is defined by the initial and ending
** x coordinate and by a pointer to the next span
** on the same scanline.
**
** x0 = starting X for the span
** x1 = ending X for the span
** next = next span on the same scanline
*/
typedef struct SPAN
{
int x0;
int x1;
struct SPAN *next;
} Span;
/*
** For every scanline there's a pointer to the
** first span for that scanline. MAXYRES is the
** maximum vertical resolution admissible for
** the screen.
*/
Span *FirstSpan[MAXYRES];
|
|
During initialization it will be created a single span for every
scanline as large as the screen, to mark the scanline completely
available. During the rendering those span lists will be kept updated
with new insertion or deletion. Since during the redering of a scene
there will be a lot of creation/deletion of spans it's better to
implement a reuse logic to avoid the overhead a general memory manager
would add.
/*
** List of reusable spans
*/
static Span *FirstUnusedSpan;
/*
** Allocation of a new span: before asking a real
** allocation to the memory manager the reusable
** span list is checked.
*/
static Span *NewSpan(void)
{
Span *s;
if ((s=FirstUnusedSpan)==NULL)
s=(Span *)Malloc(sizeof(Span));
else
FirstUnusedSpan=s->next;
return s;
}
/*
** Deallocation of a span: it's not a true
** memory manager deallocation but just an
** insertion in the list of reusable spans.
*/
static void FreeSpan(Span *s)
{
s->next=FirstUnusedSpan;
FirstUnusedSpan=s;
}
|
|
|