非常感谢匿名大哥一直对我的支持,本文内容由他赞助


#1. 视锥(Frustum)是什么

在相机的近裁剪面和远裁剪面之间的渲染范围内的空间叫做视锥空间(Frustum),通常情况下我们是不需要处理,但
当下比较流行动态遮挡剔除技术,只渲染视锥空间里面物体,再配合LOD等级来最大化效果,可以最小使用设备机能
Unity的FOV和Unreal的FOV有些区别
Unity的FOV是指视锥空间的张开角度,好比人眼睛的开闭角度
Unreal的FOV是指视锥空间的范围角度,基于水平面来伸开的


#2. 计算视锥需要的参数

相机自身的2个参数:FieldOfView(FOV)和Aspect Ratio宽高比

一般情况下还需要配合摇臂一起使用,Target Arm Length
CameraSpringArm默认延展的长度是反向距离,所以需要减掉forward反向的摇臂长度


#3. 计算方法

使用0为原点,视角FOV为90度,摇臂300米来进行推导
第一个有效计算三角形,图中绿色部分
一个角是90度,另外一个角度是45度的等腰三角形
这是视锥空间中有效的计算三角形,总计2个可用,分别位于左右两边,我们已知了一个角度和一条边

因为视锥空间是真3维空间,完全是在空间中计算的,通常情况下3维度空间的计算我们可以转为
2维空间来处理,比如玩家移动,此时朝上的轴向是固定的
在完全3维空间中计算,我们需要找投影,只有投影的向量才是有效的(相机也是投影成像)
找投影需要找一个可靠的平面来进行投影,需要是相互垂直的平面,可以构成勾股定律

计算一个有效中心点

原点:(0,0,0)
理论点:(300,300,0) - 摇臂(300,0,0)
未知中心点:(0, 300, 0)

获取相机的宽高比aspect ratio:1.5 width/height
300/1.5 = 200
未知顶点1:(0,300,200)
未知顶点1:(0,300,-200)


#4. 显示相机的视锥空间
有两种方法可以显示相机的视锥空间,编辑器显示或者代码显示
> 编辑器显示

> 使用UDrawFrustumComponent来显示

DrawFrustum = CreateDefaultSubobject<UDrawFrustumComponent>(TEXT("DrawFrustum"));
DrawFrustum->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
DrawFrustum->FrustumAspectRatio = FollowCamera->AspectRatio;
DrawFrustum->FrustumAngle = FollowCamera->FieldOfView;
DrawFrustum->FrustumStartDist = 10.0f;
DrawFrustum->FrustumEndDist = 1010.0f;

代码显示的好处是可以在任意位置显示,编辑器默认跟随相机显示


#5. 代码绘制DebugLine
在绘制之前,我也不知道能不在空间中把相机的视锥边缘绘制出来,最关键的是如果我们不做,
那么功能一定是不能完成的,因此我们应该尝试迈出第一步,其中有3种常见情况,
分别是镜头默认在原点,镜头往上移动,镜头往下移动
> 镜头默认在原点

这种情况下,我们可以直接计算

//lv1.在原点,无旋转最简单的情况下
//1.通过角度计算对边(半边)
//2.通过比例计算上下顶点
//3.计算其余的两点
void AViewFrustumCharacter::DrawFrustumPlane(float Dist)
{float Angle = FollowCamera->FieldOfView / 2;float ToRadians = FMath::DegreesToRadians(Angle);float Tan = FMath::Tan(ToRadians);float HalfWidth = Tan * Dist;float AspectRatio = FollowCamera->AspectRatio;float HalfHeight = HalfWidth / AspectRatio;GLog->Logf(TEXT("Tan:%f Angle:%f HalfWidth:%f HalfHeight:%f"), Tan, Angle, HalfWidth, HalfHeight);FVector RightUpPoint = FVector(0, HalfWidth, HalfHeight);FVector RightBottomPoint = FVector(0, HalfWidth, -HalfHeight);FVector LeftUpPoint = FVector(0, -HalfWidth, HalfHeight);FVector LeftBottomPoint = FVector(0, -HalfWidth, -HalfHeight);DrawDebugLine(GetWorld(), RightUpPoint, RightBottomPoint, FColor::Green, true, -1, 0, 5);DrawDebugLine(GetWorld(), LeftUpPoint, LeftBottomPoint, FColor::Green, true, -1, 0, 5);DrawDebugLine(GetWorld(), LeftUpPoint, RightUpPoint, FColor::Green, true, -1, 0, 5);DrawDebugLine(GetWorld(), LeftBottomPoint, RightBottomPoint, FColor::Green, true, -1, 0, 5);
}

