【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 properties
	ALineIsCrossSphere();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

	UFUNCTION(BlueprintCallable)
	FStruct_Result_IsLineCrossSphere IsCrossSphere(FVector pointA, FVector pointB, FVector sphereOrginPoint, float sphereRadius);   //计算线段AB到球心的距离并判断AB是否穿过球体

public:	
	// Called every frame
	virtual 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、c
		float 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;
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/928297.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python办公——openpyxl处理Excel每个sheet每行 修改为软雅黑9号剧中+边框线

目录 专栏导读背景1、库的介绍①&#xff1a;openpyxl 2、库的安装3、核心代码4、完整代码5、最快的方法(50万行44秒)——表头其余单元格都修改样式总结 专栏导读 &#x1f338; 欢迎来到Python办公自动化专栏—Python处理办公问题&#xff0c;解放您的双手 &#x1f3f3;️‍…

Figma入门-约束与对齐

Figma入门-约束与对齐 前言 在之前的工作中&#xff0c;大家的原型图都是使用 Axure 制作的&#xff0c;印象中 Figma 一直是个专业设计软件。 最近&#xff0c;很多产品朋友告诉我&#xff0c;很多原型图都开始用Figma制作了&#xff0c;并且很多组件都是内置的&#xff0c…

8. Debian系统中显示屏免密码自动登录

本文介绍如何在Debian系统上&#xff0c;启动后&#xff0c;自动免密登录&#xff0c;不卡在登录界面。 1. 修改lightDM配置文件 嵌入式Debian系统采用lightDM显示管理器&#xff0c;所以&#xff0c;一般需要修改它的配置文件/etc/lightdm/lightdm.conf&#xff0c;找到[Seat…

Unity类银河战士恶魔城学习总结(P156 Audio Settings音频设置)

【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili 教程源地址&#xff1a;https://www.udemy.com/course/2d-rpg-alexdev/ 本章节实现了音频的大小设置与保存加载 音频管理器 UI_VolumeSlider.cs 定义了 UI_VolumeSlider 类&#xff0c;用于处理与音频设置相关的…

控制访问权限

Swift中的控制访问权限有5种&#xff0c;分别是private&#xff0c;fileprivate&#xff0c;public&#xff0c;open&#xff0c;intelnal。 如果我们没有写访问权限关键字时&#xff0c;默认的访问权限是intelnal 访问控制权限从高到低的顺序是&#xff1a;open > public…

单例模式的析构学习

1、例子 如果单例对象是类的static成员&#xff0c;那么在程序结束时不会调用类的析构函数&#xff0c;如下&#xff1a; #include <iostream> using namespace std;class A{ private:static A* m_ins;//声明&#xff0c;静态指针成员A(){} public:static A* getIns(){…

Function Arguments and Function Parameters (函数的实参和函数的形参)

Function Arguments and Function Parameters {函数的实参和函数的形参} 1. Object-Oriented Programming Using C2. Function Arguments and Function ParametersReferences 1. Object-Oriented Programming Using C https://icarus.cs.weber.edu/~dab/cs1410/textbook/index…

[SWPUCTF 2021 新生赛]gif好像有点大

