本文主要是介绍UE4 3DUI及曲面UI实现浅析和拓展,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
最近在工作中遇见了游戏场景内3Dui和曲面ui的需求,大致看了下ue4 4.27的实现方式,简单来说就是将widget渲染到一个RT上,然后将RT赋给一个动态材质,在场景代理中动态构建一个mesh并使用这个动态材质。
看看实现,首先在beginplay或者component onregister时初始化窗口,这里就用的创建2dui的方式。
void UWidgetComponent::InitWidget()
{
// Don't do any work if Slate is not initialized
if ( FSlateApplication::IsInitialized() )
{
UWorld* World = GetWorld();
if ( WidgetClass && Widget == nullptr && World && !World->bIsTearingDown)
{
Widget = CreateWidget(GetWorld(), WidgetClass);
SetTickMode(TickMode);
}
。。。。。。
}
}
然后在tick中更新各组件
更新widget,将widget放入一个slatewindow以进行渲染,注意在最开始首先创建了动态材质,并在材质中将RT作为参数赋了进去。这里很有迷惑性,因为这时RT还没有值是空,真正的的RT要等后面updateRenderTarget时再赋值进去。
void UWidgetComponent::UpdateWidget()
{
。。。。。
TSharedPtr<SWidget> NewSlateWidget;
if (Widget)
{
NewSlateWidget = Widget->TakeWidget();
}
// Create the SlateWindow if it doesn't exists
bool bNeededNewWindow = false;
if (!SlateWindow.IsValid())
{
UpdateMaterialInstance();
SlateWindow = SNew(SVirtualWindow).Size(CurrentDrawSize);
SlateWindow->SetIsFocusable(bWindowFocusable);
SlateWindow->SetVisibility(ConvertWindowVisibilityToVisibility(WindowVisibility));
RegisterWindow();
bNeededNewWindow = true;
}
。。。。。
// We Get here if we have a UMG Widget
if (NewSlateWidget.IsValid())
{
if (NewSlateWidget != CurrentSlateWidget || bNeededNewWindow)
{
CurrentSlateWidget = NewSlateWidget;
SlateWindow->SetContent(NewSlateWidget.ToSharedRef());
。。。。。
}
}
。。。。。
}void UWidgetComponent::UpdateMaterialInstance()
{
。。。。。
UMaterialInterface* BaseMaterial = GetMaterial(0);
MaterialInstance = UMaterialInstanceDynamic::Create(BaseMaterial, this);
UpdateMaterialInstanceParameters();
}void UWidgetComponent::UpdateMaterialInstanceParameters()
{
if ( MaterialInstance )
{
MaterialInstance->SetTextureParameterValue("SlateUI", RenderTarget);
MaterialInstance->SetVectorParameterValue("TintColorAndOpacity", TintColorAndOpacity);
MaterialInstance->SetScalarParameterValue("OpacityFromTexture", OpacityFromTexture);
}
}
更新完窗口后就将窗口绘制到RT上,核心函数就高亮的两个,在Update中创建和初始化RT,然后将其赋给material,再将窗口使用FWindowRender绘制到RT上。
void UWidgetComponent::DrawWidgetToRenderTarget(float DeltaTime)
{
。。。。。
UpdateRenderTarget(CurrentDrawSize);
// The render target could be null if the current draw size is zero
if(RenderTarget)
{
bRedrawRequested = false;
WidgetRenderer->DrawWindow(
RenderTarget,
SlateWindow->GetHittestGrid(),
SlateWindow.ToSharedRef(),
DrawScale,
CurrentDrawSize,
DeltaTime);
。。。。。
}
}void UWidgetComponent::UpdateRenderTarget(FIntPoint DesiredRenderTargetSize)
{
。。。。。
if ( RenderTarget == nullptr )
{
RenderTarget = NewObject<UTextureRenderTarget2D>(this);
。。。。。
if ( MaterialInstance )
{
MaterialInstance->SetTextureParameterValue("SlateUI", RenderTarget);
}
}
。。。。。
}
到这里是将窗口画面绘制到RT到,而生成并渲染到3Dmesh上是通过代理,meshbuilder实现的。代理构造时获取窗口的材质和各参数。
详细看看平面窗口(plane)和曲面窗口(cylinder)是怎么计算的。
FWidget3DSceneProxy( UWidgetComponent* InComponent, ISlate3DRenderer& InRenderer )
: FPrimitiveSceneProxy( InComponent )
, ArcAngle(FMath::DegreesToRadians(InComponent->GetCylinderArcAngle()))
, Pivot( InComponent->GetPivot() )
, Renderer( InRenderer )
, RenderTarget( InComponent->GetRenderTarget() )
, MaterialInstance( InComponent->GetMaterialInstance() )
, BlendMode( InComponent->GetBlendMode() )
, GeometryMode(InComponent->GetGeometryMode())
, BodySetup(InComponent->GetBodySetup())
{
bWillEverBeLit = false;
MaterialRelevance = MaterialInstance->GetRelevance_Concurrent(GetScene().GetFeatureLevel());
}virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
{
#if WITH_EDITOR
const bool bWireframe = AllowDebugViewmodes() && ViewFamily.EngineShowFlags.Wireframe;
auto WireframeMaterialInstance = new FColoredMaterialRenderProxy(
GEngine->WireframeMaterial ? GEngine->WireframeMaterial->GetRenderProxy() : nullptr,
这篇关于UE4 3DUI及曲面UI实现浅析和拓展的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!