当前位置: 首页 > news >正文

【UE5 C++】判断两点连线是否穿过球体

目录

前言

方法一 

原理

代码

测试

结果

方法二

原理

一、检查连线与球体的相交情况

二、检查距离与球体半径的关系

三、检查连线与球体的相交

代码


前言

       通过数学原理判断空间中任意两点的连线是否穿过球体,再通过射线检测检验算法的正确性。

方法一 

原理

(1)设球体球心的坐标为O(C_{x},C_{y},C_{z}) ,半径为r;

(2)设线段AB中A点的坐标为A(x_{1},y_{1},z_{1}),B点的坐标为B(x_{2},y_{2},z_{2})

(3)计算\underset{OA}{\rightarrow}\underset{OB}{\rightarrow}\underset{AB}{\rightarrow}

(4)计算点O到 线段AB的最短距离d

d=\frac{|\underset{OA}{\rightarrow}\times \underset{AB}{\rightarrow}|}{|\underset{AB}{\rightarrow}|}

(5) 如果d\leq r,则线段AB穿过球体;如果d> r,则线段AB不穿过球体。

代码

        定义一个函数“IsCrossSphere”来判断线段AB是否穿过球体,函数需要传入点A、B的坐标以及球心坐标和球体半径。

再定义一个结构体作为函数返回值

函数“IsCrossSphere”的实现如下

头文件:

// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "LineIsCrossSphere.generated.h"USTRUCT(BlueprintType)
struct FStruct_Result_IsLineCrossSphere
{GENERATED_BODY();
public:UPROPERTY(BlueprintReadWrite)float distanceOfLineAndSphereCenter;UPROPERTY(BlueprintReadWrite)bool isCrossSphere;
};UCLASS()
class STUDY_API ALineIsCrossSphere : public AActor
{GENERATED_BODY()public:	// Sets default values for this actor's propertiesALineIsCrossSphere();protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;UFUNCTION(BlueprintCallable)FStruct_Result_IsLineCrossSphere IsCrossSphere(FVector pointA, FVector pointB, FVector sphereOrginPoint, float sphereRadius);   //计算线段AB到球心的距离并判断AB是否穿过球体public:	// Called every framevirtual void Tick(float DeltaTime) override;};

源文件:

// Fill out your copyright notice in the Description page of Project Settings.#include "Test/LineIsCrossSphere.h"// Sets default values
ALineIsCrossSphere::ALineIsCrossSphere()
{// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.PrimaryActorTick.bCanEverTick = true;}// Called when the game starts or when spawned
void ALineIsCrossSphere::BeginPlay()
{Super::BeginPlay();}FStruct_Result_IsLineCrossSphere ALineIsCrossSphere::IsCrossSphere(FVector pointA, FVector pointB, FVector sphereOrginPoint, float sphereRadius)
{bool isCrossSphere;FVector OA = pointA - sphereOrginPoint;FVector OB = pointB - sphereOrginPoint;FVector AB = OB - OA;FVector n = OA.Cross(AB);float D = n.Size() / AB.Size();if (D > sphereRadius){isCrossSphere =  false;}else{isCrossSphere =  true;}FStruct_Result_IsLineCrossSphere result;result.isCrossSphere = isCrossSphere;result.distanceOfLineAndSphereCenter = D;return result;
}// Called every frame
void ALineIsCrossSphere::Tick(float DeltaTime)
{Super::Tick(DeltaTime);}

测试

        在Tick中每帧去发射射线检测并调用函数 “IsCrossSphere”,通过观察射线碰撞结果以及函数 “IsCrossSphere”的打印是否相等来判断算法是否有误。(这里设置球体半径固定为50,球体坐标为(0,0,0))

结果

        可以看到不论是线段在球体表面,穿过球体,还是在球体外,函数 “IsCrossSphere”与射线检测的结果都是一致的。

方法二

原理

一、检查连线与球体的相交情况

        使用距离公式计算点 A到球体中心O 的距离d_{A},以及点 B到地球中心O 的距离d_{B},设点AA(x_{1},y_{1},z_{1}),点BB(x_{2},y_{2},z_{2})

d_{A}=\sqrt{(x_{1}-C_{x})^{2}+(y_{1}-C_{y})^{2}+(z_{1}-C_{z})^{2}}

d_{B}=\sqrt{(x_{2}-C_{x})^{2}+(y_{2}-C_{y})^{2}+(z_{2}-C_{z})^{2}}

二、检查距离与球体半径的关系

        如果 d_{A}\leq R 且 d_{B}\leq R,则两点都在球体内部,它们的连线显然穿过球体;

        如果 d_{A}> R 且 d_{B}> R,则两点都在球体外部,需要进一步检查它们的连线是否与球体相交。

三、检查连线与球体的相交

        当两点都在球体外部时,我们可以计算直线 AB 的参数方程,并尝试找到与球体表面(即半径为 R 的球体)的交点。将直线的参数方程代入球体的方程,并解出一个关于参数的二次方程。如果这个二次方程有实数解,并且解对应的参数值在 0 和 1 之间(对于参数化的线段 AB),则连线与球体相交。

 (1)表示直线参数方程

        对于线段AB的参数方程可以表示为

r(t)=(1-t)A+tB

r(t)=((1-t)x_{1}+tx_{2},(1-t)y_{1}+ty_{2},(1-t)z_{1}+tz_{2}) 

         其中t是参数,0\leqslant t\leqslant 1表示线段AB上的点

(2)表示球体方程

        设球体球心的坐标为O(C_{x},C_{y},C_{z}) ,半径为R,则球体方程可表示为

(x-C_{x})^{2}+(y-C_{y})^{2}+(z-C_{z})^{2}=R^{2}

(3)将直线的参数方程代入球体的方程中,得到一个关于t的二次方程

 

 展开并整理后,可得到一个标准的二次方程形式:at^{2}+bt+c=0

(4)使用求根公式可以得到二次方程的解

(5)如果二次方程没有实数解(\Delta < 0),则直线不与球体相交;

         如果二次方程有一个实数解,并且解在 0≤t≤1 的范围内,则线段 AB 与球体相交于一点;

        如果二次方程有两个不同的实数解,并且至少有一个解在 0≤t≤1 的范围内,则直线段 AB 与球体相交于两点(即线段穿过球体)。

代码

bool ALineIsCrossSphere::IsCrossSphere2(FVector pointA, FVector pointB, FVector sphereOrginPoint, float sphereRadius)
{float D_A = sqrt(pow(pointA.X - sphereOrginPoint.X, 2) + pow(pointA.Y - sphereOrginPoint.Y, 2) + pow(pointA.Z - sphereOrginPoint.Z, 2));  //计算点A到球体中心的距离float D_B = sqrt(pow(pointB.X - sphereOrginPoint.X, 2) + pow(pointB.Y - sphereOrginPoint.Y, 2) + pow(pointB.Z - sphereOrginPoint.Z, 2));  //计算点B到球体中心的距离if (D_A <= sphereRadius && D_B <= sphereRadius)  //两点都在球体内部,它们的连线显然穿过球体{return true;}else if (D_A > sphereRadius && D_B > sphereRadius)  //两点都在球体外部{//将直线的参数方程代入球体的方程,得到标准二次方程的a、b、cfloat a = pow(pointB.X - pointA.X, 2) + pow(pointB.Y - pointA.Y, 2) + pow(pointB.Z - pointA.Z, 2);float b = 2 * ((pointB.X - pointA.X) * (pointA.X - sphereOrginPoint.X) + (pointB.Y - pointA.Y) * (pointA.Y - sphereOrginPoint.Y) + (pointB.Z - pointA.Z) * (pointA.Z - sphereOrginPoint.Z));float c = pow(pointA.X - sphereOrginPoint.X, 2) + pow(pointA.Y - sphereOrginPoint.Y, 2) + pow(pointA.Z - sphereOrginPoint.Z, 2) - pow(sphereRadius, 2);float discriminant = b * b - 4 * a * c;if (discriminant > 0.0f)  //△>0{// 有两个不同的实数解float t1 = (-1*b + sqrt(pow(b, 2) - 4 * a * c)) / (2 * a);float t2 = (-1*b - sqrt(pow(b, 2) - 4 * a * c)) / (2 * a);if ((0 <= t1 && t1 <= 1) || (0 <= t2 && t2 <= 1)){return true;  //至少有一个解在0~1,则线段 AB 与球体相交于两点}else {return false;  //直线与球体在无限远处相交,即线段没有穿过球体}}else if (discriminant == 0.0f)  //△=0{// 有两个相等的实数解(或说是一个重根)float t = (-1 * b) / (2 * a);if (t >= 0 && t <= 1){return true;  //直线段 AB 与球体相交于一点}else{return false; //虽然直线在无限延伸的情况下会与球体相交,但交点并不在连接点A和点B的线段上,因此没有相交}}else  //△<0{// 没有实数解,直线不与球体相交return false;}}else  //一点在球体内部,另一点在球体外部,则它们的连线一定穿过球体{return true;}
}

相关文章:

【UE5 C++】判断两点连线是否穿过球体

目录 前言 方法一 原理 代码 测试 结果 方法二 原理 一、检查连线与球体的相交情况 二、检查距离与球体半径的关系 三、检查连线与球体的相交 代码 前言 通过数学原理判断空间中任意两点的连线是否穿过球体&#xff0c;再通过射线检测检验算法的正确性。 方法一 …...

【Blender】如何创建空心管道

步骤 1&#xff1a;创建一个圆柱体 添加圆柱体&#xff1a; 在 Object Mode 下按 Shift A > Mesh > Cylinder。 步骤 2&#xff1a;制作空心效果 进入编辑模式&#xff1a; 选中圆柱体&#xff0c;按 Tab 进入 Edit Mode。 删除顶部和底部面&#xff1a; 按 3 进入面选…...

ChromeBook11 HP G7EE 刷入Ubuntu的记录

设置开发模式-> 拆电池(解锁)-> 刷入bios ->使用u盘刷入系统。 下面是详细过程&#xff0c;除了拆机有点紧&#xff0c;没有难度(我不负责&#xff5e; 其实我试了好几次其他系统的&#xff0c;先进了pe&#xff0c;pe没问题(音频x)&#xff0c;有一个win10的u盘(几个…...

16asm - 汇编介绍 和 debug使用

文章目录 前言硬件运行机制微机系统硬件组成计算机系统组成8086cpu组织架构dosbox安装配置debug debug使用R命令D命令E命令U命令T命令A命令标志寄存器 总结 前言 各位师傅大家好&#xff0c;我是qmx_07&#xff0c;今天给大家讲解 十六位汇编 和 debug调试器的使用 硬件运行…...

初识QT第一天

思维导图 利用Qt尝试做出原神登陆界面 import sys from PyQt6.QtGui import QIcon, QPixmap, QMovie from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QLineEdit# 封装原神窗口类 class Genshin(QWidget):# 构造函数def __init__(self):# 初始化父类…...

ChatGPT科研应用、论文写作、课题申报、数据分析与AI绘图

随着人工智能技术的飞速发展&#xff0c;ChatGPT等先进语言模型正深刻改变着科研工作的面貌。从科研灵感的激发、论文的高效撰写&#xff0c;到课题的成功申报&#xff0c;乃至复杂数据的深度分析与可视化呈现&#xff0c;AI技术均展现出前所未有的潜力。其实众多科研前沿工作者…...

原子类、AtomicLong、AtomicReference、AtomicIntegerFieldUpdater、LongAdder

原子类 JDK提供的原子类&#xff0c;即Atomic*类有很多&#xff0c;大体可做如下分类&#xff1a; 形式类别举例Atomic*基本类型原子类AtomicInteger、AtomicLong、AtomicBooleanAtomic*Array数组类型原子类AtomicIntegerArray、AtomicLongArray、AtomicReferenceArrayAtomic…...

c语言——数组名该如何理解呢?

一般情况下&#xff0c;数组名表示首元素地址&#xff0c;以下2种除外&#xff1a; ①、sizeof(数组名) 表示整个数组 ※只有数组名的情况 sizeof&#xff08;数组名i&#xff09; 就不能表示整个数组 ②、&数组名 表示整个数组&#xff0c;取的是整个数…...

Linux学习笔记13 系统进程管理

前文 Linux学习笔记10 系统启动初始化&#xff0c;服务和进程管理&#xff08;上&#xff09;-CSDN博客 Linux学习笔记11 系统启动初始化&#xff0c;服务和进程管理&#xff08;下&#xff09;-CSDN博客 Linux学习笔记12 systemd的其他命令-CSDN博客 之前学习了怎么使用sy…...

Spring Boot 项目集成camunda流程引擎

Spring Boot 项目集成camunda流程引擎 camunda地址 camunda中文地址 使用camunda开源工作流引擎有&#xff1a;通过docker运行、使用springboot集成、部署camunda发行包、基于源代码编译运行等多种方式。 文本重点介绍如何在Spring Boot应用程序中如何集成Camunda Platform开…...

2024.12.2工作复盘

1.今天学了什么&#xff1f; 简单的写了一篇博客&#xff0c;是关于参数校验的问题&#xff0c;参数校验&#xff0c;一个是前后端校验到底一不一致&#xff0c;一个是绕过前端校验&#xff0c;看后台的逻辑到底能不能校验住。 2.今天解决了什么问题&#xff1f; 3.今天完成…...

Hot100 - 二叉树的中序遍历

Hot100 - 二叉树的中序遍历 最佳思路&#xff1a; 中序遍历的顺序是&#xff1a;左子树 -> 根节点 -> 右子树。为了实现这个顺序&#xff0c;我们可以利用栈来模拟递归过程&#xff0c;从而避免栈溢出的问题。在遍历过程中&#xff0c;始终向左子树深入&#xff0c;直到…...

docker build ubuntu ssh

dockerfile 构建镜像 为了使用Dockerfile构建Docker镜像&#xff0c;请遵循以下步骤&#xff1a; 创建一个名为Dockerfile的文件&#xff0c;并在其中定义镜像的构建指令。 FROM swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/ubuntu:24.04# 安装openssh-server和pas…...

三维路径规划|基于黑翅鸢BKA优化算法的三维路径规划Matlab程序

三维路径规划|基于黑翅鸢BKA优化算法的三维路径规划Matlab程序 文章目录 前言三维路径规划|基于黑翅鸢BKA优化算法的三维路径规划Matlab程序基于黑翅鸢BKA优化算法的三维路径规划一、研究基本原理二、黑翅鸢BKA优化算法的基本步骤&#xff1a;三、详细流程四、总结 二、实验结果…...

day01(Linux底层)基础知识

目录 导学 基础知识 1、Bootloader是什么 2、Bootloader的基本作用 3、入式中常见的Bootloader有哪些 4、Linux系统移植为什么要使用bootloader 5、uboot和Bootloader之间的关系 6.Uboot的获取 7、uboot版本命名 8、uboot版本选择 9、uboot的特点 10.Uboot使用 导学…...

flink学习(13)—— 重试机制和维表join

重试机制 当任务出现异常的时候&#xff0c;会直接停止任务——解决方式&#xff0c;重试机制 1、设置checkpoint后&#xff0c;会给任务一个重启策略——无限重启 2、可以手动设置任务的重启策略 代码设置 //开启checkpoint后&#xff0c;默认是无限重启&#xff0c;可以…...

第三方Cookie的消亡与Google服务器端标记的崛起

随着互联网用户对隐私保护的关注日益增强&#xff0c;各大浏览器正在逐步淘汰第三方Cookie。这一变革深刻影响了广告商和数字营销人员的用户跟踪和数据分析方式。然而&#xff0c;Google推出的服务器端标记技术为这一挑战提供了新的解决方案。 什么是第三方Cookie&#xff1f; …...

微信小程序——文档下载功能分享(含代码)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

Burp Suite 全面解析:开启你的 Web 安全测试之旅

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&a…...

Oracle DataGuard 主备正常切换 (Switchover)

前言 众所周知&#xff0c;DataGuard 的切换分为两种情况&#xff1a; 系统正常情况下的切换&#xff1a;这种方式称为 switchover&#xff0c;是无损切换&#xff0c;不会丢失数据。灾难情况下的切换&#xff1a;这种情况下一般主库已经启动不起来了&#xff0c;称为 failov…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

【Go语言基础【12】】指针:声明、取地址、解引用

文章目录 零、概述&#xff1a;指针 vs. 引用&#xff08;类比其他语言&#xff09;一、指针基础概念二、指针声明与初始化三、指针操作符1. &&#xff1a;取地址&#xff08;拿到内存地址&#xff09;2. *&#xff1a;解引用&#xff08;拿到值&#xff09; 四、空指针&am…...

CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝

目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为&#xff1a;一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...

Redis:现代应用开发的高效内存数据存储利器

一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发&#xff0c;其初衷是为了满足他自己的一个项目需求&#xff0c;即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源&#xff0c;Redis凭借其简单易用、…...