March 2, 2013

JavaScript: Antialias post-processing with THREE.js on a (non) retina screen

When drawing a basic Mesh object in THREE.js, the edges can be particularly jagged if your browser doesn't properly support antialiasing in webGL (most don't seem to at the moment). In my current project this became a sticking point, and I set out to fix the aliased edges of my 3D models and Mesh objects.

I found the FXAA post-processing shader effect in the THREE.js library, and it worked like a charm to smooth the rough edges. However, the THREE.EffectComposer utility doesn't automatically handle different pixel densities, and by default, the aliasing actually became twice as bad on the Retina screen of my Mac. After some fiddling, I found that you simply have to adjust the uniforms for the shader effect if it depends on knowing your screen size, as well as set the screen size for the EffectsComposer object.

See below, where I detect the pixel density, and use that to multiply your screen dimensions in the shader and EffectComposer:
var composer, dpr, effectFXAA, renderScene;

dpr = 1;
if (window.devicePixelRatio !== undefined) {
  dpr = window.devicePixelRatio;

renderScene = new THREE.RenderPass(scene, camera);
effectFXAA = new THREE.ShaderPass(THREE.FXAAShader);
effectFXAA.uniforms['resolution'].value.set(1 / (window.innerWidth * dpr), 1 / (window.innerHeight * dpr));
effectFXAA.renderToScreen = true;

composer = new THREE.EffectComposer(renderer);
composer.setSize(window.innerWidth * dpr, window.innerHeight * dpr);
You'll also probably want to update these settings if the window size changes, like so:
$(window).on('resize', onWindowResize);

function onWindowResize(e) {
  effectFXAA.uniforms['resolution'].value.set(1 / (window.innerWidth * dpr), 1 / (window.innerHeight * dpr));
  composer.setSize(window.innerWidth * dpr, window.innerHeight * dpr);