鄭州建設(shè)企業(yè)網(wǎng)站百度一下 你就知道官方
目錄
前言
方法一?
原理
代碼
測試
結(jié)果
方法二
原理
一、檢查連線與球體的相交情況
二、檢查距離與球體半徑的關(guān)系
三、檢查連線與球體的相交
代碼
前言
???????通過數(shù)學(xué)原理判斷空間中任意兩點(diǎn)的連線是否穿過球體,再通過射線檢測檢驗(yàn)算法的正確性。
方法一?
原理
(1)設(shè)球體球心的坐標(biāo)為?,半徑為r;
(2)設(shè)線段中A點(diǎn)的坐標(biāo)為
,B點(diǎn)的坐標(biāo)為
(3)計(jì)算、
、
(4)計(jì)算點(diǎn)到?線段
的最短距離
(5)?如果,則線段
穿過球體;如果
,則線段
不穿過球體。
代碼
????????定義一個函數(shù)“IsCrossSphere”來判斷線段是否穿過球體,函數(shù)需要傳入點(diǎn)A、B的坐標(biāo)以及球心坐標(biāo)和球體半徑。
再定義一個結(jié)構(gòu)體作為函數(shù)返回值
函數(shù)“IsCrossSphere”的實(shí)現(xiàn)如下
頭文件:
// 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); //計(jì)算線段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中每幀去發(fā)射射線檢測并調(diào)用函數(shù)?“IsCrossSphere”,通過觀察射線碰撞結(jié)果以及函數(shù)?“IsCrossSphere”的打印是否相等來判斷算法是否有誤。(這里設(shè)置球體半徑固定為50,球體坐標(biāo)為(0,0,0))
結(jié)果
????????可以看到不論是線段在球體表面,穿過球體,還是在球體外,函數(shù)?“IsCrossSphere”與射線檢測的結(jié)果都是一致的。
方法二
原理
一、檢查連線與球體的相交情況
????????使用距離公式計(jì)算點(diǎn)?到球體中心
?的距離
,以及點(diǎn)?
到地球中心
?的距離
,設(shè)點(diǎn)
為
,點(diǎn)
為
。
二、檢查距離與球體半徑的關(guān)系
????????如果??且?
,則兩點(diǎn)都在球體內(nèi)部,它們的連線顯然穿過球體;
????????如果??且?
,則兩點(diǎn)都在球體外部,需要進(jìn)一步檢查它們的連線是否與球體相交。
三、檢查連線與球體的相交
????????當(dāng)兩點(diǎn)都在球體外部時,我們可以計(jì)算直線?AB?的參數(shù)方程,并嘗試找到與球體表面(即半徑為?R?的球體)的交點(diǎn)。將直線的參數(shù)方程代入球體的方程,并解出一個關(guān)于參數(shù)的二次方程。如果這個二次方程有實(shí)數(shù)解,并且解對應(yīng)的參數(shù)值在?0?和?1?之間(對于參數(shù)化的線段?AB),則連線與球體相交。
?(1)表示直線參數(shù)方程
????????對于線段的參數(shù)方程可以表示為
?
?????????其中是參數(shù),
表示線段
上的點(diǎn)
(2)表示球體方程
????????設(shè)球體球心的坐標(biāo)為?,半徑為
,則球體方程可表示為
(3)將直線的參數(shù)方程代入球體的方程中,得到一個關(guān)于的二次方程
?
?展開并整理后,可得到一個標(biāo)準(zhǔn)的二次方程形式:
(4)使用求根公式可以得到二次方程的解
(5)如果二次方程沒有實(shí)數(shù)解(),則直線不與球體相交;
? ? ? ? ?如果二次方程有一個實(shí)數(shù)解,并且解在?0≤t≤1?的范圍內(nèi),則線段?AB?與球體相交于一點(diǎn);
????????如果二次方程有兩個不同的實(shí)數(shù)解,并且至少有一個解在?0≤t≤1?的范圍內(nèi),則直線段?AB?與球體相交于兩點(diǎn)(即線段穿過球體)。
代碼
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)); //計(jì)算點(diǎn)A到球體中心的距離float D_B = sqrt(pow(pointB.X - sphereOrginPoint.X, 2) + pow(pointB.Y - sphereOrginPoint.Y, 2) + pow(pointB.Z - sphereOrginPoint.Z, 2)); //計(jì)算點(diǎn)B到球體中心的距離if (D_A <= sphereRadius && D_B <= sphereRadius) //兩點(diǎn)都在球體內(nèi)部,它們的連線顯然穿過球體{return true;}else if (D_A > sphereRadius && D_B > sphereRadius) //兩點(diǎn)都在球體外部{//將直線的參數(shù)方程代入球體的方程,得到標(biāo)準(zhǔn)二次方程的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{// 有兩個不同的實(shí)數(shù)解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 與球體相交于兩點(diǎn)}else {return false; //直線與球體在無限遠(yuǎn)處相交,即線段沒有穿過球體}}else if (discriminant == 0.0f) //△=0{// 有兩個相等的實(shí)數(shù)解(或說是一個重根)float t = (-1 * b) / (2 * a);if (t >= 0 && t <= 1){return true; //直線段 AB 與球體相交于一點(diǎn)}else{return false; //雖然直線在無限延伸的情況下會與球體相交,但交點(diǎn)并不在連接點(diǎn)A和點(diǎn)B的線段上,因此沒有相交}}else //△<0{// 沒有實(shí)數(shù)解,直線不與球體相交return false;}}else //一點(diǎn)在球體內(nèi)部,另一點(diǎn)在球體外部,則它們的連線一定穿過球體{return true;}
}