-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
Shader-based BLUR Filter Discrepancy in p5.js #6397
Comments
I think blur is the hardest filter to match exactly with CPU mode. The main difficulties trying to recreate the Gaussian blur were:
Happy to hear other thoughts and approaches though. It would be nice to have a better blur. |
Instead of having a hardcoded kernel, we could generate the kernel value dynamically per sample based on the distance from the center. I'm not sure how much of a performance impact this would have compared to using hardcoded values, but if it's not significant, it would avoid the complexity of managing different shaders. To loop over a variable number of pixels, to circumvent the issue where loops have to have constant bounds, I generally see people loop up to a max value (max radius, in this case) and then |
I asked Patricio Gonzales-Vivo (creator of Lygia and co-creator of the Book of Shaders). He shared the following insights:
I'd argue that accuracy should be essential since we're replacing the CPU blur with the GPU blur. I believe minimizing discrepancies would be beneficial. As for performance, we could test it and see how costly this type of blur is. I'm guessing it should still be significantly faster than CPU blur but some benchmarking would help 🤓 An example of all 2D shaders in Lygia, including 2D gausian blur, using p5.js can be found here: https://editor.p5js.org/patriciogonzalezvivo/sketches/XCkTzoyB3 |
Ah ok, thanks for the resources and ideas. I can take the next week or two to dig in and learn and see what I can come up with. |
Just one more resource that may be helpful: Taken from: http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html |
Here's an initial draft: https://editor.p5js.org/jwong/sketches/Cn2z2G-to But this is a single shader, single pass gaussian blur using the Note that performance drops kinda quickly. Some rough manual benchmarks from my machine:
|
I think the current implementation could be modified to continue using the two pass solution, which should keep things much more performant. Here's another implementation of the gaussian kernel setup for a two pass renderer |
Thanks @wong-justin and @aferriss! How do these two approaches compare with the CPU blur in terms of quality? |
Here's a few comparisons I put together, with the GPU blur on top and CPU on the bottom of each image. None of them match perfectly, and to be honest I think we will have a pretty difficult time matching the cpu blur exactly. I think we may need to make a tradeoff here between performance and visual appeal. That being said we can keep tinkering away at this and hopefully get as close as possible. I think of the options presented here the one I shared yesterday seems the closest, but would love to hear other opinions! |
@aferriss I agree that your version looks the best of the ones there. Another approach suggested here is to use a single precalculated kernel and apply the blur multiple times with two ping-pong framebuffers. I've made a quick attempt based on your sketch, setting the samples relatively low, but calling Edit: I also tried a version of the hardcoded kernel from the article: https://editor.p5js.org/SableRaf/sketches/NTy_zPyPX |
Another useful reference. This article does a systematic exploration of GPU blur, with regard to performance and quality across platforms: |
I've done further exploration using a shader to visualize the lightness as a graph, inspired by the figures in the article from VentureBeat. You can find the test sketch here: https://editor.p5js.org/SableRaf/sketches/6it1LpFq8 We can see that Adam's blur is pretty close but it has a bias towards the bottom right direction that becomes more visible at higher samples.
This is not a super rigorous benchmark but I hope it will be helpful. By the way, I'm testing in Chrome Version 116.0.5845.187 on macOS Ventura 13.4. CPU Blur
|
I think the offset in Adam's might be from this line: float x2 = (i - samples * 0.5); If we're using e.g.
If we instead use
This only works when |
Hey y'all, with some help from @davepagurek and others, I've built a more advanced benchmark for blur. Feel free to play around with it to get a better feel for the parameter space and performance. Hold click and move the cursor to see the profile change based on the radius and number of passes. Feel free to experiment and let me know what you think. Notes
Interactive demohttps://editor.p5js.org/SableRaf/sketches/KFye28jYn Instructions
ExamplesAdding extra passes helps with the smoothness of the shader blur. You can see that the sharp edges almost completely disappear when applying the blur twice. More passes help with the shape of the blur. The CPU blur does get a more rounded shape at higher radii but shows some discontinuities (unless those are caused by my graphing shader, but they don't appear on the GPU blur). To get this kind of roundedness on the GPU a higher number of passes is necessary. |
I dug a bit more into the code for the existing CPU blur:
Putting all those together into @SableRaf's scaffolding, I got this: https://editor.p5js.org/davepagurek/sketches/x6wNXyzxQ |
One more update: @SableRaf and I have been working on versions that don't drift to the side when the blur radius is greater than the max samples. Here's his with even spacing for 64 samples (we can always turn this up though): https://editor.p5js.org/SableRaf/sketches/QpkhsI94y Here's a fork of that that I added dithering to, still 64 samples: https://editor.p5js.org/davepagurek/sketches/3aD5WBynW |
Thanks @davepagurek that looks great! @aferriss and @wong-justin what do you think? Should we go with that last one? |
Looks great to me! The multiple pass solution was something we had also explored early on but abandoned in favor of something simple, which we could later revisit. It does seem like its more difficult to achieve parity with the CPU blur when using more than 1 pass. I like the dithering solution as well but it does add some barely perceptible noise. Personally I don't mind it but wonder how others feel. For the dithering shader, I've had better luck with this snippet in making more natural looking noise that doesn't create artifacts and doesn't use a sin function. Also can we move that random calculation outside the for loop and save some calculations?
Nice work everyone!! |
Do we have any easy ways of passing more than one parameter to a filter shader? I think letting people use more samples could make sense (e.g. for exports), but I'm not sure that we have a great API for it yet. |
Also I'll try adding your dithering changes after work 🙂 |
Currently you can only pass a single value to the shader, but there's no reason why it couldn't be extended to receive an object of parameters. I think the function signature is already a little complicated...
@wong-justin wrote this function to deal with the parameters Line 627 in 63f94b0
|
Here's an updated dither using @aferriss's random function: https://editor.p5js.org/davepagurek/sketches/fth7UYyXI I also experimented with making better use of the random function being inside the loop and actually using a different random number per sample, but honestly it didn't look any better. That's convenient at least, because we can take the random function out of the loop and have it run a bit faster! |
Looks great! I'd say the original issue is fully addressed by the latest shader version. Should we roll with that and leave the parameter discussion for a separate issue if necessary? |
I think that makes sense. @wong-justin were you already working on adding blur filter changes into your updates? If not I can make a PR with these changes. |
The latest demo looks fantastic! @davepagurek no I didn't have a PR for blur in progress, feel free to make it. |
Hi! Sorry for jumping a bit late to the party. bartlett
|
Increasing Access
Ensuring a consistent visual output across p5 versions reduces confusion and lessens cognitive load for both learners and long time users.
Most appropriate sub-area of p5.js?
Feature enhancement details
Note: this is a follow up to the conversation on #6324
First of all, many thanks to @wong-justin, @aferriss, and @aceslowman for the outstanding work on integrating shader-based filters in p5.js.
However, I've noticed a difference in the output of the
filter(BLUR)
. For clarity, I've put together a visual comparison using a small demo sketch available here. Additionally, here's the image derived from the demo:Generated on Chrome Version 116.0.5845.140 (Official Build) (arm64), MacOS Ventura 13.4 (22F66)
The newly added shader for
BLUR
uses a box blur, as opposed to the Gaussian blur in CPU mode. Might be worth checking if we can use a Gaussian kernel in the shader and what that means for performance. It'd be great to have outputs that are consistent, especially for older projects that were built around the original blur and whose outputs will look different with the new shader-based method.Looking forward to the community's insights on this.
The text was updated successfully, but these errors were encountered: