CameraManager源码梳理

1.初始化流程

 在SpawnPlayerCameraManager中创建CameraManager并进行初始化

void APlayerController::SpawnPlayerCameraManager()
{
	// servers and owning clients get cameras
	// If no archetype specified, spawn an Engine.PlayerCameraManager.  NOTE all games should specify an archetype.
	FActorSpawnParameters SpawnInfo;
	SpawnInfo.Owner = this;
	SpawnInfo.Instigator = GetInstigator();
	SpawnInfo.ObjectFlags |= RF_Transient;	// We never want to save camera managers into a map
	if (PlayerCameraManagerClass != NULL)
	{
		PlayerCameraManager = GetWorld()->SpawnActor<APlayerCameraManager>(PlayerCameraManagerClass, SpawnInfo);
	}
	else
	{
		PlayerCameraManager = GetWorld()->SpawnActor<APlayerCameraManager>(SpawnInfo);
	}

	if (PlayerCameraManager != NULL)
	{
		PlayerCameraManager->InitializeFor(this);
	}
	else
	{
		UE_LOG(LogPlayerController, Log,  TEXT("Couldn't Spawn PlayerCameraManager for Player!!") );
	}
}

InitializeFor为给定的关联PlayerController初始化这个PlayerCameraManager

void APlayerCameraManager::InitializeFor(APlayerController* PC)
{
	FMinimalViewInfo DefaultFOVCache = GetCameraCacheView();
	DefaultFOVCache.FOV = DefaultFOV;
	SetCameraCachePOV(DefaultFOVCache);

	PCOwner = PC;

	SetViewTarget(PC);

	// set the level default scale
	SetDesiredColorScale(GetWorldSettings()->DefaultColorScale, 5.f);

	// Force camera update so it doesn't sit at (0,0,0) for a full tick.
	// This can have side effects with streaming.
	UpdateCamera(0.f);
}

 2.相机更新流程

2.1 CameraManager::UpdateCamera

内部执行两个操作:

        1. 调用DoupdateCamera()函数

        2. 如果CameraManager.buseclientsidecamerupupdates设置为True,客户端将复制相机位置到服务器,其中,会将旋转压缩到4字节

void APlayerCameraManager::UpdateCamera(float DeltaTime)
{
	check(PCOwner != nullptr);

	if ((PCOwner->Player && PCOwner->IsLocalPlayerController()) || !bUseClientSideCameraUpdates || bDebugClientSideCamera)
	{
		DoUpdateCamera(DeltaTime);

		...................

		if (bShouldSendClientSideCameraUpdate && IsNetMode(NM_Client))
		{
            ......................
		}
	}
}

 2.2 CameraManager::DoUpdateCamera

实际更新相机的函数

        1. 颜色插值变化

// update color scale interpolation
	if (bEnableColorScaleInterp)
	{
		float BlendPct = FMath::Clamp((GetWorld()->TimeSeconds - ColorScaleInterpStartTime) / ColorScaleInterpDuration, 0.f, 1.0f);
		ColorScale = FMath::Lerp(OriginalColorScale, DesiredColorScale, BlendPct);
		// if we've maxed
		if (BlendPct == 1.0f)
		{
			// disable further interpolation
			bEnableColorScaleInterp = false;
		}
	}

        2.调用UpdateViewTarget()处理ViewTarget的更新

	// Don't update outgoing viewtarget during an interpolation when bLockOutgoing is set.
	if ((PendingViewTarget.Target == NULL) || !BlendParams.bLockOutgoing)
	{
		// Update current view target
		ViewTarget.CheckViewTarget(PCOwner);
		UpdateViewTarget(ViewTarget, DeltaTime);
	}

	// our camera is now viewing there
	NewPOV = ViewTarget.POV;

        3.ViewTarget和PendingViewTarget之间的融合(过渡)逻辑

	// if we have a pending view target, perform transition from one to another.
	if (PendingViewTarget.Target != NULL)
	{
		BlendTimeToGo -= DeltaTime;

		// Update pending view target
		PendingViewTarget.CheckViewTarget(PCOwner);
		UpdateViewTarget(PendingViewTarget, DeltaTime);

		// blend....
		if (BlendTimeToGo > 0)
		{
			float DurationPct = (BlendParams.BlendTime - BlendTimeToGo) / BlendParams.BlendTime;

			float BlendPct = 0.f;
			switch (BlendParams.BlendFunction)
			{
	            .........
            }

		}
		else
		{
			// we're done blending, set new view target
			ViewTarget = PendingViewTarget;

			// clear pending view target
			PendingViewTarget.Target = NULL;

			BlendTimeToGo = 0;

			// our camera is now viewing there
			NewPOV = PendingViewTarget.POV;

			OnBlendComplete().Broadcast();
		}
	}

        4.处理CameraFade

	if (bEnableFading)
	{
		if (bAutoAnimateFade)
		{
			FadeTimeRemaining = FMath::Max(FadeTimeRemaining - DeltaTime, 0.0f);
			if (FadeTime > 0.0f)
			{
				FadeAmount = FadeAlpha.X + ((1.f - FadeTimeRemaining / FadeTime) * (FadeAlpha.Y - FadeAlpha.X));
			}

			if ((bHoldFadeWhenFinished == false) && (FadeTimeRemaining <= 0.f))
			{
				// done
				StopCameraFade();
			}
		}

		if (bFadeAudio)
		{
			ApplyAudioFade();
		}
	}

        5.缓存相机数据FillCameraCache,用于渲染

	if (AllowPhotographyMode())
	{
		const bool bPhotographyCausedCameraCut = UpdatePhotographyCamera(NewPOV);
		bGameCameraCutThisFrame = bGameCameraCutThisFrame || bPhotographyCausedCameraCut;
	}

	// Cache results
	FillCameraCache(NewPOV);