[SWPUCTF 2021 新生赛]gif好像有点大 帧解一下 找到这个二维码用软件CQR解开一下 得到flag NSSCTF{The_G1F_ls_T00_b1g} [BJDCTF 2020]base?? 给了我们base64加密的密文 用python直接解密 import base64 dict{0: J, 1: K, 2: L, 3: M, 4: N, 5: O, 6: x, 7: y, 8: U, 9: …

嵌入式蓝桥杯学习1 点亮LED

cubemx配置 1.新建一个STM32G431RBT6文件 2.在System-Core中点击SYS&#xff0c;找到Debug&#xff08;设置为Serial Wire&#xff09; 3.在System-Core中点击RCC&#xff0c;找到High Speed Clock(设置为Crystal/Ceramic Resonator) 4.打开Clock Configuration &#xff0…

机器学习周志华学习笔记-第13章<半监督学习>

机器学习周志华学习笔记-第13章&#xff1c;半监督学习&#xff1e; 卷王&#xff0c;请看目录 13半监督学习13.1 生成式方法13.2 半监督SVM13.3 基于分歧的方法13.4 半监督聚类 13半监督学习 前面我们一直围绕的都是监督学习与无监督学习&#xff0c;监督学习指的是训练样本包…

安装MySQL 5.7 亲测有效

前言&#xff1a;本文是笔者在安装MySQL5.7时根据另一位博主大大的安装教程基础上做了一些修改而成 首先在这里表示对博主大大的感谢 下面附博主大大地址 下面的步骤言简意赅 跟着做就不会出错 希望各位读者耐下心来 慢慢解决安装中出现的问题~MySQL 5.7 安装教程&#xff08;全…

Navicat连接SQL Server及SpringBoot连接SQL Server(jtds)

Navicat连接SQL Server 安装自带的SQL Server客户端 去到Navicat安装目录&#xff0c;找到安装程序&#xff0c;安装即可。 安装对应版本的Microsoft ODBC Driver for SQL Server 打开Navicat输入对应的SQL Server相关信息 然后点测试连接&#xff0c;提示连接成功。 Spr…

vue+uniapp+echarts的使用(H5环境下echarts)

1.安装 npm install echarts4.9.0 --save // 带版本号 2.main.js中全局引用 // import echarts from echarts // 如果是5.0以上版本用这个 import * as echarts from echarts Vue.prototype.$echartsecharts 3.使用 <template><view id"box" style"w…

单片机的中断系统

作者简介 彭煜轩&#xff0c;男&#xff0c;银川科技学院计算机与人工智能学院&#xff0c;2022级计算机与科学技术8班本科生&#xff0c;单片机原理及应用课程第3组。 指导老师&#xff1a;王兴泽 电子邮件&#xff1a;1696409709qq.com 前言 本篇文章是参考《单片机原理…

C 语言 “神秘魔杖”—— 指针初相识,解锁编程魔法大门(一)

文章目录 一、概念1、取地址操作符&#xff08;&&#xff09;2、解引用操作符&#xff08;*&#xff09;3、指针变量1、 声明和初始化2、 用途 二、内存和地址三、指针变量类型的意义1、 指针变量类型的基本含义2、 举例说明不同类型指针变量的意义 四、const修饰指针1、co…

Scratch游戏推荐 | 我的世界:平台冒险——像素世界的全新挑战! ⛏️

&#x1f3ae; Scratch游戏推荐 | 我的世界&#xff1a;平台冒险——像素世界的全新挑战&#xff01; ⛏️&#x1f30d; 今天给大家推荐一款精彩绝伦的Scratch平台冒险游戏——《我的世界&#xff1a;平台冒险 – 第二章》&#xff01;由atomicmagicnumber制作&#xff0c;这…

Python 入门教程(2)搭建环境 | 2.4、VSCode配置Node.js运行环境

文章目录 一、VSCode配置Node.js运行环境1、软件安装2、安装Node.js插件3、配置VSCode4、创建并运行Node.js文件5、调试Node.js代码 一、VSCode配置Node.js运行环境 1、软件安装 安装下面的软件&#xff1a; 安装Node.js&#xff1a;Node.js官网 下载Node.js安装包。建议选择L…

【0x0019】HCI_Remote_Name_Request详解

目录 一、概述 二、命令格式参数说明 2.1. BD_ADDR 2.2. Page_Scan_Repetition_Mode 2.3. Reserved 2.4. Clock_Offset 三、响应事件及参数说明 3.1. HCI_Command_Status事件 3.2. &#xff08;可选&#xff09;HCI_Remote_Host_Supported_Features_Notification事件…

网络安全技术详解:虚拟专用网络(VPN) 安全信息与事件管理(SIEM)

虚拟专用网络&#xff08;VPN&#xff09;详细介绍 虚拟专用网络&#xff08;VPN&#xff09;通过在公共网络上创建加密连接来保护数据传输的安全性和隐私性。 工作原理 VPN的工作原理涉及建立安全隧道和数据加密&#xff1a; 隧道协议&#xff1a;使用协议如PPTP、L2TP/IP…

HTML5技术贴:现代网页开发的革命

引言 HTML5作为最新的HTML标准&#xff0c;为网页开发带来了革命性的变化。它不仅提高了网页的性能和可访问性&#xff0c;还提供了更多能有效增强网络应用的API。本文将深入探讨HTML5的八大新特性&#xff0c;帮助读者更好地理解和应用这些新特性。 HTML5的八大新特性 1. 语…