颜色空间和混合
盘点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;
}