Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FXAA: Improve quality. #29524

Merged
merged 14 commits into from
Oct 5, 2024
Merged

FXAA: Improve quality. #29524

merged 14 commits into from
Oct 5, 2024

Conversation

swift502
Copy link
Contributor

@swift502 swift502 commented Sep 28, 2024

Issue

I believe the current FXAA implementation is broken, and this PR attempts to fix it.

In 2022, a new version of FXAA was merged, which exhibits inferior quality compared to the previous version. #23768

It's more blurry.
cmp1

And has worse edge recognition.
cmp2

Solution

The MIT-0 FXAA implementation by Jasper Flick.
https://catlikecoding.com/unity/tutorials/advanced-rendering/fxaa/
https://catlikecoding.com/unity/tutorials/license/

  • correct FXAA implementation
  • clean code, unlike 2018
  • based on the higher quality 28th quality preset, compared to previous 12th. This mainly means it will pick up on more details in low contrast areas.
  • supports transparent backgrounds, just like the 2022 version

2022 downgrade visible on the official example.
cmp3

Proposed shader matches 2018 in visual fidelity.
cmp4

Performance considerations

Higher quality means more computation. We're jumping from 5 samples to 10 and have lower contrast thresholds. If this is a concern, let me know. I haven't done any tests so can't confirm how much more expensive this implementation is.

We could even edit the shader to match the 2018 performance and quality, if desirable.

Edit: See #29524 (comment) for performance testing.

@swift502
Copy link
Contributor Author

swift502 commented Sep 29, 2024

Got it on WebGPU. Needs code review but it works.
Let me know if you think this PR is a good idea or no.

cmp5

@swift502 swift502 marked this pull request as ready for review September 29, 2024 15:18
@Mugen87
Copy link
Collaborator

Mugen87 commented Sep 29, 2024

This needs a very close review since the original developer of the latest version is very experienced in this area and we had no complains about the anti-aliasing quality so far. It might take a while unit I can provide feedback for this PR.

@swift502
Copy link
Contributor Author

swift502 commented Sep 29, 2024

@Mugen87 Sure thing. I'm open to being wrong. I just noticed a change in quality immediately.

@swift502
Copy link
Contributor Author

swift502 commented Sep 29, 2024

Sorry, last piece of evidence, just to cover my base. Here's a scene from the infamously aliased game Kentucky Route Zero, with the two shaders applied. You can check out the results yourself.

Original image
2022 shader
Proposed shader

Current shader completely fails to recognize these areas. Keep in mind that that's supposed to be the primary function of this shader.
image

While areas it does recognize exhibit dithering artifacts.
image

Same here, current completely fails in the background, while overblurring the car windows.
cmp6

When the contrast threshold is brought down, a lot more edges are rocognized, but the quality is just not the same as the previous algorithm.
cmp7

I rest my case. I'll show myself out, I'm too invested in this. 😄
Again, if someone knows how to just fix the current shader, by all means let's do that. But I hope I've shown compelling evidence that it's not working as well as the previous implementation has.

@mrdoob
Copy link
Owner

mrdoob commented Sep 30, 2024

The proposed version sure looks much better!

@mrdoob
Copy link
Owner

mrdoob commented Sep 30, 2024

I also wouldn't mind reverting to the 2018 version. I don't think alpha support is that important?

@swift502
Copy link
Contributor Author

swift502 commented Sep 30, 2024

@Mugen87

the original developer of the latest version is very experienced in this area

More context from the original developer:
#23768 (comment)

@mrdoob I think we could easily introduce alpha support to the 2018 version.
But how important is WebGPU? The 2018 version is incredibly long, and would be very tedious to port. (Unless you guys have an automated way of doing it)

@swift502
Copy link
Contributor Author

swift502 commented Sep 30, 2024

On top of being very long, 2018 also contains a lot of unused code. All of these were motivations to choose an implementation which has the same fidelity, but is clean and maintainable, like the one I'm proposing.

@Mugen87
Copy link
Collaborator

Mugen87 commented Sep 30, 2024

