Sleek Render - Lightning Fast Mobile Post Processing Effects

Hi @Kumo-Kairo , I just bought this, thanks for making it.

I have a somewhat stylised requirement- I want to make the glow affect less saturated colors less, so before the threshold multiply the luminance by the saturation. The attached image is what I mean.

How should I go about doing this in your code?

On another note, I’d recommend updating the sample images in the asset store as I spent quite a while making sure this plugin did in fact do non-anamorphic bloom. You don’t want to undersell your great work!

3898675--331816--GlowWithSaturation.png

This is not currently possible out of the box (it’s not possible using standard Post Processing Stack either unless used with HDR rendering) as it requires advanced graphics pipelining which masks objects using custom alpha values or using dual-pass logic like in Tron 2.0 (http://developer.download.nvidia.com/books/HTML/gpugems/gpugems_ch21.html)
It seems to be a popular request, which is unfortunately not really possible to solve in a generic way that is also performant on mobile, so I will most likely add a how-to tutorial on this with customizing shaders used in a project, please stay tuned.

It’s totally possible. The “hardest” part of it is calculating saturation (converting RGB to HSV) - it’s a pretty costly operation. There are other color profiles that use saturation like YCrC or YCoCg (chroma there is stored in two channels) and they are usually easier to get from RGB than HSV. Contact me on support e-mail to discuss this in more detail for your specific case.

Thanks for the advice, I will be sure to update the screenshots.

Heya,

I’m a PlayMaker user and i’d like to write my own action that lets me control color value in the colorize module. I’ve written a few actions before but i’d really appreciate some tips as to the scripting syntax I should be using.

Cheers,

Simon

Hi @GrooGadgets
Writing custom actions for Playmaker to control Sleek Render is a trivial task - just follow the PlayMaker manual to hook up a reference field and tweak the settings values from your action code. There’s nothing special or different from the standard custom action creation process here, so any general tutorial on PlayMaker actions would do.

any example code to access Sleek Parameter via Scirpt ?
Actually i wish to change value of Vignette Color from black to red when player dies…

@mbhagat it’s the same as tweaking Standard Post Processing stack parameters. Check the 4. Script Control section in the ReadMe file

Hello,

Thanks for the great asset. I have a feature request for the bloom effect.

Right now bloom has very high saturation and for this reason it doesn’t fit our game very well.
For example, bloom effect on a red surface is (that is lit by white light) is red. With unity’s post-processing stack it’s reddish white. I think that’s also how it happens in real life.

That’s why I wish to request an option to set the saturation value of the bloom.
If the value would normally be (255,0,0) with bloom, When saturation is fully reduced it shouldn’t be (85,85,85) but it should become something like (235,235,235)
And for (255,255,0) I guess it should become something like (250,250,250)

When saturation is in the middle setting, it should turn to full white on areas where bloom effect is most intense. And where it’s less intense it should keep the saturated color.
I think that’s how it works with unity’s post-processing and also in real life. But if it’s very costly on performance a simple saturation slider can also work.

Thanks.

@Meceka thanks for your feedback.
Yes, real-world lights work like you’ve described - we observe desaturation when brighness increases (while physically leaving the light source with the original color), and it usually happens not only on blooming objects, but with bright objects in general. This is actually how Unreal Engine handles their color profiles and tonemapping (and this is how our eyes actually work, where certain colors sensitive cells “overlap” in their response and tend to “leak out” on other “channels” when light is too bright).

Although it sounds like a total win-win (in the end don’t we all want more realistic effects?), it’s actually pretty project-specific. In fact, you’re the second person to tell me about this brightness-saturation thing.

I’m not exactly sure how Unity does this right now, but last time I checked it relied on HDR quite a lot, which makes it much easier to handle overly bright colors through tonemapping.
LDR, on the other hand, has an inherent limitation - it’s pretty limited in terms of “raw” luma calculation as we won’t be able to rely on that luma value if only one channel is maxed out and others with zero. We will have to convert RGB space to HSV (or some other sort of chroma-luma color space where these things are separate and color values aren’t tied with saturation), which is usually quite costly and requires a lot of ALU clock cycles.

And while it seams like an unsolvable problem for a general case, it can be hacked a bit to work on specific project to get a specific look. Like you’ve said, we can desaturate bloom separately. You can actually do some extra stuff to approximate that corner cases of high saturation with low overall “lightness” (luma) like (1, 0, 0) with some simple almost-sigmoid function that boosts luma values quickly after some value and then leaving it as it is, not making any brighter to avoid hard edges of white color.
As bloom is calculated only on downscaled render targets, it’s possible to do this work at a lower cost than running an actual Desaturation effect on the whole picture.
It’s quite a specific case and I can help you add these things up to your local Sleek Render copy if you want to get into shaders a bit. I definitely won’t add this to the main package in nearest future as it’s pretty project-specific and will just increase overall cognitive load on users increasing confusion.

Hello,

I think you can add this option as a slider and with a shader variant. (multi compile) So that it wouldn’t cost performance to users that want to use the saturated bloom. (as it is now)

It took me some time to understand why your bloom looks unrealistic compared to Unity’s post process bloom. And I could find the reason after spending some time comparing the effects. The reason is this issue.
By this, I mean that most developers wouldn’t actually realize that high saturation is an issue without comparing it to a more realistic bloom effect. I think it’s not that project specific and you can make lot’s of developers happier by adding an update that adds an option to improve the realism of the effect.

And I didn’t really understand the reasoning behind why it would cause confusion. Every developer I know likes tinkering with such sliders :wink:

I worked with surface shaders before, but I don’t know about luma or post-processing. I’m not sure what you would recommend for modification, but I can give a try to modify the shader with your directions.

Doing such local modifications on assets can be very time-consuming for developers because when asset-developer updates the asset, you are either stuck with the old version or you need to re-do same stuff again every time for updates.

1 Like

I also think @Meceka 's idea would be nice to have built-in into sleek. I had to do some changes to sleek (a “low-pass” filter of sorts) and would rather not make any more local changes.

You would be surprised how many seemingly-trivial and simple questions I get on everyday basis and how simple changes can cause quite a lot of confusion for such users. Befure I started making packages I didn’t even think there would be some problems with it (it’s completely unintuitive for me and for most experienced developers that I know)

Tinkering with assets is usually incouraged - there’s just no good-enough general tool that will suit everyone. Most of professional commertial projects I have worked on (or consulted) customized most of the things that related to core functionality (especially graphics). I personally consider Sleek Render a good ground point for commercial projects that is relatively easy to extend or change.

@Meceka , @SugoiDev I got your feedback and will consider implementing it as core functionality in future versions. It would be best to receive some screenshots of your projects, something like before-after, or just the desired output compared to current implementation so I’ll be able to actually represent the desired result.

Cheers :slight_smile:

@Kumo-Kairo I’ll see if I can get some shots of my current changes. They are not directly related to this feature request, though. What I did was to make it Sleek only apply bloom to HDR colors with high enough exposure setting. Our current game is very colorful, but also has lots of very white characters (ghosts), so the default implementation was making the whites too overwhelming and the artists could not get to the same effect she had with the PPS.

Specifically, this is what I did on the getTapAndLumaFrom
I get the luminance from the tap, and use it to decide whether to apply bloom or not.
It’s far from optimized and more of a proof-of-concept so my artist could decide if Sleek was able to give the effect she wanted, or if we should continue with Unity’s PostProcess stack. In the end, Sleek won and it seems we’ll be using it!

    //Only HDR stuff (components > 1) contribute to bloom.
    //this is the luminance of this tap.rgb
    //it's the Y from YCbCr
    //it can tell us how "white" the color is

    half Y = 0.2126*tap.r + 0.7152*tap.b + 0.0722*tap.b;
  
    //if the color is almost white (0.8 out of 1, but NOT HDR, ie, not over 1, then we don't apply bloom to it)
    //this prevents "whiter" colors from becoming too overwhelming (like Ghosty's white color)
    //it also gives the chance for other colors to glow prettier
    if (Y > 0. && Y <= 1) {
        return half4(0, 0, 0, 0);
    }

    //now, we only consider colors over the threshold to contribute to bloom
    //this could be just half3(1, 1, 1), but using luminanceThreshold makes it better to configure in the inspector
    tap.rgb = tap.rgb - luminanceThreshold.rgb;
    //we call those max below because, if a component was smaller than the threshold's component, then
    //that component would now be negative
    //In that case, it was below the threshold, so we want to treat it as zero
    tap.r = max(0, tap.r);
    tap.g = max(0, tap.g);
    tap.b = max(0, tap.b);
2 Likes

Hello @Kumo-Kairo

Great to hear that. I will send you some example screenshots this week that shows the desired output.

1 Like

Hello. I’m really sorry for the delay. Here are the screenshots.

Edit: Bloom settings
4058497--353335--upload_2019-1-3_13-8-48.png

No Bloom

Unity Post Process V1 Bloom

Sleek Bloom

No Bloom

Unity Post Process V1 Bloom

Sleek Bloom

@Meceka thanks for the screenshots, very informative. I will look into it, stay tuned

Hi @Kumo-Kairo, thanks for your excellent work.

I noticed that this reference Post Process Mobile Performance : Alternatives To Graphics.Blit , OnRenderImage ? has mentioned something like “Don’t use OnRenderImage(), use OnPreRender() & OnPostRender() with custom targetTexture instead” in order to avoid CPU ReadPixel().

Have you tried that? Since it’s still OnRenderImage() in Sleek Postprocess.

Sleek Render initially used OnPostRender with a custom target texture (to accomodate for future “main render downscaler” feature), but that implementation made it incompatible with other image effects placed on the same camera - Sleek Render users kept using other effects on the same camera, which is not really good in terms of performance, but user’s desicions are out of my hands.
This is why I have “fallen back” to OnRenderImage after I’ve done quite a lot of profiling (both in terms of CPU usage and natively using some GPU-specific profilers like PerfHUD ES, Adreno Profiler, PVRTrace etc.) - turned out that OnRenderImage doesn’t have any CPU pixel copies, nor does it have additional “identity” GPU blits “before every OnRenderImage” as people sometimes tend to say on these forums (you can easily check that yourself using some native mobile GPU profilers). In the end, this OnRenderImage approach turned out to be the same in terms of performance as the OnPostRender as the underlying processes for one package with a custom way of combining effects (which Sleek Render is) are completely the same.
OnPostRender approach is essential if you want to make a 3D master downscale (so 3D scene is rendered at a fraction of the native screen resolution, leaving overlay UI unaffected), but it has nothing to do with how OnRenderImage works internally.
In general, there’s nothing internally slow in OnRenderImage, it’s just that common approach to chaining effects using OnRenderImage instead of some custom pipeline, as well as inability to make a downscaled 3D scene “view” that can lead to worse performance.

2 Likes

Hello, I just bought your package, but it seems that it doesn’t support HDR. I need the bloom effect to be on the object where I added HDR. I works with Unity’s post process stack but not your package, is there something I can change for it to work? the code you previsouly posted for getTapAndLumaFrom is it enabling HDR?
Thanks.

@madgreg I strongly discourage you from using HDR on mobile. Not only it’s buggy (device-specific black screens on certain GPUs due to graphics driver bugs and tonemapping implementation), it increases overall bandwidth by an incredible amount, nullifying all of the optimizations, making it slow as hell.
If you need to mask your blooming areas, there are a ton of other ways to do it without using HDR.
You can surely change the Sleek Render code by changing intermediate texture formats to some HDR type that your target devices might support and changing the shaders to work with HDR tonemapped values instead of normalized gamma.
But one can as well just use Unity’s Post Processing Stack with this setup because most of the stuff won’t even be supported on low-end devices anyway, and using Sleek Render with it would be like trying to close a breach on a Titanic hull with a finger.

1 Like