颜色空间和混合

盘点Unreal的颜色

FLinearColor和FColor

FLinearColor是范围在[0, 1]的浮点颜色值,而FColor是4个8位的整数RGB值,范围在[0, 255]。

虽然叫做FLinearColor,但是它和FColor进行相互转换的时候,只是单纯地除以255,伽马矫正很后面。

Unreal在逻辑上,都是线性的,然后在着色器伽马矫正的时候,就转换到SRGB空间,正好和屏幕的抵消掉,我们调整颜色是0.5,那么屏幕上也显示的是0.5。

FSlateColor

FSlateColor比较特殊,它存储着一个FLinearColor,颜色的用法枚举值(ESlateColorStylingMode),

如果是使用颜色表,则会读取一个JSON配置文件,里面有主题色,根据存储的颜色id去取得颜色。

它的get_color函数根据传入的FWidgetStyle,去返回不同的颜色。

Widget绘制使用到的FWidgetStyle

draw_window_and_children绘制每个窗口的时候,会构造一个FWidgetStyle,空的,传递到paint window里面。

这个FWidgetStyle的三个成员,都是FLinearColor::White,第三个则透明度会低一点。

在Paint的时候,会乘以传播下来的FWidgetStyle的ColorAndOpacityTint,不断混合,我们的SBorder控件可以控制这个值。

我们的自定义控件,在重写OnPaint的时候,可以修改这个值,然后控制子级控件的混合。

SViewport和FSceneViewport

SViewport是我们的视口控件,FSceneViewport存储着一张RenderTarget,到时候采样到SViewport的控件上,SViewport持有FSceneViewport。

我们看一下SViewport的OnPaint函数:

int32 SViewport::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
{
	bool bEnabled = ShouldBeEnabled( bParentEnabled );
	bool bShowDisabledEffect = ShowDisabledEffect.Get();
    //展示效果,这个到时候会控制着色器显示灰色
	ESlateDrawEffect DrawEffects = bShowDisabledEffect && !bEnabled ? ESlateDrawEffect::DisabledEffect : ESlateDrawEffect::None;

	// Viewport texture alpha channels are often in an indeterminate state, even after the resolve,
	// so we'll tell the shader to not use the alpha channel when blending
    //ue的注释说视口纹理的alpha通道通常处于不确定状态,及时在解析之后
    //我们需要告诉着色器不要使用alpha通道,当混合的时候
    //我们的成员变量可以控制这个绘制的时候的一些效果,忽略透明度
	if( bIgnoreTextureAlpha )
	{
		DrawEffects |= ESlateDrawEffect::IgnoreTextureAlpha;
	}

	//是否执行伽马矫正,默认是开启的
	if( !bEnableGammaCorrection )
	{
		DrawEffects |= ESlateDrawEffect::NoGamma;
	}

	//这个是另一种效果
	if (bReverseGammaCorrection)
	{
		DrawEffects |= ESlateDrawEffect::ReverseGamma;
	}

	//是否开启混合
	if( !bEnableBlending )
	{
		DrawEffects |= ESlateDrawEffect::NoBlending;
	}
	else if( bPreMultipliedAlpha )
	{
		DrawEffects |= ESlateDrawEffect::PreMultipliedAlpha;
	}

	TSharedPtr<ISlateViewport> ViewportInterfacePin = ViewportInterface.Pin();

	if (ViewportInterfacePin.IsValid())
	{
		ViewportInterfacePin->OnDrawViewport( AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled );
	}

	//绘制一个quad,如果不直接画到backbuffer上
	if( !ShouldRenderDirectly() )
	{
		if( ViewportInterfacePin.IsValid() && ViewportInterfacePin->GetViewportRenderTargetTexture() != nullptr )
		{
			FSlateDrawElement::MakeViewport( OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry(), ViewportInterfacePin, DrawEffects, InWidgetStyle.GetColorAndOpacityTint() );
		}
		else
		{
			//视口没有准备好,画一个黑色的盒子
			static FSlateColorBrush BlackBrush( FColor::Black );
			FSlateDrawElement::MakeBox( OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry(), &BlackBrush, DrawEffects, BlackBrush.GetTint( InWidgetStyle ) );
		}
	}

	int32 Layer = SCompoundWidget::OnPaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bEnabled );

	return Layer;
}

SlateElementPixelShader.usf这个着色器绘制的时候,会调用GammaCorrect函数:

/** x:gamma curve adjustment, y:inverse gamma (1/GEngine->DisplayGamma), z:InvertAlpha, w:Contrast */
half4 GammaAndAlphaValues;//这个存了很多值,用来调节的

half3 GammaCorrect(half3 InColor)
{
	half3 CorrectedColor = InColor;

#if SOURCE_IN_LINEAR_SPACE
	FLATTEN if( GammaAndAlphaValues.y != 1.0f )
	{
        //这里面转成srgb
		CorrectedColor = ApplyGammaCorrection(CorrectedColor, GammaAndAlphaValues.x);
	}
#endif

	return CorrectedColor;
}

half3 ApplyGammaCorrection(half3 LinearColor, half GammaCurveRatio)
{
	//应用伽马曲线调整
	half3 CorrectedColor = pow(LinearColor, GammaCurveRatio);

	#if MAC
		//mac平台的,就单纯1.0/2.2
		CorrectedColor = pow(CorrectedColor, 1.0/2.2);
	#else
		#if USE_709
			CorrectedColor = LinearTo709Branchless(CorrectedColor);
		#else
			CorrectedColor = LinearToSrgb(CorrectedColor);
		#endif
	#endif

	return CorrectedColor;
}