I don't think alpha support is that important?

The community frequently asked for alpha support in context of FX. Since MSAA as well as our manual SSAA support alpha, I think's good to support it with FXAA as well.

@Mugen87
Copy link
Collaborator

Mugen87 commented Sep 30, 2024

I'm familiar with the current FXAA shader but I currently don't have the time to study the new code. However, I'm okay with supporting the proposed solution now if @swift502 is willing to support the new FXAA implementation.

@swift502
Copy link
Contributor Author

@Mugen87 As much as I'm able to. 👍

The code is from a tutorial where every line is explained. So I should be able to fix potential shader issues.

@Mugen87
Copy link
Collaborator

Mugen87 commented Sep 30, 2024

I've quickly checked the example at https://rawcdn.githack.com/swift502/three.js/fxaa/examples/index.html?q=fxaa#webgpu_postprocessing_fxaa and they look good so far.

Just one hint: Be aware that WebGPURenderer uses different NDC and uv conventions than WebGLRenderer so you might have to update the shader code accordingly to produce a 100% match. This details are sometimes easy to overlook and not implementing it right might not be visible in a demo scene.

  • NDC Z-range in WebGPU is [ 0, 1 ] and in WebGL it's [ -1, 1 ]
  • UV y is y.oneMinus() in WebGPU. That is relevant if you sample pixels "above" and "below" the current pixel which is typical for FXAA. That means you might have to switch the sign to sample the correct north and south pixel.

@Mugen87
Copy link
Collaborator

Mugen87 commented Sep 30, 2024

Check how these uv offsets in TSL differ to the GLSL/WebGLRenderer version:

const rgbaM = FxaaTexTop( uv ).toVar();
const rgbaS = FxaaTexOff( uv, vec2( 0.0, - 1.0 ), fxaaQualityRcpFrame.xy ).toVar();
const rgbaE = FxaaTexOff( uv, vec2( 1.0, 0.0 ), fxaaQualityRcpFrame.xy ).toVar();
const rgbaN = FxaaTexOff( uv, vec2( 0.0, 1.0 ), fxaaQualityRcpFrame.xy ).toVar();
const rgbaW = FxaaTexOff( uv, vec2( - 1.0, 0.0 ), fxaaQualityRcpFrame.xy ).toVar();
// . S .
// W M E
// . N .

vec4 rgbaM = FxaaTexTop(tex, posM);
vec4 rgbaS = FxaaTexOff(tex, posM, vec2( 0.0, 1.0), fxaaQualityRcpFrame.xy);
vec4 rgbaE = FxaaTexOff(tex, posM, vec2( 1.0, 0.0), fxaaQualityRcpFrame.xy);
vec4 rgbaN = FxaaTexOff(tex, posM, vec2( 0.0,-1.0), fxaaQualityRcpFrame.xy);
vec4 rgbaW = FxaaTexOff(tex, posM, vec2(-1.0, 0.0), fxaaQualityRcpFrame.xy);
// . S .
// W M E
// . N .

@swift502
Copy link
Contributor Author

swift502 commented Sep 30, 2024

That means you might have to switch the sign to sample the correct north and south pixel

I'll get onto fixing that.

Could you help me with how to pass objects (glsl structs) as parameters, and as return values?

// TODO: How to pass structs as parameters?
const pixelBlend = DeterminePixelBlendFactor( lm, ln, le, ls, lw, lne, lnw, lse, lsw, contrast );
const edge = DetermineEdge( texSize, lm, ln, le, ls, lw, lne, lnw, lse, lsw );
const edgeBlend = DetermineEdgeBlendFactor( texSize, lm, edge.x, edge.y, edge.z, edge.w, uv );

// TODO: How to return an object with multiple return values?
// isHorizontal should be bool, not float
return vec4( isHorizontal, pixelStep, oppositeLuminance, gradient );

@swift502
Copy link
Contributor Author

swift502 commented Sep 30, 2024

you might have to update the shader code accordingly to produce a 100% match.

