Thursday, November 13th, 2008

Projective texturing using Canvas

Category: Canvas

Steven Wittens has taken on a challenge to make fake 3D in Canvas and built a library for projective texturing:

Canvas is still limited to 2D: its drawing operations can only do typical vector graphics with so-called affine transformations, i.e. scaling, rotating, skewing and translation. Though there have been some efforts to try and add a 3D context to Canvas, these efforts are still experimental and only available for a minority of browsers through plug-ins.

So when my colleague Ross asked me if we could build a Cover Flow-like widget with JavaScript, my initial reaction was no… but really, that’s just a cop out. All you need are textured rectangles drawn in a convincing perspective: a much simpler scenario than full blown 3D.

Perspective views are described by so-called projective transforms, which Canvas2D does not support. However, it does support arbitrary clipping masks as well as affine transforms of both entire and partial images. These can be used to do a fake projective transform: you cut up your textured surface into a bunch of smaller patches (which are almost-affine) and render each with a normal affine transform. Of course you need to place the patches just right, so as to cover any possible gaps. As long as the divisions are small enough, this looks convincingly 3D.

So some hacking later, I have a working projective transform renderer in JavaScript. The algorithm uses adaptive subdivision to maintain quality and can be tuned for detail or performance. At its core it’s really just a lot of linear algebra, though I did have to add a bunch of tweaks to make it look seamless due to annoying aliasing effects.

Check out the code.

Posted by Dion Almaer at 8:01 am

4.2 rating from 40 votes


Comments feed TrackBack URI

I always love to see Canvas developments. This one is seems a bit buggy and pretty laggy still…

Comment by TNO — November 13, 2008

It’s funny how javascript lags flash by a few years. People used to hack 3d support into flash in much the same way as is done here for javascript.

Comment by Joeri — November 13, 2008

And its funny how Flash still lags Wild Tangent and Java applets in the 3D realm by a few years. The difference is that JavaScript+Canvas won’t cause brain cancer with a plopped in object tag.
If you can plot a pixel, you can do 3D, Its a no brainer and the rest follows from that. (Limited by memory and processor power of course).

Comment by TNO — November 13, 2008

>> It’s funny how javascript lags flash by a few years.
>> And its funny how Flash still lags Wild Tangent and Java applets in the 3D realm by a few years.
Funny how DRY works in real life :)
I also remember seeing a neat ActiveX OpenGL hackjob way back.
I wonder if it wouldn’t just be more beneficial for these guys to spend time with the Mozilla/Webkit/whoever folks making proper bindings and wrappers to existing C libraries available and submitting the prototypes as proposals to W3C, rather than spend time twisting and bending canvas and javascript.

Comment by LeoHorie — November 13, 2008

But the twisting and bending is where the fun is at.

Comment by jseidelin — November 13, 2008

Shouldn’t this be called the Phantom Zone project. Please, someone put a photo of General Zod up there!

Comment by heme — November 13, 2008

^ I was also reminded of that Superman movie. ;) It’s interesting to see the performance difference between Safari and Firefox on this one, also. I haven’t played with it much, but canvas is pretty nifty stuff.

Comment by Schill — November 13, 2008

This demo oozes cool. :)
I encourage everyone to have a look at the sources!
As it says in the comments: “Built for readability, not for speed.”
A very interesting read.
Of course this has been done on countless systems using countless languages – as said before the basic principles always remain the same once you can draw pixels.
But this is a very nice & clean implementation. Would make for fine educational material.

Comment by carrion — November 13, 2008

Thanks for the plug! However I think some of you have the wrong idea.

Obviously, this is slow and experimental, and in no way a replacement for real 3D support in Canvas, Java or Flash. But, that doesn’t mean that this isn’t an interesting experiment: when Canvas 3D finally lands, we’ll still be stuck with old browsers. This little toy was meant to answer a couple of questions… How good does this fake technique look? How detailed does it need to be? Can typical canvas implementations handle it? The only way to do that was to try it out.

In fact, I find it funny that Ajaxian uses the word ‘library’ to refer to this, when this was never meant as such. They cut out the last paragraph of my post where I clearly said it was just a curiosity and nothing serious. It’s weird how on the web, every piece of code seems to be judged on how other people can re-use it. I’m guessing this is because there is a large contingent of ‘developers’ who don’t in fact write any code themselves. They just glue together existing libraries and widgets.

For me, this was just a fun distraction. But I’m equally comfortable hand-rolling assembly code for triangle rasterizers, or stringing together shaders to run on the GPU. I’m sure I’m not the only programmer out there who likes coding in scripting languages for how quickly you can build things, rather than ultimate usefulness of the end product.

Comment by UnConeD — November 13, 2008

If you’re sat there thinking that it’s slow, fire up chrome and give it a whirl…

Comment by jtresidder — November 14, 2008

>> But the twisting and bending is where the fun is at

That, I can’t argue with :)

I still would like to have a mature 3D API to use for a serious web application someday though. On that note, flash 10 isn’t looking too shabby.

Comment by LeoHorie — November 14, 2008

This reminds me of a similar Flash project called PaperVision3D:

In the past year, the project has made some amazing progress.

Comment by RSarvas — November 14, 2008

People complaining about the speed, or not, should look at the code. There is two obvious reasons why this can be slow :
* the rendering is called in the event listener of mousemove instead of being called regularly.
* the Canvas is resized for every frame, which can trigger some garbage collection.

Anyhow I’m fan of such experiments and really like UnConeD‘s ways.

Comment by p01 — November 14, 2008

Leave a comment

You must be logged in to post a comment.