• UE4 MVP 坐标转换


    UE4 MVP 坐标转换

    局部的模型点从局部空间(相对空间)到世界空间以及到屏幕空间。
    
    • 1

    ViewProject

    视图投影矩阵

    	ULocalPlayer* const LP = Player ? Player->GetLocalPlayer() : nullptr;
    	if (LP && LP->ViewportClient)
    	{
    		// get the projection data
    		FSceneViewProjectionData ProjectionData;
    		if (LP->GetProjectionData(LP->ViewportClient->Viewport, eSSP_FULL, /*out*/ ProjectionData))
    		{ 
    			FMatrix const InvViewProjMatrix = ProjectionData.ComputeViewProjectionMatrix().InverseFast();
    		}
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    DeprojectScreenToWorld

    屏幕空间到世界空间。

    void FSceneView::DeprojectScreenToWorld(const FVector2D& ScreenPos, const FIntRect& ViewRect, const FMatrix& InvViewProjMatrix, FVector& out_WorldOrigin, FVector& out_WorldDirection)
    {
    	float PixelX = FMath::TruncToFloat(ScreenPos.X);
    	float PixelY = FMath::TruncToFloat(ScreenPos.Y);
    
    	// Get the eye position and direction of the mouse cursor in two stages (inverse transform projection, then inverse transform view).
    	// This avoids the numerical instability that occurs when a view matrix with large translation is composed with a projection matrix
    
    	// Get the pixel coordinates into 0..1 normalized coordinates within the constrained view rectangle
    	const float NormalizedX = (PixelX - ViewRect.Min.X) / ((float)ViewRect.Width());
    	const float NormalizedY = (PixelY - ViewRect.Min.Y) / ((float)ViewRect.Height());
    
    	// Get the pixel coordinates into -1..1 projection space
    	const float ScreenSpaceX = (NormalizedX - 0.5f) * 2.0f;
    	const float ScreenSpaceY = ((1.0f - NormalizedY) - 0.5f) * 2.0f;
    
    	// The start of the ray trace is defined to be at mousex,mousey,1 in projection space (z=1 is near, z=0 is far - this gives us better precision)
    	// To get the direction of the ray trace we need to use any z between the near and the far plane, so let's use (mousex, mousey, 0.5)
    	const FVector4 RayStartProjectionSpace = FVector4(ScreenSpaceX, ScreenSpaceY, 1.0f, 1.0f);
    	const FVector4 RayEndProjectionSpace = FVector4(ScreenSpaceX, ScreenSpaceY, 0.5f, 1.0f);
    
    	// Projection (changing the W coordinate) is not handled by the FMatrix transforms that work with vectors, so multiplications
    	// by the projection matrix should use homogeneous coordinates (i.e. FPlane).
    	const FVector4 HGRayStartWorldSpace = InvViewProjMatrix.TransformFVector4(RayStartProjectionSpace);
    	const FVector4 HGRayEndWorldSpace = InvViewProjMatrix.TransformFVector4(RayEndProjectionSpace);
    	FVector RayStartWorldSpace(HGRayStartWorldSpace.X, HGRayStartWorldSpace.Y, HGRayStartWorldSpace.Z);
    	FVector RayEndWorldSpace(HGRayEndWorldSpace.X, HGRayEndWorldSpace.Y, HGRayEndWorldSpace.Z);
    	// divide vectors by W to undo any projection and get the 3-space coordinate
    	if (HGRayStartWorldSpace.W != 0.0f)
    	{
    		RayStartWorldSpace /= HGRayStartWorldSpace.W;
    	}
    	if (HGRayEndWorldSpace.W != 0.0f)
    	{
    		RayEndWorldSpace /= HGRayEndWorldSpace.W;
    	}
    	const FVector RayDirWorldSpace = (RayEndWorldSpace - RayStartWorldSpace).GetSafeNormal();
    
    	// Finally, store the results in the outputs
    	out_WorldOrigin = RayStartWorldSpace;
    	out_WorldDirection = RayDirWorldSpace;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    ProjectWorldToScreen

    世界空间到屏幕空间。

    • 通过ViewProjectionMatrix矩阵把点从世界位置转为视口下,然后再从视口点转为近剪裁面上的点
    FPlane Result = ViewProjectionMatrix.TransformFVector4(FVector4(WorldPosition, 1.f));
    
    • 1
    • 通过投射除法映射到NDC空间[-1.1]
    // the result of this will be x and y coords in -1..1 projection space
    const float RHW = 1.0f / Result.W;
    FPlane PosInScreenSpace = FPlane(Result.X * RHW, Result.Y * RHW, Result.Z * RHW, Result.W);
    
    • 1
    • 2
    • 3
    • 通过半兰伯特手法把[-1.1]映射到[0,1]
    // Move from projection space to normalized 0..1 UI space
    const float NormalizedX = ( PosInScreenSpace.X / 2.f ) + 0.5f;
    const float NormalizedY = 1.f - ( PosInScreenSpace.Y / 2.f ) - 0.5f;
    
    • 1
    • 2
    • 3
    • 通过和屏幕宽高相乘获得屏幕的位置
    FVector2D RayStartViewRectSpace(
    			( NormalizedX * (float)ViewRect.Width() ),
    			( NormalizedY * (float)ViewRect.Height() )
    			);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    bool FSceneView::ProjectWorldToScreen(const FVector& WorldPosition, const FIntRect& ViewRect, const FMatrix& ViewProjectionMatrix, FVector2D& out_ScreenPos)
    {
    	FPlane Result = ViewProjectionMatrix.TransformFVector4(FVector4(WorldPosition, 1.f));
    	if ( Result.W > 0.0f )
    	{
    		// the result of this will be x and y coords in -1..1 projection space
    		const float RHW = 1.0f / Result.W;
    		FPlane PosInScreenSpace = FPlane(Result.X * RHW, Result.Y * RHW, Result.Z * RHW, Result.W);
    
    		// Move from projection space to normalized 0..1 UI space
    		const float NormalizedX = ( PosInScreenSpace.X / 2.f ) + 0.5f;
    		const float NormalizedY = 1.f - ( PosInScreenSpace.Y / 2.f ) - 0.5f;
    
    		FVector2D RayStartViewRectSpace(
    			( NormalizedX * (float)ViewRect.Width() ),
    			( NormalizedY * (float)ViewRect.Height() )
    			);
    
    		out_ScreenPos = RayStartViewRectSpace + FVector2D(static_cast<float>(ViewRect.Min.X), static_cast<float>(ViewRect.Min.Y));
    
    		return true;
    	}
    
    	return false;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    参考

    1、UE4 & OpenGL坐标系
    2、UE4 屏幕空间转世界空间

  • 相关阅读:
    MarqueeView - 跑马灯
    cmd启动MySQL服务显示服务名无效,MySQL服务无法启动
    搜维尔科技:Xsens在现实生活中实时控制虚幻人形机器人
    C#使用iKvm黑科技无缝接入JVM生态
    学习集合工具类CollectionUtils——List对象案例
    低代码如何在新时代为企业实现数字化转型
    计算机毕业设计选什么题目好?springboot 学生考勤管理系统
    linux命令
    下班后用微信处理工作时发病身亡,法院判决:工伤!
    解决 Yarn 运行时的 Node.js 版本问题:一个详尽的指南
  • 原文地址:https://blog.csdn.net/mrbaolong/article/details/126568908