I'm getting like 99.5% close to WebGL, like in the comparison I posted earlier. #29524 (comment)

Is that normal? The result also looks the same whether I flip the vertical coordinates or not.

I double checked by flipping the image (to check if the algorithm behaves differently upside down), and the results are always the same. So it seems to be correct?

@swift502
Copy link
Contributor Author

swift502 commented Oct 1, 2024

Performance

I did some benchmarks where I put a 1000x for loop in the shader's main function call. No idea how accurate that is, but I got some data.
The test scene looks like this:

image

Initially, this new shader was significantly slower (by 25%), so I've lowered the samples from 10 to 6, and now the shader is 10% faster than the 2022 implementation. (according to my test)

For reference 2018 used 4 samples, and 2022 is using 5.

Alpha

I double checked that alpha works, here it is rendering over html text

image

Other

  • cleaned up the TSL node. I's still not 1:1 port, but I can't figure out how to do glsl structs in TSL. If it can be done, I'd love to know. 🙏 @Mugen87
  • double checked the threshold constants, and raising them showed no performance benefits, suggesting they solely influence edge recognition accuracy. So I kept them where they are.

I'm personally happy with the code now. I'll be happy to do further changes based on feedback.

@Mugen87
Copy link
Collaborator

Mugen87 commented Oct 1, 2024

I'm getting like 99.5% close to WebGL, like in the comparison I posted earlier. #29524 (comment) Is that normal? The result also looks the same whether I flip the vertical coordinates or not.

We need a 100% match since there should be no difference. What I did when porting FXAA to TSL and encountered a similar issue, I literally compared line by line the TSL and GLSL version by assigning the current result values of a line to frag color. In that way I eventually found the difference which I believe was a missing one-minus computation in TSL.

Sorry but there should be no pixel differences like in #29524 (comment). You can easily see them at the bottom of the triangle.

@swift502
Copy link
Contributor Author

swift502 commented Oct 1, 2024

@Mugen87 Have you ever encountered problems with number precision? Everything is the same until it hits a greater than comparison.

e.isHorizontal = horizontal >= vertical;

vs

const isHorizontal = horizontal.greaterThanEqual( vertical );

where suddenly some pixels flip on/off differently, when horizontal and vertical have similar values.

@mrdoob
Copy link
Owner

mrdoob commented Oct 2, 2024

/cc @sunag

@mrdoob mrdoob added this to the r170 milestone Oct 2, 2024
@swift502
Copy link
Contributor Author

swift502 commented Oct 2, 2024

@Mugen87 I unflipped the shader because now it produces 100% match even upside down. And this way it doesn't need the uv flipping hacks, and is a 1:1 port with glsl.

Is that fine? It means "north" refers to the down direction now. But basically that's a naming issue.

@Mugen87
Copy link
Collaborator

Mugen87 commented Oct 2, 2024

TBH, I find that confusing. The logic/naming should be correct even if the implementation differs from GLSL.

Keep in mind that at some point WebGLRenderer will be deprecated as well as EffectComposer and FXAAShader. So the TSL implementation should be the leading one and coherent in any event.

@swift502
Copy link
Contributor Author

swift502 commented Oct 2, 2024

Alright I'll fix it.

@swift502
Copy link
Contributor Author

swift502 commented Oct 2, 2024

Corrected. I got nothing else to add. 👌

These new shaders should be marginally faster, should be much more faithful to the FXAA white paper specification, and provide results much closer to Nvidia's reference implementations of FXAA.
I think Daniel Sturk's implementation could still be presented under a different name as an alternative to FXAA, since some people may prefer it, and it was already ported to TSL.

And thanks a lot for the help @sunag. 🙏

@mrdoob
Copy link
Owner

mrdoob commented Oct 3, 2024

I think we should just have one version.
If someone complains that it looks different we can think about what to do then.

@Mugen87 Mugen87 changed the title FXAA Update proposal FXAA: Improve quality. Oct 5, 2024
@sunag sunag merged commit f22effa into mrdoob:dev Oct 5, 2024
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants