迹忆客 专注技术分享

当前位置:主页 > 学无止境 > 编程语言 > C++ >

C++ 中射线与平面的交集

作者:迹忆客 最近更新:2023/03/31 浏览次数:

在本文中,首先,我们将获得有关 C++ 中光线平面相交的完整指导。

首先,我们将讨论矢量运算及其实现。 接下来,我们将讨论射线平面相交的概念及其在 C++ 中的实现。


在 C++ 中使用向量运算

在这里,我们将介绍相关的矢量运算及其在 C++ 中的实现。 我们需要以下功能:

  1. 减号运算符:计算两点之间的距离。
  2. 点积:计算两个向量之间点积的函数,其结果为实数。
  3. 乘法运算符:计算两个向量的叉积。
  4. 带浮点参数的乘法运算符:计算向量和标量值之间的标量乘法。
  5. 幅度函数:计算向量的幅度。
  6. 归一化函数:计算向量的法线。
  7. 最后,流运算符被重载以显示向量。

这是完整的矢量 3D 类。 main 函数检查/演示 vector 3D 类的成员函数。

#include <iostream>
#include <math.h>

using namespace std;

class Vec3{
    float x;
    float y;
    float z;
public:
    Vec3(){ x, y, z = 0;}
    Vec3(float x, float y, float z){
        this->x = x;
        this->y = y;
        this->z = z;
    }
    Vec3& operator += (const Vec3 &b){
        x = x + b.x;
        y = y + b.y;
        z = z + b.z;
        return *this;
    }
    Vec3& operator-=(const Vec3 &b){
        x = x - b.x;
        y = y - b.y;
        z = z - b.z;
        return *this;
    }
    Vec3 operator + (const Vec3 &b){
        Vec3 newV = *this;
        newV += b;
        return newV;
    }
    Vec3 operator - (const Vec3 &b){
        Vec3 newV = *this;
        newV -= b;
        return newV;
    }
    Vec3 operator * (const Vec3 &b){    //cross operator
        Vec3 newV;
        newV.x = y * b.z - z * b.y;
        newV.y = x * b.z - z * b.x;
        newV.z = x * b.y - y * b.x;
        return newV;
    }
    Vec3& operator *= (const float s){    //Dot equal operator
        x = x * s;
        y = y * s;
        z = z * s;
        return *this;
    }
    Vec3 operator * (const float s){    //Dot equal operator
        Vec3 newV = *this;
        return newV *= s;
    }
    float mag(){    return sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2));    }
    Vec3& normalize(){
        double mag = this->mag();
        x /= mag;
        y /= mag;
        z /= mag;
        return *this;
    }
    //Dot operator
    float dot(const Vec3 &b){    return x*b.x + y*b.y + z*b.z;    }
    friend ostream& operator << (ostream &out, const Vec3 &v){
        out << "(" << v.x << ", " << v.y << ", " << v.z << ")\n";
        return out;
    }
};

int main(){
    Vec3 v1(-5, 7, 2);
    Vec3 v2(4, 12, 1);
    Vec3 v3(1, 1, 1);
    cout << "Vec1 = " << v1 << endl;
    v1+=v2;
    cout << "Result of adding Vec2 in Vec1 = " << v1 << endl;
    v1-=v3;
    cout << "Result of subtracting Vec3 from vec1 = " << v1 << endl;
    v1*=5;//dot operation
    cout << "Resultant after the scaling of Vec1 with a value of 5 = " << v1 << endl;
    cout << "Magnitude for Vec1 = " << v1.mag() << endl;
    v1.normalize();
    cout << "Vec1 after normalization = " << v1 << endl;
    cout << "Dot product of Vec1 and Vec2 = " << v1*v2 << endl;
    return 0;
}

这是主函数的输出,显示了矢量 3D 类的准确实现。

Vec1 = (-5, 7, 2)

Result of adding Vec2 in Vec1 = (-1, 19, 3)

Result of subtracting Vec3 from vec1 = (-2, 18, 2)

Resultant after the scaling of Vec1 with a value of 5 = (-10, 90, 10)

Magnitude for Vec1 = 91.1043
Vec1 after normalization = (-0.109764, 0.987878, 0.109764)

Dot product of Vec1 and Vec2 = (-0.329293, -0.548821, -5.26868)

C++ 中射线与平面的交集

从这里开始,我们假设读者对矢量运算的概念相当熟悉。 我们进一步假设读者对平面有基本的了解。

如果您对这些概念感到不自在,请点击此链接了解有关矢量运算和平面的更多信息。

让我们看看一些正式的数学事实和细节,以找到平面中的光线交点。

我们知道两个正交或垂直向量的点积总是0。假设a和b是两个垂直或正交向量,则a.b=0。

现在,考虑平面上的点 p0,表示平面与原点和垂直于平面的矢量 n 的距离。 我们可以通过从点 p0 减去平面上的任意点来计算向量 p。

合成矢量位于平面内并垂直于平面法线。

这个事实给了我们等式:

(p-p0) . N = 0                                            (i)

l0l 分别视为射线的起点和射线的方向。 我们可以使用参数形式到达平面(意味着相交于点 p):

l0 + l * t = p                                            (ii)

如果射线不平行于平面,则沿射线方向 t 次; 光线会使飞机感兴趣。 接下来,我们可以将等式(ii)中的p值代入等式(i),我们得到:

(l0 + l * t – p0) . n = 0

我们想要计算 t,这可以帮助我们使用参数方程计算交点的位置。 让我们来解方程:

l * t . n + (l0 – p0) . n = 0
l * t . n = - (l0 – p0) . n

t = - (l0 – p0) . n / l . n

我们需要确定分母中的线和平面法线的点积,因为如果值为 0 或接近 0,结果将是无穷大,这意味着光线和平面是平行的。 因此,我们将检查分母并返回 false,这意味着没有解决方案(即交点)。

下面的函数可以检查射线是否与平面相交:

bool intersection plane (Vec3 &n, Vec3 &p0, Vec3 & lo, Vec3 &l, float &t){
    //assuming vectors are all normalized
    float denom = n.dot(l);
    if (denom < 0.00005 && denom >-0.00005) /denom is near to 0
        return false;
    Vec3 p010 = p0 – l0;
    t = p010.dot(n);
    if t>=0)    return true;
    return false;
}

使用 if 条件,我们检查分母是否接近 0,这意味着会有无限结果。 如果光线平行于平面,则平面法线与光线方向的点积为 0; 因此,我们返回 false。

否则,我们将计算 t 并返回 true,这意味着射线与平面相交。 我们可以使用 t 找到一个交点。

接下来,我们在下面有完整的代码来找到射线和平面的交点。

#include <iostream>
#include <math.h>

using namespace std;

class Vec3{
    float x;
    float y;
    float z;
public:
    Vec3(){    x, y, z = 0;}
    Vec3(float x, float y, float z){
        this->x = x;
        this->y = y;
        this->z = z;
    }
    Vec3&  operator += (const Vec3 &b){
        x = x + b.x;
        y = y + b.y;
        z = z + b.z;
        return *this;
    }
    Vec3& operator += (const float s){
        x = x + s;
        y = y + s;
        z = z + s;
        return *this;
    }
    Vec3 operator + (const float s){
        Vec3 newV = *this;
        return newV += s;
    }
    Vec3&  operator-=(const Vec3 &b){
        x = x - b.x;
        y = y - b.y;
        z = z - b.z;
        return *this;
    }
    Vec3 operator + (const Vec3 &b){
        Vec3 newV = *this;
        newV += b;
        return newV;
    }
    Vec3 operator - (const Vec3 &b){
        Vec3 newV = *this;
        newV -= b;
        return newV;
    }
    Vec3 operator * (const Vec3 &b){    //cross operator
        Vec3 newV;
        newV.x = y * b.z - z * b.y;
        newV.y = x * b.z - z * b.x;
        newV.z = x * b.y - y * b.x;
        return newV;
    }
    Vec3&  operator * (const float s){    //Dot equal operator
        x = x * s;
        y = y * s;
        z = z * s;
        return *this;
    }
    float mag(){    return sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2));    }
    Vec3&  normalize(){
        double mag = this->mag();
        x /= mag;
        y /= mag;
        z /= mag;
    }
    //Dot operator
    float dot(const Vec3 &b){    return x*b.x + y*b.y + z*b.z;    }
    friend ostream& operator << (ostream &out, const Vec3 &v){
        out << "(" << v.x << ", " << v.y << ", " << v.z << ")\n";
        return out;
    }
};