> 镜头左右有旋转

这种情况下,需要计算旋转之后的偏移,我们可以直接使用集成的API来计算
我直接在Google上找到了解决办法

//lv2.相机有旋转,只有左右旋转的情况下
void AViewFrustumCharacter::DrawFrustumPlaneEx1(float Dist)
{float Angle = FollowCamera->FieldOfView / 2;float ToRadians = FMath::DegreesToRadians(Angle);float Tan = FMath::Tan(ToRadians);float HalfWidth = Tan * Dist;float AspectRatio = FollowCamera->AspectRatio;float HalfHeight = HalfWidth / AspectRatio;GLog->Logf(TEXT("Tan:%f Angle:%f HalfWidth:%f HalfHeight:%f"), Tan, Angle, HalfWidth, HalfHeight);FVector RightUpPoint = FVector(0, HalfWidth, HalfHeight);FVector RightBottomPoint = FVector(0, HalfWidth, -HalfHeight);FVector LeftUpPoint = FVector(0, -HalfWidth, HalfHeight);FVector LeftBottomPoint = FVector(0, -HalfWidth, -HalfHeight);FRotator Rotation = GetActorRotation();FVector EulerAngle = Rotation.Euler();FVector NewRightUpPoint = RightUpPoint.RotateAngleAxis(EulerAngle.Z, FVector::UpVector);FVector NewRightBottomPoint = RightBottomPoint.RotateAngleAxis(EulerAngle.Z, FVector::UpVector);FVector NewLeftUpPoint = LeftUpPoint.RotateAngleAxis(EulerAngle.Z, FVector::UpVector);FVector NewLeftBottomPoint = LeftBottomPoint.RotateAngleAxis(EulerAngle.Z, FVector::UpVector);DrawDebugLine(GetWorld(), NewRightUpPoint, NewRightBottomPoint, FColor::Black, true, -1, 0, 5);DrawDebugLine(GetWorld(), NewLeftUpPoint, NewLeftBottomPoint, FColor::Black, true, -1, 0, 5);DrawDebugLine(GetWorld(), NewLeftUpPoint, NewRightUpPoint, FColor::Black, true, -1, 0, 5);DrawDebugLine(GetWorld(), NewLeftBottomPoint, NewRightBottomPoint, FColor::Black, true, -1, 0, 5);
}

> 镜头上下有旋转

上下旋转的时候,是反方向进行的

void AViewFrustumCharacter::DrawFrustumPlaneEx2(float Dist)
{float Angle = FollowCamera->FieldOfView / 2;float ToRadians = FMath::DegreesToRadians(Angle);float Tan = FMath::Tan(ToRadians);float HalfWidth = Tan * Dist;float AspectRatio = FollowCamera->AspectRatio;float HalfHeight = HalfWidth / AspectRatio;GLog->Logf(TEXT("Tan:%f Angle:%f HalfWidth:%f HalfHeight:%f"), Tan, Angle, HalfWidth, HalfHeight);FVector RightUpPoint = FVector(0, HalfWidth, HalfHeight);FVector RightBottomPoint = FVector(0, HalfWidth, -HalfHeight);FVector LeftUpPoint = FVector(0, -HalfWidth, HalfHeight);FVector LeftBottomPoint = FVector(0, -HalfWidth, -HalfHeight);FRotator Rotation = GetActorRotation();FVector EulerAngle = Rotation.Euler();// 上下的旋转是默认取反,下显示往上,上显示往下FVector NewRightUpPoint = RightUpPoint.RotateAngleAxis(-EulerAngle.Y, FVector::RightVector);FVector NewRightBottomPoint = RightBottomPoint.RotateAngleAxis(-EulerAngle.Y, FVector::RightVector);FVector NewLeftUpPoint = LeftUpPoint.RotateAngleAxis(-EulerAngle.Y, FVector::RightVector);FVector NewLeftBottomPoint = LeftBottomPoint.RotateAngleAxis(-EulerAngle.Y, FVector::RightVector);DrawDebugLine(GetWorld(), NewRightUpPoint, NewRightBottomPoint, FColor::Purple, true, -1, 0, 5);DrawDebugLine(GetWorld(), NewLeftUpPoint, NewLeftBottomPoint, FColor::Purple, true, -1, 0, 5);DrawDebugLine(GetWorld(), NewLeftUpPoint, NewRightUpPoint, FColor::Purple, true, -1, 0, 5);DrawDebugLine(GetWorld(), NewLeftBottomPoint, NewRightBottomPoint, FColor::Purple, true, -1, 0, 5);
}

