Schnittpunkt von Ray und Plane in C++

Abdul Mateen 12 Oktober 2023
  1. Verwenden Sie Vektoroperationen in C++
  2. Schnittpunkt von Ray und Plane in C++
Schnittpunkt von Ray und Plane in C++

In diesem Tutorial erhalten wir zunächst eine vollständige Anleitung zum Schnittpunkt der Strahlebene in C++.

Zuerst werden wir Vektoroperationen mit ihrer Implementierung diskutieren. Als nächstes werden wir das Konzept der Strahlebenenüberschneidung und seine Implementierung in C++ erörtern.

Verwenden Sie Vektoroperationen in C++

Hier behandeln wir verwandte Vektoroperationen und ihre Implementierung in C++. Wir benötigen folgende Funktionen:

  1. Minus-Operator: Um die Entfernung zwischen zwei Punkten zu berechnen.
  2. Punktprodukt: Eine Funktion zur Berechnung des Punktprodukts zwischen zwei Vektoren, was zu einer reellen Zahl führt.
  3. Multiplikationsoperator: Zur Berechnung des Kreuzprodukts zweier Vektoren.
  4. Multiplikationsoperator mit Float-Parameter: Zur Berechnung der Skalarmultiplikation zwischen Vektor und Skalarwert.
  5. Größenfunktion: Um die Größe des Vektors zu berechnen.
  6. Normalisierungsfunktion: Um die Normale eines Vektors zu berechnen.
  7. Schließlich wird der Stream-Operator überladen, um den Vektor anzuzeigen.

Hier ist die komplette Vektor-3D-Klasse. Die Funktion main prüft/demonstriert Mitgliedsfunktionen der Vektor-3D-Klasse.

#include <math.h>

#include <iostream>

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;
}

Hier ist die Ausgabe der Funktion main, die die genaue Implementierung der Vektor-3D-Klasse zeigt.

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)

Schnittpunkt von Ray und Plane in C++

Von hier an gehen wir davon aus, dass der Leser mit den Konzepten der Vektoroperationen vertraut ist. Wir gehen weiter davon aus, dass der Leser eine Grundvorstellung von Flugzeugen hat.

Wenn Sie mit diesen Konzepten nicht einverstanden sind, folgen Sie bitte diesem Link, um mehr über Vektoroperationen und Ebenen zu erfahren.

Schauen wir uns einige formale mathematische Fakten und Details an, um einen Schnittpunkt von Strahlen in der Ebene zu finden.

Wir wissen, dass das Skalarprodukt zweier orthogonaler oder senkrechter Vektoren immer 0 ist. Angenommen, a und b seien zwei senkrechte oder orthogonale Vektoren, dann ist a.b=0.

Betrachten Sie nun den Punkt p0 auf der Ebene, der den Abstand der Ebene vom Ursprung darstellt, und den Vektor n, der senkrecht zur Ebene steht. Wir können den Vektor p berechnen, indem wir einen beliebigen Punkt auf der Ebene vom Punkt p0 subtrahieren.

Der resultierende Vektor liegt in der Ebene und senkrecht zur Ebenennormalen.

Diese Tatsache gibt uns die Gleichung:

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

Betrachten Sie l0 und l als Startpunkt des Strahls bzw. als Richtung des Strahls. Wir können die Ebene erreichen (d. h. schneiden im Punkt p) indem wir die parametrische Form verwenden:

l0 + l * t = p                                            (ii)

Wenn der Strahl nicht parallel zur Ebene ist, dann t mal in Richtung des Strahls; der Strahl wird das Flugzeug interessieren. Als nächstes können wir den Wert von p aus Gleichung (ii) in die Gleichung (i) einsetzen und erhalten:

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

Wir wollen t berechnen, was uns helfen kann, die Position des Schnittpunkts mit Hilfe der parametrischen Gleichung zu berechnen. Lösen wir die Gleichung:

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

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

Wir müssen das Skalarprodukt der Linien- und Ebenennormalen bestimmen, die in den Nenner kommen, denn wenn der Wert 0 oder nahe 0 ist, ist das Ergebnis unendlich, was bedeutet, dass Strahl und Ebene parallel sind. Daher überprüfen wir den Nenner und geben false zurück, was bedeutet, dass es keine Lösung (d. h. den Schnittpunkt) gibt.

Die folgende Funktion kann prüfen, ob der Strahl die Ebene schneidet oder nicht:

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;
}

Mit der if-Bedingung prüfen wir, ob der Nenner nahe 0 ist, was bedeutet, dass es unendliche Ergebnisse geben wird. Das Skalarprodukt der Ebenennormalen mit der Strahlrichtung ergibt 0, wenn der Strahl parallel zur Ebene verläuft; deshalb geben wir false zurück.

Andernfalls berechnen wir t und geben true zurück, was bedeutet, dass der Strahl die Ebene schneidet. Einen Schnittpunkt finden wir mit t.

Als nächstes haben wir unten den vollständigen Code, um den Schnittpunkt eines Strahls und einer Ebene zu finden.

#include <math.h>

#include <iostream>

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;
}

Die Ausgabe dieses Codes ist:

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

Wie Sie sehen können, ist der Strahl in unserem ersten Datenpunkt parallel zur Ebene. Der Nenner ist also 0.

Im zweiten Satz von Datenpunkten ist der Nenner jedoch größer als 0, sodass wir den Schnittpunkt finden können.

Verwandter Artikel - C++ Math