Wednesday, March 14, 2012

Anisotropic Filtering in WebGL

On Google+ this morning Ilmari Heikkinen pointed out that support for the EXT_texture_filter_anisotropic WebGL extension landed in Webkit a little over a week ago. That's awesome, as it's one of the features that I've been waiting for essentially since I first started playing with WebGL.

[Update: To give credit where credit is due I've had several people point out that Firefox had this feature first. Guess I need to follow their commits more closely! Also, while some of the appropriate code is in Chrome I guess the feature isn't officially part of the browser yet. Apparently the fact that it works at all is something of a happy fluke. So I guess that for the time being Firefox is the only browser that actually supports this!]

I put together a really simple demo this morning to show how it works, and if you've got a recent Chrome dev build or Firefox nightly you should give it a try!


Yeah, the demo is ugly but it shows off the effect nicely. For those of you not familiar with the concept, Anisotropic Filtering is an extension of standard mip-mapping techniques that improves the quality of textures when viewed at an angle. A good example of this is the floor and ceiling of the demo above. With standard trilinear filtering (gl.LINEAR_MIPMAP_LINEAR) as the floor gets further away from you it becomes blurry pretty quickly:


By using anisotropic filtering, we alter the way that the mipmaps are queried in scenarios like this one, which can give a much crisper, cleaner image:

There is a performance penalty associated with enabling this, and when you do enable it you can specify how many samples are taken for a performance/quality tradeoff. This has become a pretty common setting available for the user to toggle in PC games, and so it's great that we're now able to take advantage of it on the web too!

Implementation

Turning this feature on is pretty simple in a supported browser. As with compressed textures the first thing we do is query the appropriate extension to see if it's available:

var ext = gl.getExtension("MOZ_EXT_texture_filter_anisotropic");

Obviously with Chrome you would query with WEBKIT_ instead of MOZ_, and eventually the prefix should go away entirely. If it returns null the extension is not supported and we move on, doomed to a life of blurry walls. If it is supported, however, it will return an object containing the required enumerations for the extension.

Since this is a filter mode, we enable per-texture just like we do bi/trilinear filtering or texture wrap modes, but with symbols from the newly queried extension:

gl.texParameterf(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, 4);

Here we're enabling 4x filtering (the value used by the demo) on the bound texture. Setting it to 1 effectively turns off this filtering, and your graphics card determines how high you can set it. You can query the maximum supported value like so:

var max_anisotropy = gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT);

This particular API seems a little bugged right now, as I was only able to get it to return 0, but I expect that will be ironed out soon.

So, like I said, a simple API but a great one for bumping up your rendering quality!