按照惯例,先自淫一下,宣传下自己的书。
本节Unreal渲染流程架构的最后一节,这节结束后,就继续Skeletal Mesh Dynamic Instance讲解,这样你就会很好理解如何,实现Skeletal Mesh Dynamic Instance。
Light、Primitive、Material等数据在Game Thread发生了变化,Render Thread对应的数据也要发生变化,Unreal使用了3种方式让Game Thread通知Render Thread。
1. bRenderTransformDirty——Transform更新,例如:动态物体位置变化
2. bRenderDynamicDataDirty——动态数据更新,例如:骨骼数据
3. bRenderStateDirty——RenderState更新,除以上情况外的变化。
基本流程需要主动调用MarkRenderStateDirty、MarkRenderTransformDirty、MarkRenderDynamicDataDirty三个函数(还有其他函数,读者自己可以查询,需要添加Dirty变量和重载DoDeferredRenderUpdates_Concurrent函数)。然后通知World是帧末多线程执行、帧末GameThread执行、还是马上执行。无论哪种都会触发DoDeferredRenderUpdates_Concurrent函数。
void UActorComponent::DoDeferredRenderUpdates_Concurrent()
{
…
if(bRenderStateDirty)
{
RecreateRenderState_Concurrent();
}
else
{
if(bRenderTransformDirty)
{
SendRenderTransform_Concurrent();
}
if(bRenderDynamicDataDirty)
{
SendRenderDynamicData_Concurrent();
}
}
}
其中RecreateRenderState_Concurrent包括销毁DestroyRenderState_Concurrent和重建CreateRenderState_Concurrent两个函数。
CreateRenderState_Concurrent、DestroyRenderState_Concurrent、SendRenderTransform_Concurrent、SendRenderDynamicData_Concurrent都为虚函数,子类可以重载。
下面是UPrimitiveComponent和USkinnedMeshComponent的重载函数。
void UPrimitiveComponent::CreateRenderState_Concurrent()
{
…
Super::CreateRenderState_Concurrent();
…
// If the primitive isn't hidden and the detail mode setting allows it, add it to the scene. if (ShouldComponentAddToScene())
{
GetWorld()->Scene->AddPrimitive(this);
}
…
}
void UPrimitiveComponent::DestroyRenderState_Concurrent()
{
// Remove the primitive from the scene. UWorld* World = GetWorld();
if(World && World->Scene)
{
World->Scene->RemovePrimitive(this);
}
Super::DestroyRenderState_Concurrent();
}
void UPrimitiveComponent::SendRenderTransform_Concurrent()
{
…
// If the primitive isn't hidden update its transform. const bool bDetailModeAllowsRendering = DetailMode <= GetCachedScalabilityCVars().DetailMode;
if( bDetailModeAllowsRendering && (ShouldRender() || bCastHiddenShadow))
{
// Update the scene info's transform for this primitive. GetWorld()->Scene->UpdatePrimitiveTransform(this);
}
Super::SendRenderTransform_Concurrent();
}
void USkinnedMeshComponent::SendRenderDynamicData_Concurrent()
{
…
Super::SendRenderDynamicData_Concurrent();
…
}
根据目前UE4代码,UPrimitiveComponent以及子类不频繁调用MarkRenderStateDirty,这会导致UPrimitiveComponent从Scene移除并加入,是一个很昂贵的操作。同理ULightComponent也是。
如果真的有频繁的操作,希望用MarkRenderTransformDirty或者MarkRenderDynamicDataDirty方式,不需要移除再加入Component两个操作就可以更改Component属性。