bool intersectPlane(Vec3 &n, Vec3 &p0, Vec3 &l0, Vec3 &l, float &t) {
    // considering vectors are normalized
    float denom = n.dot(l);    //dot product n.l
    if (denom > 1e-6) {
        Vec3 p0l0 = p0 - l0;
        t = n.dot(p0l0) / denom;
        return (t >= 0);
    }
    return false;
}

int main(){
    Vec3 n1(3, -9, 1);//Normal
    Vec3 p01(-4, 2, 2);
    Vec3 l01(1, 1, 1);
    Vec3 l1(1, 2, 1);
    float t;
    n1.normalize();
    p01.normalize();
    l01.normalize();
    l1.normalize();
    if (intersectPlane(n1, p01, l01, l1, t))
        cout << "T:" << t << '\n';
    else
        cout << "Ray is not intersecting the plane\n";
    cout << "------------------------\n";
    Vec3 n2(2, 2, -2);//Normal
    Vec3 p02(2, 4, 1);
    Vec3 l02(1, 1, 1);
    Vec3 l2(1, 2, 1);
    n2.normalize();
    p02.normalize();
    l02.normalize();
    l2.normalize();
    if (intersectPlane(n2, p02, l02, l2, t))
        cout << "T:" << t << '\n';
    else
        cout << "Ray is not intersecting the plane\n";
    Vec3 intersectionPoint = l02 + l2 * t;
    cout << intersectionPoint;
    return 0;
}

此代码的输出是:

Ray is not intersecting the plane
------------------------
T:0.629199
(0.83422, 1.09109, 0.83422)

如您所见,在我们的第一个数据点中,射线平行于平面。 因此,分母为0。

但是,在第二组数据点中,分母大于0,所以我们可以找到交点。

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

在 C++ 中通过掷骰子生成随机值

发布时间:2023/04/09 浏览次数:169 分类:C++

本文解释了如何使用时间因子方法和模拟 C++ 中的掷骰子的任意数方法生成随机数。了解它是如何工作的以及它包含哪些缺点。提供了一个 C++ 程序来演示伪数生成器。

在 C++ 中使用模板的链表

发布时间:2023/04/09 浏览次数:158 分类:C++

本文解释了使用模板在 C++ 中创建链表所涉及的各个步骤。工作程序演示了一个链表,该链表使用模板来避免在创建新变量时声明数据类型的需要。

在 C++ 中添加定时延迟

发布时间:2023/04/09 浏览次数:142 分类:C++

本教程将为你提供有关在 C++ 程序中添加定时延迟的简要指南。这可以使用 C++ 库为我们提供的一些函数以多种方式完成。

在 C++ 中创建查找表

发布时间:2023/04/09 浏览次数:155 分类:C++

本文重点介绍如何创建查找表及其在不同场景中的用途。提供了三个代码示例以使理解更容易,并附有代码片段以详细了解代码。

如何在 C++ 中把字符串转换为小写

发布时间:2023/04/09 浏览次数:63 分类:C++

介绍了如何将 C++ std::string 转换为小写的方法。当我们在考虑 C++ 中的字符串转换方法时,首先要问自己的是我的输入字符串有什么样的编码

如何在 C++ 中确定一个字符串是否是数字

发布时间:2023/04/09 浏览次数:163 分类:C++

本文介绍了如何检查给定的 C++ 字符串是否是数字。在我们深入研究之前,需要注意的是,以下方法只与单字节字符串和十进制整数兼容。

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便