增加接口控制shader参数
sunAlpha和sunBlur这两个pass分别有半径,在PostProcessMobile.usf文件中,这俩值是写死的,我们想要动态改变,所以增加了这俩参数。我们只希望平行光有光束效果,所以,只修改平行光相关的属性。
LightComponent.h文件里增加shaft里shader参数的设置:
UFUNCTION(BlueprintCallable, Category = "Rendering|Components|Light")
void SetLightShaftAlphaRadius(float NewValue);
UFUNCTION(BlueprintCallable, Category = "Rendering|Components|Light")
void SetLightShaftBlurRadius(float NewValue);
跟BloomTint逻辑一样:
UFUNCTION(BlueprintCallable, Category="Rendering|Components|Light")
void SetBloomThreshold(float NewValue);
UFUNCTION(BlueprintCallable, Category="Rendering|Components|Light")
void SetBloomTint(FColor NewValue);
另外,这两个属性需要在属性窗口里可见,属性定义跟bloomTInt一样,在LightProxy里也有相同名字的属性,update函数也会实时更新这俩属性,FViewInfo里也有这俩属性。
增加接口动态设置光束位置
设置lightShaft位置的接口:
在SceneVisibility文件,PostVisibilityFrameSetup函数中,worldSpaceBlurOrigin是通过LightSceneInfo->Proxy->GetPosition()获取的,那么这个Position是啥?GetPosition返回的是position,Position又是通过Light->GetLightPosition赋值的(),如下所示:
void FScene::UpdateLightTransform(ULightComponent* Light)
{
if(Light->SceneProxy)
{
FUpdateLightTransformParameters Parameters;
Parameters.LightToWorld = Light->GetComponentTransform().ToMatrixNoScale();
Parameters.Position = Light->GetLightPosition();
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
UpdateLightTransform,
FScene*,Scene,this,
FLightSceneInfo*,LightSceneInfo,Light->SceneProxy->GetLightSceneInfo(),
FUpdateLightTransformParameters,Parameters,Parameters,
{
FScopeCycleCounter Context(LightSceneInfo->Proxy->GetStatId());
Scene->UpdateLightTransform_RenderThread(LightSceneInfo, Parameters);
});
}
}
而GetLightPosition又是啥?在DirectionalLightComponent.cpp文件中,该函数的重载实现为:
FVector4 UDirectionalLightComponent::GetLightPosition() const
{
return FVector4(-GetDirection() * WORLD_MAX, 0 );
}
GetDirection又是啥?在LightComponent.cpp文件中,GetDirection函数:
FVector ULightComponent::GetDirection() const
{
return GetComponentTransform().GetUnitAxis( EAxis::X );
}
我们用我们自己的接口代替LightSceneInfo->Proxy->GetPosition(),为了light位置能用代码控制变化,我们用GetLightShaftOverridePositionForLightShafts()和void UDirectionalLightComponent::SetLightShaftOverridePosition(const FVector4& InPosition)来get和set这个值。
另外,set时,参数就要用一个component位置跟太阳位置变化一样,然后用这个component的GetComponentTransform.GetUnitAxis(EAxis::X),这是方向,然后*-1*WORLD_mAX,然后和0组成FVector4,跟前面分析的GetLightPosition的代码一样实现即可。
编辑器中能编辑上面的position
在编辑器中改变一个值,会从FPropertyNode的NotifyPostChange函数调用到USceneComponent的PostEditChangeChainProperty, 然后UActorComponent::PostEditChangeChainProperty,UObject::PostEditChangeChainProperty,UDirectionalLightComponent::PostEditChangeProperty,ULightComponent::PostEditChangeProperty,ULightComponentBase::PostEditChangeProperty,USceneComponent::PostEditChangeProperty,UActorComponent::PostEditChangeProperty,UActorComponent::ConsolidatedPostEditChange,~FComponentReregisterContext(),ComponentReregisterContext.h里Reregister,UActorComponent::ExecuteRegisterEvents(),ULightComponent::CreateRenderState_Concurrent(),World->Scene->AddLight,在FScene的AddLight函数里,设置坐标更新
void FScene::AddLight(ULightComponent* Light)
{
// Create the light's scene proxy.
FLightSceneProxy* Proxy = Light->CreateSceneProxy();
if(Proxy)
{
// Associate the proxy with the light.
Light->SceneProxy = Proxy;
// Update the light's transform and position.
Proxy->SetTransform(Light->GetComponentTransform().ToMatrixNoScale(),Light->GetLightPosition());
Proxy->SetLightShaftOverridePosition(Light->LightShaftOverridePosition);
// Create the light scene info.
Proxy->LightSceneInfo = new FLightSceneInfo(Proxy, true);
INC_DWORD_STAT(STAT_SceneLights);
// Adding a new light
++NumVisibleLights_GameThread;
// Send a command to the rendering thread to add the light to the scene.
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
FAddLightCommand,
FScene*,Scene,this,
FLightSceneInfo*,LightSceneInfo,Proxy->LightSceneInfo,
{
FScopeCycleCounter Context(LightSceneInfo->Proxy->GetStatId());
Scene->AddLightSceneInfo_RenderThread(LightSceneInfo);
});
}
}
shader渲染管线分析整理
执行在postProcessing.cpp文件的ProcessES2函数。pass先后包括sunMask,BloomSetup,dof,4个bloomDown,3个bloomUp,SunAlpha,SunBlur,SunMerge,SunAverage,FRCSeparateTranslucensyPassES2.其中lightShaft涉及的就有sunMask,BloomSetup,4个bloomDown,3个bloomUp,SunAlpha,SUnBlur,SunMerge等。
其中每个pass的输入都可以在代码中看到,比如:SunMerge有3个输入:
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessSunMergeES2(PrePostSourceViewportSize));
if(bUseSun)
{
Pass->SetInput(ePId_Input0, PostProcessSunBlur);
}
if(bUseBloom)
{
Pass->SetInput(ePId_Input1, PostProcessBloomSetup);
Pass->SetInput(ePId_Input2, PostProcessUpsample2);
}
PostProcessSunMerge = FRenderingCompositeOutputRef(Pass);
又比如bloomUp的upScale赋值:
{
FVector4 TintA = FVector4(Settings.Bloom2Tint.R, Settings.Bloom2Tint.G, Settings.Bloom2Tint.B, 0.0f);
TintA *= View.FinalPostProcessSettings.BloomIntensity;
// Scaling Bloom2 by extra factor to match filter area difference between PC default and mobile.
TintA *= 0.5;
FVector4 TintB = FVector4(1.0f, 1.0f, 1.0f, 0.0f);
FRenderingCompositePass* Pass = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBloomUpES2(PrePostSourceViewportSize/8, FVector2D(UpScale, UpScale), TintA, TintB));
Pass->SetInput(ePId_Input0, PostProcessDownsample2);
Pass->SetInput(ePId_Input1, PostProcessUpsample3);
PostProcessUpsample2 = FRenderingCompositeOutputRef(Pass);
}
其构造函数会进行赋值:
FRCPassPostProcessBloomUpES2(FIntPoint InPrePostSourceViewportSize, FVector2D InScaleAB, FVector4& InTintA, FVector4& InTintB) : PrePostSourceViewportSize(InPrePostSourceViewportSize), ScaleAB(InScaleAB), TintA(InTintA), TintB(InTintB) { }
该文件中调用的具体shader类,shader变量的赋值在PostProcessMobile.cpp中可以找到。比如SunBLurVS的SetVS:
void SetVS(const FRenderingCompositePassContext& Context)
{
const FVertexShaderRHIParamRef ShaderRHI = GetVertexShader();
FGlobalShader::SetParameters<FViewUniformShaderParameters>(Context.RHICmdList, ShaderRHI, Context.View.ViewUniformBuffer);
PostprocessParameter.SetVS(ShaderRHI, Context, TStaticSamplerState<SF_Bilinear,AM_Clamp,AM_Clamp,AM_Clamp>::GetRHI());
SetShaderValue(Context.RHICmdList, ShaderRHI, LightShaftCenter, Context.View.LightShaftCenter);
SetShaderValue(Context.RHICmdList, ShaderRHI, LightShaftBlurRadius, Context.View.LightShaftBlurRadius);
}
shader文件是PostProcessMobile.usf:
sunMask Pass:
//
// Convert depth in alpha into combined circle of confusion and sun intensity.
// Or pre-tonemap before hardware box-filtered resolve.
//
void SunMaskVS_ES2(
in float4 InPosition : ATTRIBUTE0,
in float2 InTexCoord : ATTRIBUTE1,
out float4 OutUVPos : TEXCOORD0,
out float4 OutPosition : SV_POSITION
)
{
DrawRectangle(InPosition, InTexCoord, OutPosition, OutUVPos.xy);
OutUVPos.zw = OutPosition.xy;
}
void SunMaskPS_ES2(
float4 InUVPos : TEXCOORD0,
out half4 OutColor : SV_Target0
)
{
#if ((COMPILER_GLSL_ES2||COMPILER_GLSL_ES3_1) && ES2_USE_FETCH) || (METAL_PROFILE && !MAC)
OutColor = FramebufferFetchES2();
#else
OutColor = PostprocessInput0.Sample(PostprocessInput0Sampler, InUVPos.xy);
#endif
#if ES2_USE_MSAA
// On-chip pre-tonemap before MSAA resolve.
OutColor.rgb *= rcp(OutColor.r*0.299 + OutColor.g*0.587 + OutColor.b*0.114 + 1.0);
#else
#if ES2_USE_DEPTHTEXTURE
half InDepth = CalcSceneDepth(InUVPos.xy); //深度信息,
#else
half InDepth = OutColor.a;
#endif
#if ES2_USE_SUN
half2 DepthMaskScaleBias = SunConstDepthMaskScaleBias();
half FarAmount = saturate(InDepth * DepthMaskScaleBias.x + DepthMaskScaleBias.y); //较远地方的farAmount为1,较近地方的FarAmount值为0
half3 SunAmount = OutColor.rgb * S