-
-
Notifications
You must be signed in to change notification settings - Fork 22k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Drawing linear colors to a HDR2D Viewport is not possible #93206
Comments
Some updates: The visual output of the darkened color is actually correct. It is simply the linear color displayed in sRGB space without correction applied. I don't know if that is a wrong assumption, but I expected a shader, operating on a canvas item in a HDR2D SubViewport, to still sample a neutral grey color of rgb(0.5,0.5,0.5) as rgb(0.5,0.5,0.5). This is what the in-shader-sampler checks for. While right now it receives a color that has seemingly gone through a sRGB to linear conversion of rgb(0.214, 0.214, 0.214). My current theory is that this is actually happening because the ColorRect converts its color to linear when it's drawn. I assumed that setting it's color would set a linear color, instead of an sRGB color that then gets converted. This apparently also means, that anything drawn in a _draw function gets implicitly converted from sRGB to linear. Which was not at all what I expected. |
Thank you akien, I was struggling with the spelling. ;) |
|
Since this has been labelled as a documentation issue, what is the expected workflow for drawing canvas items that are in linear colorspace?
For textures, applying a conversion shader would work. |
@Motioneer When using HDR2D, rendering happens in linear space. No conversion is needed from your end, Godot will automatically adjust so that everything is in linear space. Note, Godot isn't converting your scene to linear space, it just renders in linear space. The docs currently have this to say:
If you want to sample that Viewport without displaying it to the screen, and you need the viewport to be in sRGB space, you are responsible for tonemapping the viewport yourself. I'm not sure what your use-case is. But it might help to understand the reason we have the hdr_2d mode. It was created to allow:
In both cases, it is absolutely necessary to render in linear space and avoid any color space conversions until the Viewport is blitted to the screen. If you have a use-case where you require a higher dynamic range, but you don't want to the final result to be in the linear color space, you have to tonemap yourself. Either in a fullscreen shader pass in the Viewport, or when sampling the viewport. |
@clayjohn Thank you for responding! sRGB is not something I want in this case, since I don't actually draw the HDR SubViewport to screen. The simulation is a fragment shader that offsets the UV based of the "wind speed" input, which is a HDR SubViewport. (Visible in the second part of the gif.) I map the x, y (-1.0 .. 1.0) coordinates of the wind direction to red and green channels (0.0 .. 1.0), when drawing to the SubViewport and reverse the mapping in the simulation shader. Here is the relevant code snipped for the color calculation of the "wind lines", for clarity: var direction: Vector2 = start_pos.direction_to(end_pos)
# Modify from -1 _ 0 space to 0 _ 1
var scaled_direction:Vector2 = direction * 0.5
var moved_direction:Vector2 = scaled_direction + Vector2(0.5, 0.5)
var directional_color := Color()
directional_color.r = moved_direction.x
directional_color.g = moved_direction.y
var srgb_color = directional_color.linear_to_srgb() I originally assumed, since I was drawing in an HDR SubViewport, I would be drawing linear colors and could directly map the coordinates to the channels, but without the sRGB transformation, the coordinates would be thrown off when not 0.0 or 1.0. Please let me know if this explanation was coherent! |
@Motioneer Thank you for the detailed explanation! The problem in this case comes from you encoding a directional vector in a color. In Godot all color inputs to 2D shaders are in sRGB space (I.e. colors, textures, etc.). For textures you can specify with the uniform whether the texture contains sRGB color data (and may need to be converted) or whether it contains raw data. Uniforms, we use the input type to determine if the input is a color (and may need to be converted) or whether it is raw data. Vectors are treated as raw data while Colors are treated as colors. In your case, you should be using either a Vector2 or a Vector4 to pass in your directional data. Unless I am missing something and it is important for you to be storing a raw direction in a Color variable? |
@clayjohn I am not sure I follow. The reason I am using a SubViewport as a "wind texture" is that i can use the GPU to draw lines in it, enabling a per-pixel modification of the simulation shader. I don't know if it is visible in the gif, but the SubViewport never clears, blends old lines together, fades to grey and will itself have a shader that modifies it's content over time. This gives me a really cheap way to simulate complex airflow. I am not quite sure how that would work with passing raw vectors? |
@Motioneer I mean, instead of passing |
@clayjohn uniform sampler2D wind_texture: filter_linear, source_color; Are you proposing I pass all "wind lines", as I have dubbed them, as a vector array and then figure out which pixels to modify and in what way, in the shader? uniform vec2[10] wind_lines; Sorry, I not sure what you mean with passing raw data. 😅 The moved_direction itself is only part of the information I need, since I actually need all information nessesary to know what pixels need to be affected by this "line". So start and end position as well as width. |
Where is the
I have no idea what "wind lines" are in the context of your application. I am suggesting using Without seeing your code, I can't really provide too direct of feedback. But 2 things to consider:
|
@clayjohn Setup ExplanationI have fully commented the code, to make it as easy as possible to follow.
The direction of the line is encoded in the color.
The sample UV of the last "smoke" frame is offset by the decoded "wind" direction of the "wind" ViewportTexture, Please note that the uniform sampler2D wind_texture: filter_linear;
// uniform sampler2D wind_texture: filter_linear, source_color; TestingDisabling the "Convert Line and Clear Color to sRGB" checkbox will break the direction encoding. Disabling HDR on the SubViewports as well, fixes the direction encoding: ConclusionWhen using HDR SubViewports, and reading the ViewportTexture as a shader uniform, the any color drawn to the SubViewport needs to be converted from linear to sRGB to be read as linear in the shader. The This leads me to believe that somewhere in this pipeline, a sRGB to linear conversion happens implicitly, and I don't know where. |
I think I can boil the issue down to this:
There is no way to avoid this conversion. It is apparently not possible to draw linear colors directly. The shader on the right directly maps the color channel value to the coloring of the UV.
|
I think what clayjohn meant was: Don't pass in your parameter as a "color" (a tuple of values that need to be converted between color spaces) but as a generic vector (a tuple of values that I would like to get as-is). In your shader:
In your script:
|
@bs-mwoerner |
I was referring to this line in your script
and this line in your shader:
For the purpose of Godot shaders, this would be considered setting and reading a color. |
@bs-mwoerner I see, thank you for clarifying. The reason why I don't pass a generic vector is, that in my use case, I don't need a single vector or color, but want to sample a ViewportTexture. In this example, you can see the ViewportTexture beeing sampled. |
For reading from a texture instead of a single value, the syntax would change to
and
With |
@bs-mwoerner
The problem is, that I can't draw a linear color to a HDR2D Viewport and then sample the ViewportTexture to get the same linear color again. |
I took a look at the new MRP and I can see why this is causing so much frustration. @bs-mwoerner and I have been talking about uniforms and texture sampling, but the MRP doesn't have a problem with uniforms and texture sampling. The problem comes from the fact that you are passing data in the color parameter of This situation is extra complex because your chosen approach for this effect requires you to use colors to pass in non-color data. Let's go through the steps one by one:
|
Yes, I got a bit side-tracked by the slider example and the actual problem is indeed not in how the texture is read but rather in how it is filled. It's not a bad idea in principle to use the drawing functions for creating non-color textures (because otherwise you'd have to implement your own line drawing), but since they have color conversion built in, one needs to give them a "fake" color that they convert to the intended numerical value. ("Just don't use a color" doesn't work here, because there's no The crucial bit of information (that I felt was true from experience but wasn't able to find a documentation quote for) is that the color parameter for |
Thank you all for your patience in understanding this issue. To paraphrase my original question, what is the intended workflow when using What is the intended way to use |
The
I'm not sure what you mean as we haven't discussed a case for |
Thank you for clearing that up! In the documentation the whole sRGB / Linear blending and color conversion topic is currently only outlined in the Viewport class reference in the
Should we expand on this, or will it bloat the description? Alternatively, the "Using Viewports" page, in the section about "Rendering", could include an explanation of HDR2D in general, as well as the color conversion. I plan to write a pull request, but could someone more experienced with the documentation give me a pointer which approach would be preferable? |
Since this is apparently a much more niche issue than I thought. I'm closing this ticket. |
Tested versions
Tested in 4.3 dev and 4.2,1 (stable)
System information
v4.3.beta.custom_build [71699e0]
Issue description
When drawing in a HDR2D SubViewport, a sRGB-to-linear colorspace conversion happens, regardless of the source's colorspace. Shaders also receive a wrongly corrected sampler.
See MRP:
Manually applying a Linear to sRGB transformation in a shader corrects the color.
Not in the MRP, but also tested:
Steps to reproduce
or
Minimal reproduction project (MRP)
srgb_linear_test.zip
The text was updated successfully, but these errors were encountered: