If it's slow or spiderwebby, try Chrome. If the image disappears, refresh
the page (NaN somewhere, sorry about that).
Old software-rendering tricks ride again!
<canvas> can be abused to provide affine
triangle-texture-mapping: set a triangular clip region,
compute the right tranformation matrix, and then call
drawImage(). Similar to the way Papervision3d works in Flash.
See drawTriangle in jsgl.js for details. This works
especially well in Chrome, because it doesn't antialias clipping regions.
Hopefully they never "fix" that.
Affine texture mapping has glaring artifacts if there's much
depth -- textures look like they're swimming. Back in the
days of software rendering, I experimented with recursive
subdivision and I knew it worked, though it wasn't as fast on
Pentium as the technique pioneered by Quake and Descent, where
your scanline fill routine calculates the correct 1/z every 8
or 16 pixels.
However, scanline subdivision is not really feasible in
Javascript, where we're relying on <canvas> to fill our
pixels using native code.
So, triangle subdivision makes sense.
The subdivision method is fairly simple; the idea is that
each triangle edge is tested to see how badly it needs
perspective correction. Each edge is tested independently.
Each edge that needs subdivision is bisected. This gives 8
cases for any triangle (each edge can either be bisected, or
not), generating between 1 and 4 output triangles. Then the
process is applied recursively. Because it is edge centric, adjacent
triangles make the same decisions for shared edges, and there are no
t-junctions.
This works pretty well. However, it does have some bizarre
swimming artifacts of its own, when combined with conventional
near-plane clipping. The problem is that the new vertices
generated by clipping are highly sensitive to the relationship
of near_z to the original triangle verts, so they cause the
clipped triangle boundaries to crawl around, and that makes
the perspective correction swim around distractingly. I
implemented an alternative method of near-plane clipping to
prevent the crawling. It works by bisecting the edges of triangles
that cross the near_z plane to form four subtriangles.
Some of those new triangles are completely behind
near_z and are discarded; some are completely in front and are
retained, and some are still spanning near-z, and are processed
recursively. I use a margin around near_z so the recursion
eventually terminates. (Sadly, the way I'm doing this clipping does
make t-junctions all over the place. Cie la vie.)