FPS 游戏第一人称枪械使用统一的 fov 会产生广角畸变。这是可能需要自定义 fov。在官方的 answer hub 上找到了自定义 fov 的方法。https://answers.unrealengine.com/questions/25526/custom-mesh-fov.html
继承自 PrimitiveComponent
的组件都有一个函数 GetRenderMatrix
,返回一个从 local space 到 world space 的变换矩阵。之后还要经历 view transform 和 projection transform。
重载这个函数,将 view transform 和自定义的 fov 的 projection transform 应用到这个变换矩阵上。最后乘上原 fov 的 view transform 和 projection transform 的逆矩阵,来抵消原 fov 的变换。
FMatrix CustomFOVMeshComponent::GetRenderMatrix() const
{
//Get camera perspectiveMatrix
APlayerController* playerController = GetWorld()->GetFirstPlayerController();
if (playerController)
{
ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(playerController->Player);
if (LocalPlayer != NULL && LocalPlayer->ViewportClient != NULL && LocalPlayer->ViewportClient->Viewport != NULL)
{
FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(
LocalPlayer->ViewportClient->Viewport,
GetWorld()->Scene,
LocalPlayer->ViewportClient->EngineShowFlags)
.SetWorldTimes(0.0f, 0.0f, 0.0f)
.SetRealtimeUpdate(false));
FVector ViewLocation;
FRotator ViewRotation;
float MinZ = 3.0f;
float MaxZ = MinZ;
// Avoid zero ViewFOV's which cause divide by zero's in projection matrix
float MatrixFOV = FMath::Max(0.001f, WeaponFOV) * (float)PI / 360.0f;
FMatrix projMatrix = FReversedZPerspectiveMatrix(
MatrixFOV,
MatrixFOV,
1.0f,
LocalPlayer->Size.X * LocalPlayer->ViewportClient->Viewport->GetSizeXY().X / (LocalPlayer->Size.Y * LocalPlayer->ViewportClient->Viewport->GetSizeXY().Y),
MinZ,
MaxZ
);
FScaleMatrix ClipSpaceFixScale(FVector(1.0f, 1.0f, 1.0f - 0.0f));
FTranslationMatrix ClipSpaceFixTranslate(FVector(0.0f, 0.0f, 0.0f));
projMatrix = projMatrix * ClipSpaceFixScale * ClipSpaceFixTranslate;
FMatrix viewMatrix;
FMatrix invViewProjectionMatrix;
GetViewMatrices(viewMatrix, invViewProjectionMatrix);
FMatrix adjustedViewProjectMatrix = viewMatrix * projMatrix;
FMatrix inverseOldViewProjectMatrix = invViewProjectionMatrix;
FMatrix adjTransform = ComponentToWorld.ToMatrixWithScale() * adjustedViewProjectMatrix * inverseOldViewProjectMatrix;
return adjTransform;
}
else
{
return Super::GetRenderMatrix();
}
}
else
{
return Super::GetRenderMatrix();
}
}
void CustomFOVMeshComponent::GetViewMatrices(FMatrix& viewMatrix, FMatrix& invViewProjectionMatrix) const
{
APlayerController* playerController = GetWorld()->GetFirstPlayerController();
ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(playerController->Player);
//Get View Origin
FVector ViewOrigin;
FRotator ViewRotation;
playerController->GetPlayerViewPoint(/*out*/ ViewOrigin, /*out*/ ViewRotation);
FMatrix ViewRotationMatrix = FInverseRotationMatrix(ViewRotation) * FMatrix(
FPlane(0, 0, 1, 0),
FPlane(1, 0, 0, 0),
FPlane(0, 1, 0, 0),
FPlane(0, 0, 0, 1));
if (!ViewRotationMatrix.GetOrigin().IsNearlyZero(0.0f))
{
ViewOrigin += ViewRotationMatrix.InverseTransformPosition(FVector::ZeroVector);
ViewRotationMatrix = ViewRotationMatrix.RemoveTranslation();
}
// Calculate view matrix
viewMatrix = FTranslationMatrix(-ViewOrigin) * ViewRotationMatrix;
// Calculate project matrix
int32 X = FMath::TruncToInt(LocalPlayer->Origin.X * LocalPlayer->ViewportClient->Viewport->GetSizeXY().X);
int32 Y = FMath::TruncToInt(LocalPlayer->Origin.Y * LocalPlayer->ViewportClient->Viewport->GetSizeXY().Y);
uint32 SizeX = FMath::TruncToInt(LocalPlayer->Size.X * LocalPlayer->ViewportClient->Viewport->GetSizeXY().X);
uint32 SizeY = FMath::TruncToInt(LocalPlayer->Size.Y * LocalPlayer->ViewportClient->Viewport->GetSizeXY().Y);
FIntRect UnconstrainedRectangle = FIntRect(X, Y, X + SizeX, Y + SizeY);
FSceneViewProjectionData ProjectionData;
ProjectionData.SetViewRectangle(UnconstrainedRectangle);
FMinimalViewInfo OutViewInfo;
if (playerController->PlayerCameraManager != NULL)
{
OutViewInfo = playerController->PlayerCameraManager->CameraCache.POV;
OutViewInfo.FOV = playerController->PlayerCameraManager->GetFOVAngle();
playerController->GetPlayerViewPoint(/*out*/ OutViewInfo.Location, /*out*/ OutViewInfo.Rotation);
}
else
{
playerController->GetPlayerViewPoint(/*out*/ OutViewInfo.Location, /*out*/ OutViewInfo.Rotation);
}
FMinimalViewInfo::CalculateProjectionMatrixGivenView(OutViewInfo, LocalPlayer->AspectRatioAxisConstraint, LocalPlayer->ViewportClient->Viewport, /*inout*/ ProjectionData);
FMatrix ProjMatrix = ProjectionData.ProjectionMatrix;
FViewMatrices ViewMatrices;
ViewMatrices.ViewMatrix = viewMatrix;
ViewMatrices.ProjMatrix = ProjMatrix;
invViewProjectionMatrix = ViewMatrices.GetInvViewProjMatrix();
}
网友评论