tzachshabtay / MonoAGS

AGS (Adventure Game Studio) reimagined in Mono
https://tzachshabtay.github.io/MonoAGS/
Artistic License 2.0
27 stars 8 forks source link

Question: making object's color brighter / less saturated #266

Closed ghost closed 6 years ago

ghost commented 6 years ago

So, I was experimenting with Tint a bit, and found that tint's alpha means not the opacity of applied tint, but rather than opacity of an object itself (along with its own Opacity).

Note, this is different to how tints worked in AGS, where tint had "saturation" argument, something like "tint's amount". Not sure it was mathematically correct alpha, but idea was that "tint" is like another layer of color applied over an object, which may be more or less opaque.

For example, in AGS I could Tint an object or character with RGB (255,255,255) and saturation 50%, and that would make character loose half of its original saturation (bit brighter and less colored).

In MonoAGS with the default tint corresponding to normal object image = 255,255,255,255, this means that using tint I can only make object darker.

Is there any means to reduce object's saturation? I am trying to make something look like it's far away, in a "fog".

tzachshabtay commented 6 years ago

I never understood the tinting model in AGS, frankly: no clue what the math there is. I am confused by the concepts. Saturation, how I understand it, is about how "gray" an image is, not how bright it is. Having opacity for the applied tint, is basically choosing a different color for the tint, isn't it? And MonoAGS gives you an option of setting saturation and brightness if you use HSLA instead of RGBA: obj.Tint = Color.FromHsla(...);

That said, it's true that in this model you can only go darker than the original image, that's because we use the tint as a multiplier for the texture color. Wild speculation time, but I remember you said that images in AGS appeared darker than the actual assets, maybe it's darker so that you can make it brighter? Anyway, as far as I know, this is the standard model used for setting a tint for an image. Unity, at least, seem to have the same problem: https://forum.unity.com/threads/how-do-you-make-a-sprite-glow-brighter.441371/ And currently the solution is the same: you can write a custom shader with a different tint model. If you take a look at the standard fragment shader: https://github.com/tzachshabtay/MonoAGS/blob/ec056b9d90c6c83ba9022fa22026de6695a20d7b/Source/Engine/AGS.Engine.Desktop/OpenGLBackend.cs#L173 vColor is the variable that holds the incoming tint, and col is the texture color, so you can replace col * vColor with your chosen math (and if you want a shader that is compatible with mobile as well, take a look at the example shaders in the demo game: https://github.com/tzachshabtay/MonoAGS/blob/master/Source/Demo/DemoQuest/Shaders.cs).

Thinking about how to make it built in to allow to brighten an image, and saw the answer here which says that we can pass a color brighter than white: https://gamedev.stackexchange.com/questions/73861/most-efficient-way-of-brighten-and-darken-sprites-with-opengl-2-0 I wouldn't want to allow the color structure itself to be brighter than white as it doesn't make sense, but maybe we can add a tint brightness modifier which will be applied to the color just before passing it to the shader. Doing this probably means we'll not be able to do the performance optimization laid out here: https://github.com/tzachshabtay/MonoAGS/issues/112

ghost commented 6 years ago

And MonoAGS gives you an option of setting saturation and brightness if you use HSLA instead of RGBA: obj.Tint = Color.FromHsla(...);

No, it's not that. This is just another way to define color, but the range of resulting tints, so to speak, is the same as with FromArgb.

What I am looking for is some kind of blending operation that desaturates image, but as you say Tint is using multiplication, then it is something different. (Some operation that would take object image's color and reduce its saturation but increase brightness.)

BTW, that reminds me, do you have plans for making a "blend operation" component or property for objects, or you leave that for shaders? This was one of the feature requests to AGS for years to have a selection of blends for an object.

PS. For the reference, this is how AGS tinting looks like in OpenGL shader: https://github.com/adventuregamestudio/ags/blob/master/Engine/gfx/ali3dogl.cpp#L635

tzachshabtay commented 6 years ago

(Some operation that would take object image's color and reduce its saturation but increase brightness.)

So I mentioned about how we can make the brightness built in, in the same way we can also add a saturation modifier, and add it to the built in shader (this looks like a good starting point: https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Shaders/Builtin/Functions/saturation.glsl). We can also add some more built-in modifiers, like some of the stuff that's in here: https://github.com/libretro/glsl-shaders/blob/master/misc/image-adjustment.glsl

At that point, though, it would be a waste to have all effects in the built-in shader when usually they will not be used, so we'd want this feature (https://github.com/tzachshabtay/MonoAGS/issues/47) to combine multiple shaders.

BTW, that reminds me, do you have plans for making a "blend operation" component or property for objects, or you leave that for shaders? This was one of the feature requests to AGS for years to have a selection of blends for an object.

Yes: https://github.com/tzachshabtay/MonoAGS/issues/97

I don't think you can really do blending modes in shaders alone (at least in OpenGL), as the result of the fragment shader is passed to the blending function in the OpenGL pipeline (which you set) which is outside of the scope of the shader.

For the basic blending modes, we might not even need to update the shader code, but just need to supply different component to the blending function (it's actually 3 different components that need to be set: blend src, blend dst and blend operation, iirc). For fancier blending modes, you might need to adjust both the blending function and the shader.

Another problem with the blending modes is how to make it work with pre-multiplied alphas: https://github.com/tzachshabtay/MonoAGS/issues/251 From what I've read about pre-multiplied alphas, at some cases you'd want to store the pre-multiplied alphas in the texture itself, at other cases you'd want the shader to pre-multiply the alpha, and sometimes you don't even want pre-multiplied alphas. Having pre-multiplied alphas also changes the blending function (and it doesn't matter if it's multiplied in the texture or in the shader), so whatever blending modes interface we supply (and the built-in modes we'll build around those interfaces) will need to know how to handle all of the situations (for example, they will need to query the texture to see if it's pre-multiplied or not and change the math based on that).

PS. For the reference, this is how AGS tinting looks like in OpenGL shader

Hmmm, noticed that tintHSV[2] is not being used at all in the shader code.

tzachshabtay commented 6 years ago

I've added a brightness property in this branch: https://github.com/tzachshabtay/MonoAGS/tree/Brightness

I've also added a demo for it in the features -> tweens panel. Can you take a look?

As for the saturation, as far as I can see, the existing FromHSLA already allows you to make the image less saturated (take a look at the saturation tween in the same panel and tell me what you think).

ghost commented 6 years ago

As for the saturation, as far as I can see, the existing FromHSLA already allows you to make the image less saturated (take a look at the saturation tween in the same panel and tell me what you think).

I already replied to this above, and I think there is a misunderstanding here. FromHSLA function simply lets you choose color in a different way. The question is in what changes are applied to source image pixels.

It was not easy to understand what is going on in the game, but from the code it's clear that the Saturation tween is changing the saturation in the tint, not saturation of original image pixels. Which is not the same thing.

I was not much sure about actual formulas, but suddenly found it is possible to see how saturation work by playing with color wheel in the image editor. It seems that saturation level is an opposite to level of uniformity (or greyness, as you said above), and when it ranges from 0 to 100, each color component interpolates between its original value and maximal component value.

To elaborate, if original color components have value R, G, B, and , for example, R is highest of all 3, then at saturation = 100 the color will be (R, G, B), and at saturation = 0 the color will be (R,R,R). At saturation = 50 the color is (R, (R - G / 2), (R - B / 2)). And so on.

By the way, this also means that for colors, which highest component is equal to 255, it is possible to achieve de-saturation effect using the Brightness property you added, because highest component won't be able to change further. It's just that saturation loss won't be accurately proportional to brightness parameter.