SMAAPass fragment shader does gamma correction. Is it correct?

I have an issue using SMMAPass with a renderer set with alpha: true.
The borders between solid/transparency have white edges when drawn over a light color background (does not happen over a black background). I found that SMMAShader line 442 does gamma correction and commenting that part seems to resolve my issue

// WebGL port note: Added gamma correction
//C.xyz = pow(C.xyz, vec3(2.2));
//Cop.xyz = pow(Cop.xyz, vec3(2.2));
vec4 mixed = mix(C, Cop, s);
//mixed.xyz = pow(mixed.xyz, vec3(1.0 / 2.2));

Is it correct to comment those lines since i’m already using OutputPass?
The strange thing is that if I remove SMAAPass and use a multisampled render target in EffectComposer, I get again the white edges (and quality is lower that default canvas antialiasing)

I don’t see a difference in color with and without these lines.

According to the comments of the original HLSL source file, SMAA works in gamma space. So the lines seem to apply gamma, perform the mix and then undo the gamma.

So the unmodified SMAAShader does require OutputPass.

I’m currently using SMAA after OutputPass.Is it wrong?
If I use SMAA before OutputPass, the resulting image is not antialiased

I have to study SMAA before I can say something about this. FXAA for example requires sRGB so it comes after OutputPass.

Okay, there is actually an issue in SMAAPass that I have fixed in the following PR.

You can apply the same fix now by setting the needsSwap property of SMAAPass to true. You should then use the same pass order like in the example (meaning OutputPass comes last).

1 Like

By setting needsSwap = true I can use SMMAPass before OutputPass. However I still get a white outline around objects adjacent to transparent webgl background (grey background is set in html):

By commenting the lines in SMMAShader as in my first post and executing SMAAPass after OutputPass I get better results:

That seems very strange to me, maybe there’s still something to investigate?

Since you are using a transparent background, does it help when adding the following line to the SMAA pass configuration?

pass.materialBlend.premultipliedAlpha = true;

Unfortunately no, I get the same result of the first image with a white edge between objects and background.

To summarize:

  1. When SMAA shader input data is sRGB (smaa is lat pass) and inline gamma conversion is removed result is better and transparency seems to work

  2. When SMAA shader input data is linear (output pass is last) there is a white outline and result is worse, even when commenting the inline gamma conversion

Maybe this comment from implementation may help?
https://github.com/iryoku/smaa/blob/71c806a838bdd7d517df19192a20f0c61b3ca29d/SMAA.hlsl#L127

I’ve updated the SMAA example in the dev branch so it is easier to debug the technique. If you disable auto-rotate and toggle SMAA, you can better see the effect of the anti-aliasing. After the last PR, SMAA definitely works like when it was originally added in 2016. That said, the implementation might have an issue right from the beginning.

Do you think you can modify the following fiddle so we can better investigate the issue with transparency? three.js dev template - module - JSFiddle - Code Playground

SMAA is a multi-pass technique and the first two passes should operate on non-sRGB data. If you put SMAAPass last, this isn’t true anymore since the read buffer for SMAA (the result of the previous output pass) is in sRGB color space.

The original author of SMAAShader added the inline color space conversion in SMAANeighborhoodBlendingPS() to make SMAA work with linear input.

At default it doesn’t show up, but if I stop rotation at the following angles:

child.rotation.x = 0.24;
child.rotation.y = 0.49;

I can see intense aliasing artifacts:

SMAA

And I have set needsSwap = true in the SMAAPass.js to be sure.

EDIT:
In order to be able to see the artifacts, I think it would be better to orbit slowly in the examples (and stop at will) around the object using the mouse, instead of having them rotating.

https://jsfiddle.net/b8yw57vr/
It seems the relevant line is renderer.setClearColor(0, 0);
Creating the renderer with { alpha: true|false} does not make any difference

@dllb Thin white lines on a black background is challenging for any AA technique to process. It’s not surprising that SMAA does not produce good results.

Can you please test if these lines solve the issue. It looked good on my device:

C = sRGBTransferOETF( C ); // linear -> sRGB
Cop = sRGBTransferOETF( Cop ); // linear -> sRGB
vec4 mixed = mix(C, Cop, s);
mixed = vec4( mix( pow( mixed.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), mixed.rgb * 0.0773993808, vec3( lessThanEqual( mixed.rgb, vec3( 0.04045 ) ) ) ), mixed.w ); // sRGB -> linear

These lines perform a real sRGB color space conversion (and not just gamma correction).

Sure, but even SSAA at level 1 (light on GPU) produces far better (acceptable) results:

SSAA-L1

Level 2 (medium on GPU) is very good:
SSAA-L2

At level 4 it’s near perfect (but heavy):

SSAA-L4

So I don’t see any reason whatsoever why one should use SMAA.

I tried copy/paste your lines but I got a compile error. So I substituted sRGBTransferOETF with its implementation and got this image (alpha is better but antialias still not good):

Then I tried setting premultipliedAlpha: false when creating the renderer and got this image with the default smaa shader:

This image is close to the one obtained with the wrong sRGB input (the best one untill now), although antialiasing on black parts is not so good:

Screenshot 2023-10-10 165919

I’m using postprocessing (ssao) and my goal is to have a good antialias without affecting performance too much. I first tried using a multisampled render target (4 samples) with this result:

The white outline is present here too, and antialias is worse than when using the renderer without effect composer. Should I open another thread for this topic?

Maybe you are not using the latest version of three.js?

I’m using r156

sRGBTransferOETF() was introduced with r157^^.

@dllb Keep in mind that SSAA renders the entire scene multiple times (depending on how many samples you use) whereas SMAA as FXAA requires only one beauty pass. So it’s always a tradeoff between quality and performance.