|
@@ -13,43 +13,38 @@ ifdef::env-github,env-browser[:outfilesuffix: .adoc]
|
|
|
Here is a quick overview of what lies under the “Gamma Correction term. +
|
|
Here is a quick overview of what lies under the “Gamma Correction term. +
|
|
|
More in depth rundowns on the matter can be found here : link:http://http.developer.nvidia.com/GPUGems3/gpugems3_ch24.html[http://http.developer.nvidia.com/GPUGems3/gpugems3_ch24.html] and here link:http://www.arcsynthesis.org/gltut/Texturing/Tutorial%2016.html[http://www.arcsynthesis.org/gltut/Texturing/Tutorial%2016.html]
|
|
More in depth rundowns on the matter can be found here : link:http://http.developer.nvidia.com/GPUGems3/gpugems3_ch24.html[http://http.developer.nvidia.com/GPUGems3/gpugems3_ch24.html] and here link:http://www.arcsynthesis.org/gltut/Texturing/Tutorial%2016.html[http://www.arcsynthesis.org/gltut/Texturing/Tutorial%2016.html]
|
|
|
|
|
|
|
|
-We consider color values to be linear when computing lighting. What does that means? That means that we assume that the color 0.5,0.5,0.5 is half way between black and white.+
|
|
|
|
|
-The problem is that it’s not the case, or at least not when you look at the color through a monitor.+
|
|
|
|
|
-CRT monitors had physical limitations that prevented them to have a linear way of representing colors. that means that 0.5,0.5,0.5 through a monitor is not half way between black and white (it’s darker). Note that black and white remains the same though.+
|
|
|
|
|
-+
|
|
|
|
|
-If we do not take that into account, the rendered images are overly darken and feels dull.+
|
|
|
|
|
-+
|
|
|
|
|
-LCD monitors still mimic this physical limitation (I guess for backward compatibility).+
|
|
|
|
|
-+
|
|
|
|
|
-*Output correct colors*+
|
|
|
|
|
-Gamma Correction is the technique that tends to correct the issue. Gamma is an power factor applied to the color by the monitor when lighting a pixel on screen (or at least a simplification of the function applied). So when we output the color, we have to apply the inverse power of this factor to nullify the effect : finalColor = pow(computedColor, 1/gamma);+
|
|
|
|
|
-
|
|
|
|
|
-*Knowing what colors we have as input*+
|
|
|
|
|
|
|
+We consider color values to be linear when computing lighting. What does that means? That means that we assume that the color 0.5,0.5,0.5 is half way between black and white. +
|
|
|
|
|
+The problem is that it’s not the case, or at least not when you look at the color through a monitor. +
|
|
|
|
|
+CRT monitors had physical limitations that prevented them to have a linear way of representing colors. that means that 0.5,0.5,0.5 through a monitor is not half way between black and white (it’s darker). Note that black and white remains the same though. +
|
|
|
|
|
+If we do not take that into account, the rendered images are overly darken and feels dull. +
|
|
|
|
|
+LCD monitors still mimic this physical limitation (I guess for backward compatibility). +
|
|
|
|
|
+*Output correct colors* +
|
|
|
|
|
+Gamma Correction is the technique that tends to correct the issue. Gamma is an power factor applied to the color by the monitor when lighting a pixel on screen (or at least a simplification of the function applied). So when we output the color, we have to apply the inverse power of this factor to nullify the effect : finalColor = pow(computedColor, 1/gamma); +
|
|
|
|
|
+
|
|
|
|
|
+*Knowing what colors we have as input* +
|
|
|
The other aspect of gamma correction is the colors we get as input in the rendering process that are stored in textures or in ColorRGBA material params. Almost all image editors are storing color data in images already gamma corrected (so that colors are correct when you display the picture in a viewer of in a browser). Also most hand picked colors (in a color picker) can be assumed as gamma corrected as you most probably chose this color through a monitor display.
|
|
The other aspect of gamma correction is the colors we get as input in the rendering process that are stored in textures or in ColorRGBA material params. Almost all image editors are storing color data in images already gamma corrected (so that colors are correct when you display the picture in a viewer of in a browser). Also most hand picked colors (in a color picker) can be assumed as gamma corrected as you most probably chose this color through a monitor display.
|
|
|
Such images or color are said to be in sRGB color space, meaning “standard RGB” (which is not the standard one would guess).
|
|
Such images or color are said to be in sRGB color space, meaning “standard RGB” (which is not the standard one would guess).
|
|
|
That means that textures and colors that we use as input in our shaders are not in a linear space. The issue is that we need them in linear space when we compute the lighting, else the lighting is wrong.
|
|
That means that textures and colors that we use as input in our shaders are not in a linear space. The issue is that we need them in linear space when we compute the lighting, else the lighting is wrong.
|
|
|
To avoid this we need to apply some gamma correction to the colors : (pow(color, gamma);
|
|
To avoid this we need to apply some gamma correction to the colors : (pow(color, gamma);
|
|
|
This only apply to textures that will render colors on screen (basically diffuse map, specular, light maps). Normal maps, height maps don’t need the correction.
|
|
This only apply to textures that will render colors on screen (basically diffuse map, specular, light maps). Normal maps, height maps don’t need the correction.
|
|
|
|
|
|
|
|
-this is the kind of difference you can have :+
|
|
|
|
|
-left is non corrected output, right is gamma corrected output.+
|
|
|
|
|
|
|
+This is the kind of difference you can have : +
|
|
|
|
|
+left is non corrected output, right is gamma corrected output. +
|
|
|
image:http://i.imgur.com/uNL7vw8.png[uNL7vw8.png,width="",height=""]
|
|
image:http://i.imgur.com/uNL7vw8.png[uNL7vw8.png,width="",height=""]
|
|
|
-+
|
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
|
|
=== Implementation
|
|
=== Implementation
|
|
|
|
|
|
|
|
* To handle proper gamma corrected ouput colors, Opengl expose an ARB extension that allows you to output a color in linear space and have the GPU automatically correct it : link:https://www.opengl.org/registry/specs/ARB/framebuffer_sRGB.txt[https://www.opengl.org/registry/specs/ARB/framebuffer_sRGB.txt]
|
|
* To handle proper gamma corrected ouput colors, Opengl expose an ARB extension that allows you to output a color in linear space and have the GPU automatically correct it : link:https://www.opengl.org/registry/specs/ARB/framebuffer_sRGB.txt[https://www.opengl.org/registry/specs/ARB/framebuffer_sRGB.txt]
|
|
|
-
|
|
|
|
|
-* To handle the input, instead of classic RGBA8 image format, one can use SRGB8_ALPHA8_EXT which is basically RGBA in sRGB. Using this you specify the GPU that the texture is in sRGB space and when fetching a color from it, the GPU will linearize the color value for you (for free). There are sRGB equivalent to all 8 bits formats (even compressed format like DXT).+
|
|
|
|
|
-
|
|
|
|
|
|
|
+* To handle the input, instead of classic RGBA8 image format, one can use SRGB8_ALPHA8_EXT which is basically RGBA in sRGB. Using this you specify the GPU that the texture is in sRGB space and when fetching a color from it, the GPU will linearize the color value for you (for free). There are sRGB equivalent to all 8 bits formats (even compressed format like DXT).
|
|
|
* But all textures don't need this. For example, normal maps, height maps colors are most probably generated and not hand picked by an artist looking through a monitor. The implementation needs to account for it and expose a way to exclude some textures from the sRGB pipeline.
|
|
* But all textures don't need this. For example, normal maps, height maps colors are most probably generated and not hand picked by an artist looking through a monitor. The implementation needs to account for it and expose a way to exclude some textures from the sRGB pipeline.
|
|
|
|
|
|
|
|
Gamma Correction in jME 3.0 is based on those three statements.
|
|
Gamma Correction in jME 3.0 is based on those three statements.
|
|
|
|
|
|
|
|
[IMPORTANT]
|
|
[IMPORTANT]
|
|
|
====
|
|
====
|
|
|
-Note that Gamma Correction is only available on desktop with LWJGL or JOGL renderer. They are not yet supported on Android or iOS renderers
|
|
|
|
|
|
|
+Note that Gamma Correction is only available on desktop with LWJGL or JOGL renderer. They are not yet supported on Android or iOS renderers.
|
|
|
====
|
|
====
|
|
|
|
|
|
|
|
|
|
|
|
@@ -68,7 +63,7 @@ app.setSettings(settings);
|
|
|
|
|
|
|
|
----
|
|
----
|
|
|
|
|
|
|
|
-This setting is also exposed in the Settings dialog displayed when you launch a jME application.+
|
|
|
|
|
|
|
+This setting is also exposed in the Settings dialog displayed when you launch a jME application. +
|
|
|
image:http://i.imgur.com/Lya1ldH.png[Lya1ldH.png,width="400",height=""]
|
|
image:http://i.imgur.com/Lya1ldH.png[Lya1ldH.png,width="400",height=""]
|
|
|
|
|
|
|
|
|
|
|
|
@@ -82,7 +77,7 @@ This is a short hand to enable both linearization of input textures and Gamma co
|
|
|
|
|
|
|
|
===== Enabling output Gamma Correction
|
|
===== Enabling output Gamma Correction
|
|
|
|
|
|
|
|
-You can enable or disable the Gamma correction of the rendered output by using
|
|
|
|
|
|
|
+You can enable or disable the Gamma correction of the rendered output by using:
|
|
|
|
|
|
|
|
[source,java]
|
|
[source,java]
|
|
|
----
|
|
----
|
|
@@ -92,7 +87,7 @@ renderer.setMainFrameBufferSrgb(boolean srgb)
|
|
|
This will be ignored if the hardware doesn't have the GL_ARB_framebuffer_sRGB or the GL_EXT_texture_sRGB.
|
|
This will be ignored if the hardware doesn't have the GL_ARB_framebuffer_sRGB or the GL_EXT_texture_sRGB.
|
|
|
This can be toggled at run time.
|
|
This can be toggled at run time.
|
|
|
|
|
|
|
|
-This uses Opengl hardware gamma correction that uses an approximated Gamma value of 2.2 and uses the following formula : color = pow(color,1/gamma)+
|
|
|
|
|
|
|
+This uses Opengl hardware gamma correction that uses an approximated Gamma value of 2.2 and uses the following formula : color = pow(color,1/gamma) +
|
|
|
Not that this will not yield exact results, as the real gamma can vary depending on the monitor. +
|
|
Not that this will not yield exact results, as the real gamma can vary depending on the monitor. +
|
|
|
If this is a problem, please refer to the “handling gamma correction in a post process section.
|
|
If this is a problem, please refer to the “handling gamma correction in a post process section.
|
|
|
|
|
|
|
@@ -115,7 +110,7 @@ Toggling this setting at runtime will produce unexpected behavior for now. A cha
|
|
|
|
|
|
|
|
|
|
|
|
|
All images marked as in sRGB color space will be uploaded to the GPU using a sRGB image format.
|
|
All images marked as in sRGB color space will be uploaded to the GPU using a sRGB image format.
|
|
|
-Opengl hardware texture linearization also uses an approximated Gamma value of 2.2 and linearize the fetched texel color using the following formula : color = pow(color, gamma)+
|
|
|
|
|
|
|
+Opengl hardware texture linearization also uses an approximated Gamma value of 2.2 and linearize the fetched texel color using the following formula : color = pow(color, gamma) +
|
|
|
As with output gamma correction this will not give exact result, but the error is less important since most image editor uses the same approximation to correct images and save them in sRGB color space.
|
|
As with output gamma correction this will not give exact result, but the error is less important since most image editor uses the same approximation to correct images and save them in sRGB color space.
|
|
|
|
|
|
|
|
Not all image format have their sRGB equivalent, and only 8bit formats.
|
|
Not all image format have their sRGB equivalent, and only 8bit formats.
|
|
@@ -145,16 +140,15 @@ Conventionally only the rgb channels are gamma corrected, as the alpha channel d
|
|
|
|
|
|
|
|
[IMPORTANT]
|
|
[IMPORTANT]
|
|
|
====
|
|
====
|
|
|
-Only loaded images will be marked as in sRGB color space, when using assetManager.loadTexture or loadAsset.+
|
|
|
|
|
|
|
+Only loaded images will be marked as in sRGB color space, when using assetManager.loadTexture or loadAsset. +
|
|
|
The color space of an image created by code will have to be specified in the constructor or will be assumed as Linear if not specified.
|
|
The color space of an image created by code will have to be specified in the constructor or will be assumed as Linear if not specified.
|
|
|
====
|
|
====
|
|
|
|
|
|
|
|
|
|
|
|
|
-Not all images need to be linearized. Some images don't represent color information that will be displayed on screen, but more different sort of data packed in a texture.+
|
|
|
|
|
|
|
+Not all images need to be linearized. Some images don't represent color information that will be displayed on screen, but more different sort of data packed in a texture. +
|
|
|
The best example is a Normal map that will contains normal vectors for each pixel. Height maps will contain elevation values. These textures must not be linearized.
|
|
The best example is a Normal map that will contains normal vectors for each pixel. Height maps will contain elevation values. These textures must not be linearized.
|
|
|
|
|
|
|
|
-There is no way to determine the real color space of an image when loading it, so we must deduce the color space from the usage it's loaded for.
|
|
|
|
|
-The usage is dictated by the material, those textures are used for, and by the material parameter they are assigned to.
|
|
|
|
|
|
|
+There is no way to determine the real color space of an image when loading it, so we must deduce the color space from the usage it's loaded for. The usage is dictated by the material, those textures are used for, and by the material parameter they are assigned to.
|
|
|
One can now specify in a material definition file (j3md) if a texture parameter must be assumed as in linear color space, and thus, must not be linearized, by using the keyword -LINEAR next to the parameter (case does not matter).
|
|
One can now specify in a material definition file (j3md) if a texture parameter must be assumed as in linear color space, and thus, must not be linearized, by using the keyword -LINEAR next to the parameter (case does not matter).
|
|
|
|
|
|
|
|
For example here is how the NormalMap parameter is declared in the lighting material definition.
|
|
For example here is how the NormalMap parameter is declared in the lighting material definition.
|
|
@@ -167,8 +161,7 @@ For example here is how the NormalMap parameter is declared in the lighting mate
|
|
|
|
|
|
|
|
----
|
|
----
|
|
|
|
|
|
|
|
-When a texture is assigned to this material param by using material.setTexture(“NormalMap, myNormalTexture), the color space of this texture's image will be forced to linear.
|
|
|
|
|
-So if you make your own material and want to use Gamma Correction, make sure you properly mark your textures as in the proper color space.
|
|
|
|
|
|
|
+When a texture is assigned to this material param by using material.setTexture(“NormalMap, myNormalTexture), the color space of this texture's image will be forced to linear. So if you make your own material and want to use Gamma Correction, make sure you properly mark your textures as in the proper color space.
|
|
|
|
|
|
|
|
This can sound complicated, but you just have to answer this question : Does my image represent color data? if the answer is no, then you have to set the -Linear flag.
|
|
This can sound complicated, but you just have to answer this question : Does my image represent color data? if the answer is no, then you have to set the -Linear flag.
|
|
|
|
|
|
|
@@ -185,8 +178,12 @@ The r, g, b attributes of a ColorRGBA object are *ALWAYS* assumed in Linear colo
|
|
|
|
|
|
|
|
If you want to set a color that you hand picked in a color picker, you should use the setAsSRGB method of ColorRGBA. This will convert the given values to linear color space by using the same formula as before : color = pow (color, gamma) where gamma = 2.2;
|
|
If you want to set a color that you hand picked in a color picker, you should use the setAsSRGB method of ColorRGBA. This will convert the given values to linear color space by using the same formula as before : color = pow (color, gamma) where gamma = 2.2;
|
|
|
|
|
|
|
|
-If you want to retrieve those values from a ColorRGBA, you can call the getAsSRGB method. The values will be converted back to sRGB color Space.+
|
|
|
|
|
-Note that the return type of that method is a Vector4f and not a ColorRGBA, because as stated before, all ColorRGBA objects r,g,b attributes are assumed in Linear color space.
|
|
|
|
|
|
|
+If you want to retrieve those values from a ColorRGBA, you can call the getAsSRGB method. The values will be converted back to sRGB color Space.
|
|
|
|
|
+
|
|
|
|
|
+[NOTE]
|
|
|
|
|
+====
|
|
|
|
|
+The return type of that method is a Vector4f and not a ColorRGBA, because as stated before, all ColorRGBA objects r,g,b attributes are assumed in Linear color space.
|
|
|
|
|
+====
|
|
|
|
|
|
|
|
|
|
|
|
|
==== Handling rendered output Gamma Correction with a post process filter
|
|
==== Handling rendered output Gamma Correction with a post process filter
|