Fixing the antialias
There’s another feature that seems to stop working.if you are using a screen with a pixel ratio above 1, you probably can’t see the problem.
Be careful; if you only have the renderPass available, you won’t see the problem because the render is done in the canvas with antialias support. Enable at least one pass to see the problem.
That is because the render target used by EffectComposer does not support the default antialias. We have four available options:
- Provide our own render target on which we add the antialias, but that won’t work on all the modern browsers.
- Use a pass to do the antialias but with lesser performances and a slightly different result.
- A combination of the two previous options where we test if the browser supports the antialias on the render target, and if not, we use an antialias pass.
Things suddenly got complicated.
Adding the antialias to the render target
By default EffectComposer is using a WebGLRenderTarget without the antialias.
Fortunately, we can provide our own render target as the second parameter of EffectComposer. We are going to provide the same render target to begin with and make sure that everything is working. Then, we are going to add the antialias.
If you look at the code of the EffectComposer located in /node_modules/three/examples/jsm/postprocessing/EffectComposer.js you’ll see the renderTarget being instantiated with specific parameters.
The first two parameters are the width and height. We can use random values because the render target will be resized when the setSize(...) function will be called on effectComposer:
const renderTarget = new THREE.WebGLRenderTarget(
800,
600
)
Then, we can send that renderTarget to effectComposer:
const effectComposer = new EffectComposer(renderer, renderTarget)
We should get the exact same result, but now, we have control over the render target.
The WebGLRenderTarget class can actually receive a third parameter that is an object and that will contain some options.
The only option that we need to provide is the samples property that will enable the antialias:
const renderTarget = new THREE.WebGLRenderTarget(
800,
600,
{
samples: 2
}
)
The more samples, the better the antialias with 0 corresponding to no samples at all. Be careful, every increase on that value will lower the performance.
As we said earlier, if the user has a pixel ratio above 1, the pixel density is already high enough to not be able to distinguish the aliasing. In that case, we don’t really need an antialias and we should let the samples property to 1.
We can retrieve the pixel ratio from the renderer with samples: renderer.getPixelRatio():
const renderTarget = new THREE.WebGLRenderTarget(
800,
600,
{
samples: renderer.getPixelRatio() === 1 ? 2 : 0
}
)
And that’s it!
Sadly, this won’t work for every browsers. That is a matter of WebGL 2 support. People updated WebGL a few years ago, and browsers slowly add support to the different features. You can see how the support is going on here: https://caniuse.com/#feat=webgl2
At the moment of writing this lesson, major browsers like Safari and iOS Safari only supported it recently.
Using an antialias pass
Let’s comment the samples property in order to test the antialias pass properly:
const renderTarget = new THREE.WebGLRenderTarget(
800,
600,
{
// samples: renderer.getPixelRatio() === 1 ? 2 : 0
}
)
We have different choices for the antialias pass:
- FXAA: Performant, but the result is just “ok” and can be blurry
- SMAA: Usually better than FXAA but less performant —not to be confused with MSAA
- SSAA: Best quality but the worst performance
- TAA: Performant but limited result
- And many others.
Choosing the best antialias pass is a matter of performance and visual expectations. Try them until you’re satisfied with what you see at a reasonable frame rate.
For this lesson, we will go for the SMAA.
Earlier, we said that we should add the gammaCorrectionPass at the very end, but the antialias pass should be added after it in order to optimise it.
Import the SMAAPass, instantiate it and add it to effectComposer:
import { SMAAPass } from 'three/examples/jsm/postprocessing/SMAAPass.js'
// ...
const smaaPass = new SMAAPass()
effectComposer.addPass(smaaPass)
The aliasing should be gone.
Combining the two solutions
The cool thing with the samples property that we added earlier to the WebGLRenderTarget is that if it’s not supported (because the browser is using WebGL 1) it’ll be ignored without triggering any error.
This means that we can let it like that and add the SMAAPass only if the user has a screen with a pixel ratio equal to 1 and his configuration doesn’t support WebGL 2.
To know if the browser supports WebGL 2 we can use the capabilities property on the renderer. This property contains many details about what is supported. The property we need is isWebGL2:
if(renderer.getPixelRatio() === 1 && !renderer.capabilities.isWebGL2)
{
const smaaPass = new SMAAPass()
effectComposer.addPass(smaaPass)
console.log('Using SMAA')
}
And we get a nice picture on every browsers with a minimal drawback.