GammaCorrectionFilter

In old versions of JMonkeyEngine, gamma correction was implemented using the GammaCorrectionFilter class (in the “jme3-effects” library) to correct the displayed pixels.

In JMonkeyEngine v3.2, an AppSettings flag was added, which not only corrected the displayed pixels, but also linearized certain textures loaded from assets. At that time, GammaCorrectionFilter was deprecated, apparently to prod developers into using the AppSettings flag instead.

However, GammaCorrectionFilter was never deleted, and it still has valid uses. For instance, it was formerly used to provide contrast adjustment in SkyControl (see issue 781). It could also be used to implement gamma correction for frame buffers that aren’t sRGB-capable, such as Androids with OpenGL version < ES 3 (see issue 809).

Perhaps deprecation was a good idea at the time, but after four years, any developer still using the filter is unlikely to switch simply because it’s deprecated.

I’d like to see issue 781 resolved in v3.5. I’m considering various approaches. For instance:

  • simply un-deprecate the existing filter
  • delete the deprecated filter and create a new filter specifically designed for contrast adjustment (which could also be configured for gamma correction)

How should we proceed?

3 Likes

The second option has the advantage of not confusing new users. It’s a little confusing to have gamma at the application level and also have a filter and I think probably many new users will accidentally try to use both.

Ever since the days of Doom/Quake, to game players “gamma” = “brightness” and so game-players-trying-development might think they need the filter even when they don’t.

Edit: and the least amount of work would be to do nothing at all. Leave it deprecated and hopefully the new users described above would be given pause… and anyone else would hopefully look into the issue and make their own choice situationally.

6 Likes

I think the 2nd choice would be the righteous action too, to avoid unnecessary confusions about which to use (the config gamma correction or the application level gamma correction), changing the name to contrast adjustment filter is better, and better for documentations too.

For Android, I have tried gamma correction on OGLES3.2 through setting the FrameSRGB to true as I can remember but nothing happens ! May be I did a wrong maneuver, but reading more on this revealed that the underlying hardware may not support correcting colors (I am not sure since resources on embedded devices are somehow limited)

3 Likes

On mobile, I remember it seems that gamma correction can only be done through the post processor. For this, a FilterPost that does not support hardware gamma correction can be provided.

2 Likes

Hey guys, I’ve been notified by mail for this issue.
I think I’d chime in as I think this topic is misunderstood.
The GammaCorrectionFilter was a mistake in the first place. Gamma correction is not an aesthetic thing to make the image prettier, it’s a mandatory process that you need to have proper lighting calculation.
The idea is that any input that are in srgb space (textures, colors) must be converted to linear space before lighting calculation happens. Then at the end the final frame buffer is converted back to srgb space. That’s what the AppSetting flag does through an opengl extension. But the filter only does the last part, which makes no sense at all if you didn’t convert the input to linear space.

If the extension is not supported on android, the correct fallback would be to perform the conversion manually in the shaders → convert color texture fetch to linear space on each texture fetch (If I recall correctly this is done for the solid colors already in JME, but on the cpu side). Then the Gamma correction filter could be used to convert back the last framebuffer to srgb.
However Having an additional full screen pass just for this is not the most efficient thing to do, especially on android where fill rate still suck plenty. So maybe some thing more integrated to the FilterPostProcessor would be better (like inject a define in the last filter to do trigger the conversion).

If you need aesthetic image modification, you’d better implement a color balance filter, and a tone mapping filter which will do a way better job.

My 2 cents.
Thank you @sgold to have taken over all this.

12 Likes

Yes, the screen display will perform gamma decoding, and the human eye itself will also perform gamma decoding, that is to say, the shader output value has been gamma decoded twice from the screen to the human eye, so it is usually used when sampling the texture. Gamma-decode (usually gamma2.2 decoding) is performed in the shader, so that the sampled texture value is returned to gamma1.0 space (ie linear space), and then calculated after linear lighting, and gamma encoding (usually gamma1/2.2) is reused. The resulting value falls into the gamma encoding space, and the final screen will perform gamma decoding (usually gamma2.2), so that the value on the screen is gamma1.0, and then the value that enters the human eye is the value that the real shader needs to calculate , That is, the value of gamma1/2.2 is not implemented.

1 Like

I’ve waited years for a power-law contrast-adjustment filter.

Then the Gamma correction filter could be used to convert back the last framebuffer to srgb.

To me, that sounds like a perfectly valid use-case.

If you need aesthetic image modification, you’d better implement a color balance filter, and a tone mapping filter which will do a way better job.

Also a valid use-case. And while jme3-effects provides a ToneMapFilter, it implements a polynomial function with constant coefficients.

What I want is an un-deprecated tunable function, preferably a power-law, similar that built into Adobe Photoshop or GIMP. If it doesn’t have “gamma” in the name, that’s fine with me. But changing the name won’t prevent people from abusing it.

All the worry around providing an undeprecated power-law filter seems patronizing. It’s our job to document the fact that the filter isn’t the best option for gamma correction. If developers use it that way anyway, well, perhaps they know something we don’t.

Everywhere else in the Engine, we take the attitude of “developer beware”. 99% of the time, we don’t null-check method arguments. We provide setters that create aliases because that feature is occasionally useful to someone who knows exactly what they’re doing.

Providing a feature that could be used in a misguided attempt to implement gamma correction seems like a relatively small risk.

4 Likes

But if it’s not going to implement gamma correction then can we not call it something else?

1 Like

We can call it whatever we like, but we can’t prevent developers from setting the exponent to 2.2 in order to convert linear colors to sRGB.

Consensus seems to favor deleting the deprecated filter and replacing it with a new one.

If someone wants to write the PR, go ahead and self-assign the issue. Otherwise, it’s on my to-do list.

1 Like

For along while, I was having intentions to make custom filters but didn’t find the appropriate conditions, so I will try this time (study both custom filters on wiki + the Heart contrast filter), but I will postpone this to the next week (ofc if that won’t annoy you, currently I have ongoing exams, I take momentary breaks, but cannot do a full development work).

2 Likes

I have many other tasks on my list, and honestly I enjoy fixing bugs more than implementing features.

2 Likes

Yeah sometimes fixing existing bugs is more challenging and more tricky than a new feature, and usually new features introduce new bugs…keep doing the good work !

I have finished my exams today, and I will do some commits to the docs, and test the new CompatHarness before doing a PR, then I will start the ContrastAdjustmentFilter.

2 Likes

Hello @sgold , i was navigating the Heart today, but i cannot find these .vert files ?

1 Like

They are in jme3-effects/src/main/resources/Common/MatDefs/Post/

2 Likes

Alright, I have studied some glsl, Heart ContrastAdjusment, some j3md files, 3d color gamut and color reproduction :slight_smile: .

First of all, addressed questions are :

  • Do I really need to convert color.rgb to linear color space ?
    (Because that means finding XYZ rgb components for each color in the 3d color gamut with no reason since color.rgb is already a linear resultant vector).

  • What about having a Transfer Functions or Simple Tone Map operator for different channels ?, so something like :

color.r = Math.pow(color.r, exp_r,) + brightness
color.b = Math.pow(color.b, exp_b) + brightness
color.g = Math.pow(color.g, exp_g) + brightness

We could also handle those values (exp_r, exp_g, exp_b) from java to choose either to be similar or different.

Where brightness value would take the function up or down changing the brightness generally.

2 Likes
  • Do I really need to convert color.rgb to linear color space ?

No, the filter should take linear-color input and produce linear-color output.

What about having a Transfer Functions or Simple Tone Map operator for different channels ?

Having separate parameters for each channel is fine with me, if you think it would be useful.

Adjusting brightness with simple addition is likely to cause unwanted color clipping. Instead, I suggest applying low and high limits before the power law, thus:

     color.r = (color.r - m_LowInputLimit) / (m_HighInputLimit - m_LowInputLimit);
     color.r = max(color.r, 0)
     color.r = pow(color.r, m_Exponent);
2 Likes

So the result value of color.r would be scaled from 0% to a 100% ?

2 Likes

An input value of m_LowInputLimit would produce an output value of 0.
An input value of m_HighInputLimit would produce an output value of 1.

If we want to adjust the output range, I suggest doing that after the pow() call.

2 Likes

Brilliant, I think its fine, no need to scale it again after the power law.

Its okay to use low and high limits before the power law.

2 Likes