Color issues with UCanvas K2_DrawMaterial()

Hi, actually I have 2 questions. First the questions

  1. What is special about the K2_DrawMaterial() that it fails with a simple material color displayed wrong? Is it a ‘works as designed’ effect, that a material needs to do an sRGB conversion manually? Drawing on Canvas maybe has no post process to apply sRGB conversion? See link at end of my post

  2. Why isn’t there a function in FLinearColor to do an sRGB conversion while keeping the float values? I’m pretty sure, there’s some reason for that, or I just did not find the correct function.

Here’s the background for these questions

I’m picking a color with a UPROPERTY FLinearColor and use this as a background color in 2 different situations. In the examples, I’m just using a fixed linear color value.

Case A) Drawing a background using a slate brush:

FLinearColor BGColor = FLinearColor(0.8f, 0.2f, 0.0f, 1.0f);
BGBrush->TintColor = BGColor;
BGOverlay = SNew(SImage).Image(BGBrush);

This one works perfect, the background color is rendered in desired visible color tone.

Case B) In a CustomGameViewportClient using a Canvas DrawMaterial() with a dynamic material instance:

MI_TestMaterial = UMaterialInstanceDynamic::Create(TestMaterial, this);
FLinearColor bgcolor = FLinearColor(0.8f, 0.2f, 0.0f, 1.0f);
MI_TestMaterial->SetVectorParameterValue(TEXT("BGColor"), bgcolor);
Canvas->K2_DrawMaterial(MI_TestMaterial, FVector2D(0.0, 0.0f), FVector2D(1280, 720), FVector2D(0.0, 0.0), FVector2D::UnitVector);

This one fails, not showing the correct value (much darker almost red color) - looks like some typical sRGB mixup…

My base material, showing 4 different paths for the Output color:

Even the directly set color value within the material itself is displayed incorrectly with DrawMaterial(). BTW: The same also happens if using Material Domain Surface.

The only way to get around this: I need to do a conversion to sRGB for the color channels before feeding in to the material color pin.
This increases the Shader instruction count from 59 to 81.

And there’s no need to do that conversion over and over in the shader. Instead, passing in a color with already corrected values only once is sufficient here. The real logic of the material is about transparency only, the color is just passed through.

Trying to use an FColor derived from this, with sRGB conversion flag set:

FColor bgfcolor = bgcolor.ToFColor(true);

This does not change anything, except that passed in values in the material slightly differ because of the 8bit quantization, e.g. 0.8 now 0.8069, although I would have expected different values here due to the sRGB conversion flag being set.

I also tried to set a color as sRGB color as UPROPERTY (with almost identical orange tone) and converted to linear color before setting as parameter value.

FColor bgfcolor = FColor(204, 119, 0, 255);
bgcolor = FLinearColor::FromSRGBColor(bgfcolor);

This also makes absolutely no difference.

So I did go for the following: convert bgcolor using the simple power 2.2 approximation before setting the material parameter. (The simple approximation is ok for me)

FLinearColor bgcolor = FLinearColor(0.8f, 0.2f, 0.0f, 1.0f);
bgcolor.R = FMath::Pow(bgcolor.R, 1.0 / 2.2f);	// only approximation!!
bgcolor.G = FMath::Pow(bgcolor.G, 1.0 / 2.2f);
bgcolor.B = FMath::Pow(bgcolor.B, 1.0 / 2.2f);
MI_TestMaterial->SetVectorParameterValue(TEXT("BGColor"), bgcolor);

Et voila - works like a charm - but I still have the impression, that I am missing somehting here…

As said, I did not find any functions in FLinearColor, that allow to do that sRGB value conversion similar to what I did with the above while keeping float values and not relying on any FColor stuff.

BTW:
I found also Nick Darnells post on UI Materials and RGB

  • All material processing happens in the linear color space…
  • There’s no reason you need to work in gamma space in the material, just let the player choose the color in gamma space, convert it to a linear color, and set that as the material parameter.
  • Gamma correction happens as a post process

The last one makes me believe, that it could be related to post processing somehow not taking place with canvas draw…

Thanks a lot for any thoughts.

The same issues also happen when using textures in the material.

Texture with sRGB checked and Sampler Type color: looks fine in material preview - the standard situation for image textures. But in K2_DrawMaterial(), it gets darkened down.

So, I can change the texture to Linear and use Sampler type Linear Color. Now it looks bad in preview, but it will render correctly in K2_DrawMaterial(). (Similar can be achieved by setting RGBCurve adjustment in texture editor to 1/2.2…) But that is bad, because I need the texture for background in Slate as well, and here things work ‘normal’ so I would end up in keeping 2 versions of the same texture…

So, in this case, the cost for keeping double textures seems to be worse than going for the more complex material with conversion nodes as shown in the next image. Note, how the texture preview shows the ‘wrong’ view, too light, which then gets compensated by K2_DrawMaterial().