1、获取fov
const double fov = UGameplayStatics::GetPlayerCameraManager(this, 0)->GetFOVAngle();
2、获取视点坐标(世界坐标系)和视口高度
if (auto world = this->GetWorld()) {
if (auto gameView = world->GetGameViewport())
{
if (auto viewport = gameView->Viewport)
{
//获取视口高度
const int32 viewportHeight = viewport->GetSizeXY().Y;
//视点的屏幕坐标
FVector2D viewportScreenCoord= FVector2D(viewport->GetSizeXY().X / 2, viewportHeight / 2);
FVector viewportWorldCoord, direction;
//viewportWorldCoord就是视点在世界坐标下的坐标
UGameplayStatics::DeprojectScreenToWorld(world->GetFirstPlayerController(), viewportScreenCoord, viewportWorldCoord, direction);
}
}
}
3、获取视椎体的near、top、bottom、left、right的平面(透视相机)
为什么没有far平面呢,跟踪源码可知,远平面并不是固定的是动态计算的,我目前的需求也不需要获取远平面。而且远平面也可以根据自己的需要动态指定
/// <summary>
/// 获取屏幕四个角点的屏幕坐标
/// </summary>
/// <param name="OutTopLeft">左上角</param>
/// <param name="OutTopRight">右上角</param>
/// <param name="OutBottomLeft">左下角</param>
/// <param name="OutBottomRight">右下角</param>
void getViewportCorners(FVector2D& OutTopLeft, FVector2D& OutTopRight, FVector2D& OutBottomLeft, FVector2D& OutBottomRight)
{
if (UWorld* world = GetWorld()) {
if (APlayerController* PlayerController = world->GetFirstPlayerController())
{
int32 sizeX, sizeY;
PlayerController->GetViewportSize(sizeX, sizeY);
OutTopLeft = FVector2D(0.0, 0.0);
OutTopRight = FVector2D(sizeX, 0);
OutBottomLeft = FVector2D(0, sizeY);
OutBottomRight = FVector2D(sizeX, sizeY);
}
}
}
/// <summary>
/// 根据屏幕坐标获取射线(射线方向是从视点到输入的屏幕坐标)
/// </summary>
/// <param name="InScreenPosition">屏幕坐标</param>
/// <param name="OutLineStartpoint">屏幕坐标点在世界坐标系下的坐标</param>
/// <param name="OutLineEndpoint">沿着射线方向一定距离的点</param>
void getFrustumEdgeEndpoint(FVector2D InScreenPosition, FVector& OutLineStartpoint, FVector& OutLineEndpoint)
{
if (UWorld* world = GetWorld()) {
FVector worldPosition, worldDirection;
UGameplayStatics::DeprojectScreenToWorld(world->GetFirstPlayerController(), InScreenPosition, worldPosition, worldDirection);
UCameraComponent* CameraComponent = world->GetFirstPlayerController()->GetPawn()->FindComponentByClass<UCameraComponent>();
FMinimalViewInfo CaptureView;
CameraComponent->GetCameraView(0, CaptureView);
const float farPlaneDistance = 0.0;
const float nearPlaneDistance = CaptureView.PerspectiveNearClipPlane > 0.0 ? CaptureView.PerspectiveNearClipPlane : GNearClippingPlane;
OutLineEndpoint = worldPosition + worldDirection * 100000;
OutLineStartpoint = worldPosition;
}
}
/// <summary>
/// 获取视椎体平面(平面法向量方向指向视椎体内部)
/// </summary>
/// <param name="OutLeftPlane"></param>
/// <param name="OutRightPlane"></param>
/// <param name="OutTopPlane"></param>
/// <param name="OutBottomPlane"></param>
/// <param name="OutNearPlane"></param>
void getViewFrustumPlanes(FPlane4d& OutLeftPlane, FPlane4d& OutRightPlane, FPlane4d& OutTopPlane, FPlane4d& OutBottomPlane, FPlane4d& OutNearPlane)
{
FVector2D topLeft, topRight, bottomLeft, bottomRight;
_getViewportCorners(topLeft, topRight, bottomLeft, bottomRight);
FVector topLeftStartpoint, topLeftEndpoint;
_getFrustumEdgeEndpoint(topLeft, topLeftStartpoint, topLeftEndpoint);
FVector topRightStartpoint, topRightEndpoint;
_getFrustumEdgeEndpoint(topRight, topRightStartpoint, topRightEndpoint);
FVector bottomLeftStartpoint, bottomLeftEndpoint;
_getFrustumEdgeEndpoint(bottomLeft, bottomLeftStartpoint, bottomLeftEndpoint);
FVector bottomRightStartpoint, bottomRightEndpoint;
_getFrustumEdgeEndpoint(bottomRight, bottomRightStartpoint, bottomRightEndpoint);
FVector leftPlaneNoraml = -FVector::CrossProduct((topLeftStartpoint - topLeftEndpoint), (bottomLeftEndpoint - topLeftEndpoint));
leftPlaneNoraml.Normalize();
OutLeftPlane = FPlane4d(topLeftEndpoint, leftPlaneNoraml);
FVector rightPlaneNoraml = -FVector::CrossProduct((topRightEndpoint - topRightStartpoint), (bottomRightStartpoint - topRightStartpoint));
rightPlaneNoraml.Normalize();
OutRightPlane = FPlane4d(topRightStartpoint, rightPlaneNoraml);
FVector topPlaneNoraml = FVector::CrossProduct((topLeftStartpoint - topLeftEndpoint), (topRightEndpoint - topLeftEndpoint));
topPlaneNoraml.Normalize();
OutTopPlane = FPlane4d(topLeftEndpoint, topPlaneNoraml);
FVector bottomPlaneNoraml = -FVector::CrossProduct((bottomLeftStartpoint - bottomLeftEndpoint), (bottomRightEndpoint - bottomLeftEndpoint));
bottomPlaneNoraml.Normalize();
OutBottomPlane = FPlane4d(bottomLeftEndpoint, bottomPlaneNoraml);
FVector nearPlaneNoraml = FVector::CrossProduct((topLeftStartpoint - topRightStartpoint), (bottomRightStartpoint - topRightStartpoint));
nearPlaneNoraml.Normalize();
OutNearPlane = FPlane4d(topRightStartpoint, nearPlaneNoraml);
}
/// <summary>
/// 获取osg坐标系下的视椎体平面(平面法向量方向指向视椎体内部)
/// </summary>
/// <param name="OutLeftPlane"></param>
/// <param name="OutRightPlane"></param>
/// <param name="OutTopPlane"></param>
/// <param name="OutBottomPlane"></param>
/// <param name="OutNearPlane"></param>
void getViewFrustumPlanes(osg::Plane& OutLeftPlane, osg::Plane& OutRightPlane, osg::Plane& OutTopPlane, osg::Plane& OutBottomPlane, osg::Plane& OutNearPlane)
{
FVector2D topLeft, topRight, bottomLeft, bottomRight;
_getViewportCorners(topLeft, topRight, bottomLeft, bottomRight);
FVector topLeftStartpoint, topLeftEndpoint;
_getFrustumEdgeEndpoint(topLeft, topLeftStartpoint, topLeftEndpoint);
FVector topRightStartpoint, topRightEndpoint;
_getFrustumEdgeEndpoint(topRight, topRightStartpoint, topRightEndpoint);
FVector bottomLeftStartpoint, bottomLeftEndpoint;
_getFrustumEdgeEndpoint(bottomLeft, bottomLeftStartpoint, bottomLeftEndpoint);
FVector bottomRightStartpoint, bottomRightEndpoint;
_getFrustumEdgeEndpoint(bottomRight, bottomRightStartpoint, bottomRightEndpoint);
auto ConvertCoordinate = [](FVector point) {
double X = point.X / 100.0;
double Y = -point.Y / 100.0;
double Z = point.Z / 100.0;
return osg::Vec3f(X, Y, Z);
};
osg::Vec3f leftPlaneNormalOsg = (ConvertCoordinate(topLeftStartpoint) - ConvertCoordinate(topLeftEndpoint)) ^ (ConvertCoordinate(bottomLeftEndpoint) - ConvertCoordinate(topLeftEndpoint));
leftPlaneNormalOsg.normalize();
OutLeftPlane = osg::Plane(leftPlaneNormalOsg, ConvertCoordinate(topLeftEndpoint));
osg::Vec3f rightPlaneNormalOsg = (ConvertCoordinate(topRightEndpoint) - ConvertCoordinate(topRightStartpoint)) ^ (ConvertCoordinate(bottomRightStartpoint) - ConvertCoordinate(topRightStartpoint));
rightPlaneNormalOsg.normalize();
OutRightPlane = osg::Plane(rightPlaneNormalOsg, ConvertCoordinate(topRightStartpoint));
osg::Vec3f topPlaneNormalOsg = -(ConvertCoordinate(topLeftStartpoint) - ConvertCoordinate(topLeftEndpoint)) ^ (ConvertCoordinate(topRightEndpoint) - ConvertCoordinate(topLeftEndpoint));
topPlaneNormalOsg.normalize();
OutTopPlane = osg::Plane(topPlaneNormalOsg, ConvertCoordinate(topLeftEndpoint));
osg::Vec3f bottomPlaneNormalOsg = (ConvertCoordinate(bottomLeftStartpoint) - ConvertCoordinate(bottomLeftEndpoint)) ^ (ConvertCoordinate(bottomRightEndpoint) - ConvertCoordinate(bottomLeftEndpoint));
bottomPlaneNormalOsg.normalize();
OutBottomPlane = osg::Plane(bottomPlaneNormalOsg, ConvertCoordinate(bottomLeftEndpoint));
osg::Vec3f nearPlaneNormalOsg = -(ConvertCoordinate(topLeftStartpoint) - ConvertCoordinate(topRightStartpoint)) ^ (ConvertCoordinate(bottomRightStartpoint) - ConvertCoordinate(topRightStartpoint));
nearPlaneNormalOsg.normalize();
OutNearPlane = osg::Plane(nearPlaneNormalOsg, ConvertCoordinate(topRightStartpoint));
}
4、视椎体剔除(在osg坐标系下进行剔除(我把虚幻坐标转换为osg坐标了,我的需求就是这样,也可以在虚幻坐标系下进行剔除))
struct Frustum {
osg::Plane leftPlane, rightPlane, topPlane, bottomPlane, nearPlane;
Frustum() {}
Frustum(const osg::Plane leftPlane, const osg::Plane rightPlane, const osg::Plane topPlane, const osg::Plane bottomPlane, const osg::Plane nearPlane) {
//要求视椎体的各个平面的法向量方向是指向视椎体内部的
this->leftPlane = leftPlane;
this->rightPlane = rightPlane;
this->topPlane = topPlane;
this->bottomPlane = bottomPlane;
this->nearPlane = nearPlane;
ExpandFrustum(5000.0);//扩大视椎体范围,将五个平面后移5000米
}
void ExpandFrustum(float distance) {
//沿着法向量反方向平移平面(向视椎体外部平移)
leftPlane[3] += distance;
rightPlane[3] += distance;
topPlane[3] += distance;
bottomPlane[3] += distance;
nearPlane[3] += distance;
}
bool IsInFrustum(const osg::BoundingBox InBoundingBox)
{
if (leftPlane.intersect(InBoundingBox) > -1 && rightPlane.intersect(InBoundingBox) > -1 && topPlane.intersect(InBoundingBox) > -1 && bottomPlane.intersect(InBoundingBox) > -1 && nearPlane.intersect(InBoundingBox) > -1) {
return true;
}
return false;
}
bool IsInFrustum(const osg::BoundingSphere InBoundingSphere)
{
if (leftPlane.intersect(InBoundingSphere) > -1 && rightPlane.intersect(InBoundingSphere) > -1 && topPlane.intersect(InBoundingSphere) > -1 && bottomPlane.intersect(InBoundingSphere) > -1 && nearPlane.intersect(InBoundingSphere) > -1) {
return true;
}
return false;
}
};