2.3 APlayerCameraManager::UpdateViewTarget

UpdateViewTarget 根据 ViewTarget 内的 Target 计算对应的 POV 信息(核心逻辑)

1. 如果TargetActor是CameraActor类型的,调用其CameraComponent的GetCameraView()方法

	if (ACameraActor* CamActor = Cast<ACameraActor>(OutVT.Target))
	{
		// Viewing through a camera actor.
		CamActor->GetCameraComponent()->GetCameraView(DeltaTime, OutVT.POV);
	}

2. 否则根据设置的CameraStyle进行相应的处理,如果CameraStyle为默认值则调用UpdateViewTargetInternal()方法

	    static const FName NAME_Fixed = FName(TEXT("Fixed"));
		static const FName NAME_ThirdPerson = FName(TEXT("ThirdPerson"));
		static const FName NAME_FreeCam = FName(TEXT("FreeCam"));
		static const FName NAME_FreeCam_Default = FName(TEXT("FreeCam_Default"));
		static const FName NAME_FirstPerson = FName(TEXT("FirstPerson"));

		if (CameraStyle == NAME_Fixed)
		{
            ...........
		}
		else if (CameraStyle == NAME_ThirdPerson || CameraStyle == NAME_FreeCam || CameraStyle == NAME_FreeCam_Default)
		{
            ......
		}
		else if (CameraStyle == NAME_FirstPerson)
		{
	        .......
		}
		else
		{
			UpdateViewTargetInternal(OutVT, DeltaTime);
		}
	}

3. ApplyCameraModifiers 处理特殊的镜头需求;比如:CameraShake、粒子效果等

	if (!bDoNotApplyModifiers || bAlwaysApplyModifiers)
	{
		// Apply camera modifiers at the end (view shakes for example)
		ApplyCameraModifiers(DeltaTime, OutVT.POV);
	}

4. 更新CameraManager的位置以及任何附加的相机镜头效果

	// Synchronize the actor with the view target results
	SetActorLocationAndRotation(OutVT.POV.Location, OutVT.POV.Rotation, false);

	UpdateCameraLensEffects(OutVT);

2.4 CameraManager::UpdateViewTargetInternal

本质上也是为了调用CameraComponent的GetCameraView()方法

void APlayerCameraManager::UpdateViewTargetInternal(FTViewTarget& OutVT, float DeltaTime)
{
	if (OutVT.Target)
	{
		FVector OutLocation;
		FRotator OutRotation;
		float OutFOV;

		if (BlueprintUpdateCamera(OutVT.Target, OutLocation, OutRotation, OutFOV))
		{
			OutVT.POV.Location = OutLocation;
			OutVT.POV.Rotation = OutRotation;
			OutVT.POV.FOV = OutFOV;
		}
		else
		{
			OutVT.Target->CalcCamera(DeltaTime, OutVT.POV);
		}
	}
}

优先调用蓝图中的实现,若没有,则调用Actor中的CalcCamera()方法

AActor::CalcCamera的实现如下:

void AActor::CalcCamera(float DeltaTime, FMinimalViewInfo& OutResult)
{
	if (bFindCameraComponentWhenViewTarget)
	{
		// Look for the first active camera component and use that for the view
		TInlineComponentArray<UCameraComponent*> Cameras;
		GetComponents(/*out*/ Cameras);

		for (UCameraComponent* CameraComponent : Cameras)
		{
			if (CameraComponent->IsActive())
			{
				CameraComponent->GetCameraView(DeltaTime, OutResult);
				return;
			}
		}
	}

	GetActorEyesViewPoint(OutResult.Location, OutResult.Rotation);
}

会在Actor中寻找CameraComponent,然后调用CameraComponent::GetCameraView()方法

如果没有找到Actor身上的CameraComponent,则调用GetActorEyesViewPoint()计算

void AActor::GetActorEyesViewPoint( FVector& OutLocation, FRotator& OutRotation ) const
{
	OutLocation = GetActorLocation();
	OutRotation = GetActorRotation();
}

