C++에서 광선과 평면의 교차점
이 자습서에서는 먼저 C++의 광선 평면 교차에 대한 완전한 지침을 얻을 것입니다.
먼저 구현과 함께 벡터 연산에 대해 논의합니다. 다음으로 레이-플레인 교차의 개념과 C++에서의 구현에 대해 논의할 것입니다.
C++에서 벡터 연산 사용
여기서는 관련 벡터 연산과 C++에서의 구현을 다룰 것입니다. 다음 기능이 필요합니다.
- 빼기 연산자: 두 점 사이의 거리를 계산합니다.
- 내적: 실수가 되는 두 벡터 간의 내적을 계산하는 함수입니다.
- 곱셈 연산자: 두 벡터의 외적을 계산합니다.
- float 매개변수가 있는 곱셈 연산자: 벡터와 스칼라 값 사이의 스칼라 곱셈을 계산합니다.
- 크기 함수: 벡터의 크기를 계산합니다.
- 정규화 기능: 벡터의 법선을 계산합니다.
- 마지막으로 스트림 연산자가 오버로드되어 벡터를 표시합니다.
다음은 완전한 벡터 3D 클래스입니다. main
기능은 벡터 3D 클래스의 멤버 기능을 확인/시연합니다.
#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;
}
다음은 벡터 3D 클래스의 정확한 구현을 보여주는 main
함수의 출력입니다.
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)
l0
과 l
을 광선의 시작점과 광선의 방향으로 각각 고려하십시오. 파라메트릭 형식을 사용하여 평면에 도달할 수 있습니다(점 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 <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;
}
이 코드의 출력은 다음과 같습니다.
Ray is not intersecting the plane
------------------------
T:0.629199
(0.83422, 1.09109, 0.83422)
보시다시피 첫 번째 데이터 포인트에서 광선은 평면에 평행합니다. 따라서 분모는 0입니다.
그러나 두 번째 데이터 포인트 세트에서 분모가 0보다 크므로 교차점을 찾을 수 있습니다.