Open Kobrar opened 3 years ago
In Godot, sRGB textures are only used in 3D, not 2D. This has been the case since the inception of Godot, even before 3.0. The importer will detect where the texture is used and will automatically convert it on import. (This is also why textures appear overly dark in 2D when the same texture is also used in 3D.)
I don't remember the rationale behind this exactly, but it was probably done for a reason :slightly_smiling_face:
I laid out a couple issues with the way its done right now. I find myself starting all my shaders by pasting srgb2linear conversion functions (this does not solve the "filtering srgb encoded color values" issue). Most developers probably aren't even aware something is amiss. I think it would be beneficial to at least strip current solution of its status quo. I am very confident there is a strong case to do all the calculations in 2D in linear.
You may need to explain your testing methodology a little more. In 2D, Godot doesn't do any sRGB/linear conversion. So what you put in is what you get out.
There are two places that Godot does do a linear/sRGB conversion that may be relevant.
hint_albedo
That is exactly the issue, any color rendering should be done in linear, any color texture should be converted to linear before use. And for the exact same reasons it is done in those two particular places. Any color blending on srgb encoded colors is mathematically incorrect. In most uses you can get away with it, however I find myself running into it being a genuine problem more and more often, as I work on more and more procedural solutions. And the other way around, in the cases where you can get away with srgb blending, linear blending will be better.
Right now, if I get it right, I could get the effect in the following way:
If that is the intended way to achieve the default correct behaviour (you could want to blend perceptually too, for instance), it is at best not documented, and at worst really out of the way.
So I think you are talking about a few different things here which is why I am asking for some clarification. Your above issue says that calculations are done in sRGB space. But your more recent comment sounds more like you are more concerned about UX regarding flagging values as sRGB/Linear.
In 2D Godot does not touch the color space of your images. As Calinou pointed out above, if you import textures into a 2D scene, by default they are flagged as 2D images and they will be treated as if they are encoded in linear space. Further, if you specify a value in a shader it is neither in sRGB or in linear, it is just a value. Godot doesn't do any conversion for you. So if all your inputs are linear, then your calculations are linear and then your output will be in linear space too.
In 3D, textures are flagged as being in sRGB by default and appropriate conversions are applied to make them linear.
Again, for clarity, please explain your testing methodology and how you are arriving at your conclusions. For example, are you using a texture that is encoded in sRGB and then attempting to use it in 2D without setting the srgb
flag on import? Are you specifying hint_color
or hint_albedo
on your textures in your shader? etc.
So I think you are talking about a few different things here which is why I am asking for some clarification. Your above issue says that calculations are done in sRGB space. But your more recent comment sounds more like you are more concerned about UX regarding flagging values as sRGB/Linear.
Part of the issue is that the "result" isn't in any particular space. Any color information is treated as-is, with no regard for color encoding. If you output a color value that is srgb-encoded, for instance identity, the result will look ok, because output is supposed to be srgb-encoded. However, if you mark the texture as linear, it will be displayed in linear encoding. There are no checks or conversions, no way to have texture be linearly encoded for calculations, and then the result be converted to srgb. It is my point that there should be clearly defined standards for color representation used in calculations and display, where for math color should be linearly encoded, and result should be srgb encoded.
In 2D Godot does not touch the color space of your images.
And that I consider to be erroneous. For math(shaders, blending, anything) all color data should be converted to linear encoding. Textures with raw data shouldn't be converted. I understand there could be severe difficulties with the implementation and possibly usability side of things. For now I intend to discuss merit.
As Calinou pointed out above, if you import textures into a 2D scene, by default they are flagged as 2D images and they will be treated as if they are encoded in linear space.
That is not accurate. They are correctly being displayed as srgb encoded images.
Further, if you specify a value in a shader it is neither in sRGB or in linear, it is just a value. Godot doesn't do any conversion for you. So if all your inputs are linear, then your calculations are linear and then your output will be in linear space too.
Yes. However, if all my inputs are srgb encoded, all the calculations will be incorrectly done in srgb and my result will be bogus but in srgb. But then if I mark the texture as srgb=enable, it will get darkened. The rgb 188,188,188 will be correctly moved to 0.5, linear encoding achieved. All math will become possible. But then Godot won't convert the linear output to srgb, so either I will copy a conversion function from the internet or I will get a visually wrong result.
It seems to me that the simplest workflow, which achieves any encoding awareness, would be, as stated before:
The problems with this approach are:
The ideal solution would be: a switch, probably in viewport and settings/display, which would make any simple workflow viable. By default all textures have srgb: detect. In color space aware variant, they would behave like srgb: enable. The viewport would do a linear -> srgb conversion at the end. In standard mode, no changes from now. This should achieve correctness and simplicity of conversion for the case of textures. There remains the case of Color() and things like GradientTexture, but it seems to work in 3D so perhaps there is an easy solution. The gist of it is, it would be really nice to have similar behaviour to 3D.
Again, for clarity, please explain your testing methodology and how you are arriving at your conclusions. For example, are you using a texture that is encoded in sRGB and then attempting to use it in 2D without setting the
srgb
flag on import? Are you specifyinghint_color
orhint_albedo
on your textures in your shader? etc.
So going through the tests 1 by 1:
COLOR.rgb = vec3(0.5,0.5,0.5);
. The dark grey result confirms that there is no conversion of any kind on output.It sounds to me like you a proposing that the output of Godot shaders should always be converted into sRGB space. Essentially that we should adjust the rendering to assume that every input is sRGB and to guarantee that every output is sRGB.
Is that a fair description of what you are trying to say?
Its more alike to:
Output of individual shader should probably not be directly converted.
Hit this problem in https://github.com/setzer22/godot-egui/issues/14 . Any news on this? It seems that it makes correct color blending in 2D impossible. I can either:
Post-processing the image to do linear->sRGB conversion as a workaround would mess up the 3D image.
This is entirely intended. The reason is that GLES2 (what a lot of Godot users use for performance and compatibility) can't do SRGB -> linear conversion, so the textures can't be converted to the right color space. Additionally, a lot of users are happy with this working this way, even if its not correct due to the simplicity.
What was discussed in the past is that, eventually, it should be made optional to render 2D in Linear (and mandatory if you use HDR output) when using the modern renderer (Vulkan/D3D12).
Godot version: 3.2.3
OS/device including version: Windows 7
Issue description: I made a project which showcases a couple of erroneous results.
The first example sets the output COLOR in a shader to 0.5 grey. The result is 128 srgb grey. This is erroneous because it means output values in the shader are srgb encoded. Shaders exist to perform math and srgb encoding is wrong for math. This could possibly be fixed with manual conversion in shaders.
The second and third examples shows that blending colors outside shaders still provides wrong results, which means the blending is again done with no regard for srgb encoding. No fix aside from custom shader that I know of.
The fourth and fifth examples verify that values sampled from textures are also srgb encoded. This is in line with first example. This is bad because texture filtering should account for srgb encoding. Results are not fixable with manual conversions, as those occur after filtering.
I found no documentation of this behaviour, although it can be expected. More importantly, I have found no way of fixing this behaviour. As 3D rendering seems to be done correctly with linear encoding, I can't see why it would be impossible for 2D.
Steps to reproduce: Run the test project. It showcases a few ways the behaviour is incorrect.
Minimal reproduction project: SRGB.zip