2.5 UCameraComponent::GetCameraView

返回相机的视角。设置FMinimalViewInfo里的各项参数

1. 首先执行bUsePawnControlRotation = true的逻辑

	if (bUsePawnControlRotation)
	{
		const APawn* OwningPawn = Cast<APawn>(GetOwner());
		const AController* OwningController = OwningPawn ? OwningPawn->GetController() : nullptr;
		if (OwningController && OwningController->IsLocalPlayerController())
		{
			const FRotator PawnViewRotation = OwningPawn->GetViewRotation();
			if (!PawnViewRotation.Equals(GetComponentRotation()))
			{
				SetWorldRotation(PawnViewRotation);
			}
		}
	}

2. 设置Location和Rotation

	if (bUseAdditiveOffset)
	{
		FTransform OffsetCamToBaseCam = AdditiveOffset;
		FTransform BaseCamToWorld = GetComponentToWorld();
		FTransform OffsetCamToWorld = OffsetCamToBaseCam * BaseCamToWorld;

		DesiredView.Location = OffsetCamToWorld.GetLocation();
		DesiredView.Rotation = OffsetCamToWorld.Rotator();
	}
	else
	{
		DesiredView.Location = GetComponentLocation();
		DesiredView.Rotation = GetComponentRotation();
	}

3. 其余各项FOV、AspectRation等

	DesiredView.FOV = bUseAdditiveOffset ? (FieldOfView + AdditiveFOVOffset) : FieldOfView;
	DesiredView.AspectRatio = AspectRatio;
	DesiredView.bConstrainAspectRatio = bConstrainAspectRatio;
	DesiredView.bUseFieldOfViewForLOD = bUseFieldOfViewForLOD;
	DesiredView.ProjectionMode = ProjectionMode;
	DesiredView.OrthoWidth = OrthoWidth;
	DesiredView.OrthoNearClipPlane = OrthoNearClipPlane;
	DesiredView.OrthoFarClipPlane = OrthoFarClipPlane;

	if (bOverrideAspectRatioAxisConstraint)
	{
		DesiredView.AspectRatioAxisConstraint = AspectRatioAxisConstraint;
	}

	// See if the CameraActor wants to override the PostProcess settings used.
	DesiredView.PostProcessBlendWeight = PostProcessBlendWeight;
	if (PostProcessBlendWeight > 0.0f)
	{
		DesiredView.PostProcessSettings = PostProcessSettings;
	}

	// If this camera component has a motion vector simumlation transform, use that for the current view's previous transform
	DesiredView.PreviousViewTransform = FMotionVectorSimulation::Get().GetPreviousTransform(this);

参考

UE5 相机系统流程梳理及课外延申 - 知乎

UE4 Camera系统使用与源码分析 - 知乎

《Exploring in UE4》摄像机系统解析[经验总结] - 知乎

UE5 相机系统流程梳理及课外延申 - 知乎

  • 23
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ParaView是一个开源的可视化数据分析和处理软件,其源代码托管在GitHub上。要梳理ParaView的源代码,可以按照以下步骤进行: 1. 获取源代码:首先,您需要从ParaView的GitHub仓库中获取源代码。您可以使用Git工具克隆仓库或直接下载源代码的压缩包。ParaView的GitHub仓库链接为:https://github.com/Kitware/ParaView 2. 了解项目结构:解压源代码后,您可以浏览项目的目录结构。ParaView的源代码包含许多子模块和依赖项,其中主要的模块包括`VTK`(Visualization Toolkit)和`ParaViewCore`。`VTK`是ParaView所依赖的可视化工具包,而`ParaViewCore`则是ParaView的核心功能模块。 3. 阅读文档:ParaView提供了详细的开发者文档,可以帮助您理解项目的结构和各个模块的功能。您可以在ParaView官方网站上找到文档,其中包括ParaView用户指南、开发人员指南、API文档等。 4. 浏览源代码:通过浏览源代码,了解各个模块和类的实现细节。可以从`ParaViewCore`模块开始,深入研究ParaView的核心功能实现。也可以查看与特定功能相关的模块,例如与数据读取、可视化、过滤器、插件等相关的模块。 5. 调试和实验:如果您对特定功能或模块感兴趣,可以在代码中进行调试和实验。可以使用集成开发环境(IDE)来设置断点、观察变量值,并逐步执行代码,以便更好地理解代码的执行流程和数据处理过程。 6. 参考示例和扩展:ParaView的源代码中包含许多示例和扩展,可以帮助您更好地理解代码。可以查看ParaView的示例代码和插件,了解如何使用ParaView的API和数据结构。 请注意,ParaView的源代码庞大且复杂,需要一定的时间和耐心来理解。建议在梳理源代码之前具备一定的C++编程和可视化数据处理的基础知识。同时,参考官方文档和与ParaView社区的交流可以帮助您更好地理解和使用ParaView的源代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值