Questions about Charles Bloom's software rendering article

Started by
9 comments, last by Gage64 16 years, 3 months ago
I'm trying to create a software renderer using the architecture described in Charles Bloom's article, and I have some questions. 1) He projects the vertices to the screen while transforming them, and before any clipping is done, but what if the vertex's Z-coordinate is zero? I only thought about this after implementing it, and I never got any errors or visual glitches. I stepped through it with the debugger, and when z = 0, the clip-space coordinates were something like 1.INF, but I never got a divide by zero error. Is this the way it should work or can it fail on another computer or when using a different compiler (I use VC++ 2005 Express)? Also, if it does work this way, I guess I have nothing to worry about because those vertices are not visible on the screen (after viewport mapping the coordinates are still 1.INF)? 2) If I am doing the rasterization myself, are there any different speed tradeoffs I need to consider? 3) Somewhat unrelated so I won't go into details - he bases a lot of his decisions on how a modern CPU works (being cache friendly, avoiding pipeline stalls, using predictable branches, etc.). Can you recommend a resource (preferably a website) that explains these concepts and how they affect performance? This is the first time I'm writing a software renderer, but I still want it to be reasonably efficient. Thanks in advance.
Advertisement
Look at the aritcles about the Pixomatic software rasterizer optimization by
Michael Abrash. Interesting reading.

http://www.ddj.com/architect/184405765

Petr
Thanks for the link, I will definitely take a look at it.
In the article you linked to, the author says he (only) clips to near Z plane before projecting to screen space. This makes sense, as otherwise you just get garbage at negative Z values even if you don't crash when dividing by zero. The rest of the clips are done in screen space to make sure you don't output off the screen.
Quote:Original post by Fingers_
In the article you linked to, the author says he (only) clips to near Z plane before projecting to screen space.


I don't know where you saw this, but if you look at the pseudo-code he provides, you'll see that the vertices are projected long before any clipping takes place.

Quote:This makes sense, as otherwise you just get garbage at negative Z values even if you don't crash when dividing by zero.


Exactly why I asked, but as I stated above, division by zero can still occur because clipping is done after projection.

The funny thing is that before I added near-Z clipping, my program crashed every time an object went behind the camera, and I attributed these crashes to the division by zero. After I added clipping, the crashes disappeared but the division by zero still happens sometimes, so I don't know why there were crashes in the first place.
Actually, looking at it again we're both right. Here's the paragraph I was talking about, about a third the way down:

"I clip against the near Z in View Space, and then clip against the other
planes in Screen Space. I produce new PVs where the edges intersect a
clip plane. These new PVs are filled out by interpolating
ViewSpacePosition (but not normal! it's not needed any more), color,
uv's, and specular. The reason I clip against the sides of the view in
Screen Space (instead of View Space) is to absolutely gaurantee that the
verts are inside the frame buffer rectangle. If you clip in view space
and then project, floating imprecision may push the verts slightly
outside of view, which can cause memory trashes."

I assume what he does is this: if the triangle requires near Z clipping, you create the new vertices in view space and transform them into screen space after the near Z clip, but before the screen space (top/bottom/left/right) clips. This way the screen space clip only deals with vertices with Z >= near Z, therefore they have finite screen space coordinates. The divided-by-zero values are never used for anything (that's when it'd crash). This is really clever.

The IEEE floating point standard has standard behavior for division by zero which will always result in the +/- infinite values. This will of course still crash if you feed the result to something that expects a finite number (like a screen space clipping algorithm).

[Edited by - Fingers_ on January 15, 2008 1:52:14 AM]
The clipping is included in the pseudo code (the last part). Also, if you read carefully you'll see that he says he does near-Z clipping before doing screen-space clipping, but the projection is done before doing any clipping at all.

In my code, I also re-project the vertices that result after clipping (because the clipping process can create new vertices), but other vertices are projected before clipping and they can also have Z values that are zero or negative.

Quote:I assume what he does is this: if the triangle requires near Z clipping, you create the new vertices in view space and transform them into screen space after the near Z clip, but before the screen space (top/bottom/left/right) clips. This way the screen space clip only deals with vertices whose Z is always >= near Z.


Now that you mention this, this might be the reason that the crashes disappeared, but I don't understand why it's important. Why can't screen-clipping deal with vertices with a Z that's less or equal to zero?

Thanks for you help so far.
The projection of the original vertices is done before any clipping, but when you do the near Z clipping, any vertices with Z < nearZ are rejected.

For example, if you look at a triangle that starts with one vertex Z == 0 and two vertices Z > nearZ, the nearZ clipping operation produces two triangles with all positive (view space) Z values. The two new vertices are projected, screen space coordinates are finite because their Z > 0. The two remaining original vertices were already projected and known to have finite screen space coordinates. No infinities will slip through to the screen space clip routine.

The reason why the screen space clipping can't work with the divisions by zero is that you can't really do much if any arithmetic with infinities. Like if you have a vertex with infinite X coordinate, you can't interpolate a new vertex between that and a vertex with a finite X (Infinity + N == Infinity, and all that) to place it at the edge of the screen.
I'll try to explain my "worries" a little bit better.

First, all vertices are projected. Some will have a Z value of zero, so for some of them the screen space position will be infinity. At the same time, clip flags are computed for each vertex using the following code (which appears in the article):

    PV.ClipFlags = 0;    if ( VB_CF & FRONT_PLANE )        PV.ClipFlags |= ( PV.ViewSpacePosition.Z <= Near_Z ) ? FRONT_PLANE : 0;    if ( VB_CF & LEFT_PLANE )        PV.ClipFlags |= ( PV.ScreenSpacePosition.X <= 0 ) ? LEFT_PLANE : 0;    //etc...


But this code does a comparison between two values, and one of them might be infinity. Is such a comparison defined? And if it works here, why can't the screen space clipper do the same (note that by screen space clipping I mean clipping that takes place during rasterization)? Also note that this code is executed before any clipping takes place.
I'm not certain about what the FP standard says about comparison to infinities, but since they're mathematically valid (ie. any finite number is greater than -inf and less than +inf) it'll probably be fine. In this case it's largely irrelevant as long as it doesn't "crash"; if the vertex is clipped by the near Z plane then the other clip flags of that vertex will never be looked at by the screen space clipping code. Even if the comparison generated garbage in the clip flags it would have no effect on the final result.

In general, what'll ultimately cause the system to crash in a division by zero situation depends on the exact code and may be quite a bit later than the division itself. This makes it hard to debug and is why people generally avoid divisions by zero completely.

In the software rendering example I suspect that the clipper would choke on the infinity but that's just a hunch based on that being the first place in the code that'll use the screen space coordinates. It might well not crash until e.g. the actual triangle rasterizer tries to draw an infinitely long horizontal line, or whatever comes out when you convert an infinity to an integer! If you're curious enough you can always disable the nearZ clip and run in a debugger to see how far down the pipeline the infinity will travel before something breaks.

[Edited by - Fingers_ on January 15, 2008 2:13:07 PM]

This topic is closed to new replies.

Advertisement