UE Gameplay入门51(相机视锥空间的计算方法推导)相关推荐

  1. UE Gameplay入门47(骨架网格体的动画片段)

    动画片段是指原始的fbx切片动画 粉色的骨架网格体,浅绿色的动画片段 #1. 菜单功能区域 前面的Save.Browse.Preview Mesh和以往的都一样 Create Asset:该动画片段可 ...

  2. UE Gameplay入门48(骨架网格体的混合空间)

    #1. 混合空间是什么 混合空间 利用基于特定属性或条件进行混合的资源,降低创建单个硬编码节点进行混合动画的需求.它使动画师或程序员能够指定输入.动画,以及输入在动画之间进行混合的方式,实际上任何类型 ...

  3. UE Gameplay Learning Record

    UE Gameplay Learning Type: #Learn Tags: #UnrealEngine #Gameplay Status: #Doing Time: 2022-12-10 11:2 ...

  4. UE GamePlay框架(一) GameInstance、SaveGame

    UE GamePlay框架(一) GameInstance.SaveGame 一.GameInstance game约等于进程 哪些逻辑应该放在GameInstance? 逻辑层面: Worlds,G ...

  5. UE GamePlay学习笔记

    初学UE就一直对其中的基本类有一些问题,UE中的ACtor.Pawn.Character.Controller.PlayerController这些究竟是什么?他们的作用是什么?应该在用在什么地方?最 ...

  6. UE Gameplay实例49(高级蒙太奇动画连招)

    这个连招系统是在YTB上搬运过来的,也是自我接触UE以来,认为最好用,最高级的一个连招使用方式, 非常完美的利用了UE中强大的动画编辑器来实现连招,对于程序员来说轻松.省事:对于策划来说, 我可以随便 ...

  7. 最大化使用51的RAM空间

    目录 初衷 使用bit定义变量,或"降级"变量的类型 减小缓存的大小 将大型的数组放在外部RAM 真正能用于产品的代码为非阻塞代码,需要做分层和隔离,为此使用了队列结构,另外为了标 ...

  8. 单片机的入门-51单片机

    想学习单片机的同学可以关注.私信我或者在评论区回复我要入门.早期可能是绝大多数嵌入式工程师或者电子爱好者接触的第一款单片机--51单片机,不像现在很多学习单片机的朋友都跳过51单片机直接学起了STM3 ...

  9. 从零学习入门51单片机和STM32单片机

    ❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分:建议先学习51单片机,其是STM32等高级单片机的基础:这样再学习STM32时才能融会贯通. ☀️ 专栏适 ...

最新文章

  1. 物联网背后的网络安全风险
  2. 地图之CLLocationManager的使用 定位功能使用
  3. 关于选择哪些村庄试点新农村建设的讨论
  4. Kali Linux Aircrack-ng简单破解WEP加密方式网络
  5. Theano中的Function
  6. 中兴通讯午后复牌:A股涨停 港股盘中涨逾53%
  7. java正则表达式提取需要的字符并放入数组
  8. 关于在openstack执行nova get-vnc-console命令,无法得到vnc url并提示服务器超时的问题描述...
  9. mac桌面与屏膜保护程序卡死完美解决方法
  10. JS中uibModal服务
  11. ES6的Map数据结构
  12. 1.Influxdb使用1
  13. mysql 1786_【MySQL案例】ERROR 1786 (HY000)
  14. Docker容器化技术教程,24小时快速入门
  15. 刚装修的房子多久能住?集成墙面真的好吗?
  16. Latex ulem包设置下划线删除线强调文本等效果
  17. ISP(图像信号处理)之——坏点校正
  18. java无法重命名文件_巧妙地解决Java文件重命名失败的问题
  19. 申请国家发明专利费用是多少?
  20. Webapck 体系基础

热门文章

  1. 【经典算法题】水壶问题
  2. Segmented CRC-Aided SC List Polar Decoding
  3. 【图像识别】基于计算机视觉实现水果识别matlab代码
  4. php 微信开发 自动回复,关于微信自动回复的详细介绍
  5. 求字符串长度的多种方法
  6. 【毕业设计教程】单片机接入按键控制遥感 -嵌入式 物联网 stm32 c51
  7. Bloom Filter (布隆过滤器)
  8. 树莓派简单配置.txt
  9. MongoDB数据库账号管理
  10. 怎样把XPS文件转换成Word文档?XPS转Word方法分享