UE4 自定义 FOV

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(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(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();
}

你可能感兴趣的:(UE4 自定义 FOV)