https://www.udemy.com/share/105j7o/
위 강의의 첫 번째 챕터 내용을 정리해 보았다.
강의에서는 C++로 직접 Vector 클래스부터 구현하지만, 눈에 보이는 게 없어서 재미없을 것 같아 나는 언리얼 엔진으로 구현해 보면서 관련 공식들을 이해해 보았다.
강의 내용만으로 이해되지 않는 내용들은 유투브에서 보충자료를 검색해 보았다. 아래는 도움받은 채널 중 일부이다.
https://www.youtube.com/@TheOrganicChemistryTutor
https://youtu.be/sWN2lI7ztmE?si=hOHXgolgL6YsdZYx
https://www.youtube.com/@codingmath
https://www.youtube.com/@khanacademy
Basic Types : Point, Line, Plane
각 클래스의 기본 속성은 cpp에 작성하되, 실제 화면에 보이는 Shape 나 Text Render 등의 컴포넌트는 이것을 상속받은 BP에서 추가해 주었다. 컴포넌트들의 업데이트도 BP에서 처리한다.
Point
점은 월드 상의 위치 1개만 속성으로 가진다.
Line
모든 Line은 y=mx * c 의 형태로 표현할 수 있다.
또는, 선분 AB는 ,(1-t)a + bt 로 표현할 수 있다.
t가 0 < t < 1 범위 안에 있다면 AB는 Line Segment이다.
만약 t > 1이라면 A 로부터 B 방향으로 쏘아지는 Ray이고 , t < 1이면 반직선이다.
2d 평면 상의 선 L(t) = vt + a는 아래와 같이 나타낼 수도 있다.
(t는 scalar, V 와 a는 점이라고 가정)
각 컴포넌트별로는
으로 표현할 수 있다.
Line Class에서 Line Mode enum을 정의해 Infinite Line, Ray, Segment Type 중 선택 가능하도록 하였다.
t는 Scalar로, 각 Line의 식에 대입되어 선분 위의 한 점의 위치를 나타낼 수 있도록 하였다.
- Infinite Line은 방향만을 갖는다.
- Ray인 경우 StartPoint, Direction 을 갖는다. Direction은 선분이 향하는 방향의 Unit vector이다.
- Segment는 StartPoint, EndPoint를 가지고, Direction은 b-a 이다. 이때는 0 < t < 1 이다.
Line을 나타낼 도형으로 Cylinder를 길게 늘여 사용하였다.
Line의 Direction이 곧 Cylinder 의 길이 쪽이 향하는 방향이 된다.
Plane
Plane은 Plane Normal 과 Plane 상의 한 점으로 표현할 수 있다.
점 P와 점 Q 가 모두 한 개의 Plane 위에 있을 때, 선 PQ는 Plane Normal 벡터와 수직이다. 즉, P-Q dot n = 0 이다.
https://www.youtube.com/watch?v=gw-4wltP5tY
- 어떤 Plane 위의 모든 점과, 이 점들을 잇는 벡터는 Plane Normal n에 대해 수직이다.→ 이것을 이용해서 아래 Plane equation을 유도할 수 있다.
= Plane 상의 어느 벡터와 노멀 n 의 dot product는 0이다.
Ax + By + Cz = D
위와 같은 평면의 방정식이 주어졌다고 하자.
평면 상의 모든 점 x, y, z는 위 방정식을 만족한다,
이 평면의 Normal Vector는 아래와 같다.
Ai + Bj + Ck
(여기에서 i, j, k는 기저 벡터를 의미)
D는 평면의 노멀 벡터, 즉 기울기에 영향을 주지 않는다 - 원점으로부터 얼마나 shift 되어 있는지만 결정할 뿐이다. D의 값이 무엇이 됐든, 같은 노멀을 가진 평면이 무한히 존재할 수 있다.
평면의 노멀 벡터와 평면 상의 한 점을 이용해 평면의 방정식 구하기
노멀 벡터 N = [1, 3, -2]
평면 위의 점 X = [1, 2, 3]
이라고 할 때, 위 두 개의 백터로 평면의 방정식을 어떻게 구할까?
- 평면 상의 또다른 점[x, y, z] 를 상정한다.
이 점으로부터 점 X 를 향하는 벡터를 구한다.
[1, 2, 3] - [x, y, z] = [1-x, 2-y, 3-z] - 이렇게 구한 벡터와 노멀 벡터의 내적은 0이다. 이 식을 활용한다.
[1-x, 2-y, 3-z] dot [1, 3, -2] = 0
= 1-x + 6 - 3y - 6 + 2z = 0
= 1-x - 3y + 2z = 0
= -x -3y +2z = -1
→ x + 3y - 2z = 1 가 이 평면의 방정식이 된다. 노멀 벡터와 각 계수를 비교해 보면 알 수 있다.
Plane 클래스는 Plane Normal, Plane Shift의 속성을 갖는다. Plane Normal은 Unit Vector로 유지되도록 할 것이다.
Geometry
Cross Product로 Line - Line Intersection 여부 판단하기
두 선분 (segment) 가 교차하는 지 여부를 어떻게 알 수 있을까?
두 점 A, B로 이루어진 선분 AB는 B-A로 표현할 수 있다. 아래 그림을 보자.
점 B에서 점 A의 좌표를 뺀 것이 선분 AB를 나타내는 벡터가 된다.
(AB = B - A)
이렇게 나타낸 벡터는 선분 AB의 Directional Vector이기도 하다. (A 가 선분의 시작점이 되었음에 주의하자)
이제 선분을 벡터로 나타냈으니, 주어진 점의 좌표에 대해서 벡터 계산을 수행할 수 있다.
선분 AB, CD 가 교차한다면,
점 A와 B는 선CD를 기준으로 서로 반대쪽에 있어야 한다.
점 C와 D는 선AB를 기준으로 서로 반대쪽에 있어야 한다.
위 두 조건을 모두 만족해야 두 선이 교차한다고 볼 수 있다. 각 점이 선분의 어느 쪽에 위치하는지는 위에서 본 벡터 간 외적 결과의 부호를 활용한다.
예를 들어, 선분 AB와 점 C 사이의 상대적인 위치를 알고 싶다면, 아래 두 벡터를 비교하면 된다.
A → B와 A → P 벡터를 구한 뒤, 이 두 벡터의 외적 결과의 부호를 본다.
외적의 결과가 양수라면 반시계방향, 음수면 시계방향에 위치한다.
- XOR - 두 boolean 값이 서로 다를 때만 1을 반환한다. 같으면 0을 반환한다.
각 선분에 대한 점의 상대적인 위치를 XOR로 비교한다 - 점의 방향을 비교했을 때 모두 1이 나와야 한다.
구현
편의를 위해 YZ 평면에서만 비교해 보았음
Cross Product 의 부호를 비교해서 각각 A와 B가 CD 의 반대편에, C와 D가 AB의 반대편에 있으면 두 선분이 서로 교차한다고 판단
Intersection Point 위치 알기
점 a 와 b로 이루어진 선분 AB 는 (b-a) 로 표현할 수 있다.교차점 P는 선분 CD 위에 있다.
만약 이 선분이 다른 선분 CD 와 교차한다고 했을 때, 교차점을 구해 본다.
선분 CD에 수직인 노멀 벡터 n이 있다.
(2D 평면상의 Normal Vector : (x, y) → (y, -x)
CD = d - c 이고, 이 벡터의 (y, -x) 를 구한 것이 수직인 벡터이다.
선분 CD 위의 어느 점 혹은 벡터와, 이 노멀 벡터와의 내적은 0이 될 것이다.
즉,
이다. P(t) - c 는 c → 교차점으로 가는 Direction Vector이다. 이 방향 벡터 또한 선분 CD 위에 있다.
교차점 P는 P(t) = (b-a)t + a 로 표현 가능하다.
P(t) 라는 선분을 parametric equation으로 표현했을 때 아래와 같다.
P(t) = vt + a
v는 a로부터 시작되는 선분의 Directional Vector이다.
t는 parameter이다. 이 값을 알면 점P의 좌표도 알 수 있다.
이것을 (b-a)t + a로 확장할 수 있다.
t에 대해 정리한 위 식을 풀어 t를 구한다.
여기에서 분모 n(b-a) (그림에서의 2번째 각도)에 관해 좀 더 살펴보자.
만약 이 값이 0이 된다면, n과 선문 AB의 Directional Vector의 dot product가 0이라는 의미이므로, n은 선분 AB의 노멀 벡터도 된다는 의미가 된다. 이 경우는 선분 AB 와 CD가 평행하거나, 직선상에 나란히 있을 때만 일어난다.
→ t를 구하면, vt+a (=(b-a)t + a) 도 구할 수 있다. 이것이 교차점의 좌표가 된다.
구현
1. 선분 CD 의 Normal Vector 를 구한다.
n = FVector2d((d-c).y, (d-c).-x)
이것을 Direction Vector로 한 선분을 Spawn한다.
2. n에 대해 다음 식을 만족하는 t 값을 구한다.
3. t를 선분 AB에 대입해서 선분 AB 상의 점의 좌표를 구한다. 이것이 교차점의 위치가 된다.
Update T에서 t에 따른 선 위의 좌표를 구하는 부분은 아래와 같다.
두 선 사이의 각도 계산하기
2D 공간 안의 두 선은 어느 지점에선가 교차하거나, 평행이다.
평행일 경우, 두 선 사이의 각도는 0이다.
두 선의 Directional Vector가 주어지면, 여기에서부터 각도를 계산할 수 있다.
3D에서는 어떨까?
2D 에서와 계산은 같다. 다만, 선이 교차하거나, 평행하거나, 또는 꼬일(skewed) 수 있다. - 평행하지도 않고, 교차하지도 않는다.
Plane 과 Line 간의 각도 계산하기
두 Line 사이의 각도를 계산할 수 있다면, 이것을 이용해 Plane 과 Line 간의 각도도 계산할 수 있다.
- Line의 Directional Vector 와 Plane Normal Vector 사이의 각도를 계산할 수 있다.
이 각도를 a라고 할 때, 90 - a 가 우리가 원하는 Plane 과 Line 간의 각도가 된다. - 두 Line 사이의 각을 계산할 때, 아래 식을 다시 사용한다.
두 Directional Vector가 Normalize 되어 있다면, 분모 |u||v| 는 1이 된다.
따라서 식은 아래와 같은 모양이 된다.
이 theta (in degree)에서 -90 한 것이 Line 과 Plane 사이의 각도가 된다.
Colinear, Coplanar
Colinear
두 벡터가 서로 평행하거나, 점 두 개가 1개의 라인상에 있음
직선 두 개가 서로 평행할 경우, Colinear함
한 벡터를 다른 벡터의 곱인 u = kv 형태로 표현 가능.
u = [x1, y1, z1], v - [x2, y2, z2] 일 때,
x1/x2 = y1/y2 = z1/z2
를 만족해야 함.
이 식을 정리하면, x1y2 - y2x1 = 0 같은 식이 3개 생성됨
→ 0인지 아닌지 검사하면, Colinear 인지 아닌지 판별할 수 있음
Coplanar
벡터들이 한 평면 위에서 평행하거나, 같은 평면에 놓여져 있음
모둔 벡터 두 개는 한 개의 평면을 공유함.
벡터가 3개일 때, 이 벡터들이 이루는 tetrahedron의 부피는 0이 됨
A벡터와 B백터가 공유하는 Plane 위에 C 벡터가 위치한다면, 이 세 벡터는 coplanar 임
A, B, C, D 점이 이루는 tetrahedron의 부피는 아래와 같이 구한다.
= Scalar Triple Product.
결과의 부호는 벡터들의 순서에 따라 결정된다.
점과 선 사이의 거리
점과 점 사이의 거리는
로 구할 수 있다.
만약 어떤 선과 점 사이의 거리를 알고 싶다면, 선에서 점과 가장 가까운 위치의 점을 구한 뒤, 이 점과 원래 점 사이의 거리를 구하면 된다.
- 점과 선 사이의 거리는, 선분 상에서 점에 가장 가까운 거리의 좌표와 점 사이의 거리이다.
- 이 좌표와 점 사이를 잇는 벡터는, 선과 직각을 이루므로 내적의 결과가 0이다.
- 선 위에서 점으로 향하는, 직각의 선을 찾는다.
이 선분과 원래의 선분의 Intersection Point를 찾는다.
선분 위의 Intersection Point가 X(t)라고 했을 때,
이다. (v는 제시된 선의 의 Normalize 된 Directional Vector 이다)
점을 Y라고 했을 때, Y - X(t) (=Y - (vt - a))가 선분과 직각을 이루는 선이다. 이 선과 원래의 선을 내적했을 때 0이 되어야 한다. 이것을 아래와 같은 식으로 나타낼 수 있다.
여기에서 a는 Intersection Point 의 좌표이다.
같은 벡터끼리 내적했을 때, 그 결과는 magnitude의 제곱이 됨을 이용하면, 아래와 같이 식을 세울 수 있다.
(v dot v 와 t1를 오른쪽 항으로 옮긴 것이다) 여기에서 t1은 Intersection Point의 t값을 의미한다.
이 식을 정리해서 t1값을 유도할 수 있다.
여기에서 v는 제시된 선분의 Directional Vector이고, a는 그 선 위의 Known Point 이다.
(Line Type이 Ray 또는 Segment 인 경우 a = Line StartPoint로 정의하였다)
이렇게 구한 t1을 제시된 선분의 Parametric equation 에 대입하면, Intersection Point를 알 수 있다.
Y - X(t) 벡터의 길이를 구하면, 그것 또한 점과 선 간의 거리가 된다.
Ray의 경우,
t가 음수라면 Ray 의 Start Point 보다 뒤에 있는 것이기 때문에, 가장 가까운 점은 Ray의 Start Point가 된다. 거리는 Start Point로부터 점 까지가 된다.
Segment 의 경우,
Parametric Equation에서 t 값은 0부터 1 까지로 제한된다.
만약 교차점 $t_1$이 0과 1 사이의 범위 안에 있지 않다면, 선분 상에서 더 가까운 endpoint 를 찾고 그 거리를 취한다.
구현
점과 평면 사이의 거리 구하기
https://www.youtube.com/watch?v=zWMTTRJ0l4w
Point Q와 Plane 사이의 거리 D를 구한다.
Plane = ax0 + by0 + cz0 + d = 0
Q = (x1, y1, x1)
먼저 Plane 상의 Known Point P를 생각한다.
P = (x0, y0, z0)
Plane 상의 가장 가까운 점인 X와 점 Q를 연결한 벡터는 Plane과 수직이다 - 즉, Plane의 normal vector N과 평행이다.
N = (a, b, c)
- 점 P와 Q를 연결하는 벡터에서 N과 평행한 성분이 D이다.
즉 PQ를 N에 대해 Scalar Projection 한 것이 D가 된다. - 벡터 PQ 와 PX 사이의 각도는 아래와 같다.
여기에서 X는 Plane 상의 Q와 가장 가까운 점이다.
또한, 삼각함수를 사용하면 아래와 같은 식을 도출할 수 있다.
QX = distance from Point Q and the Plane
벡터 a, b 와 그 사이의 각을 theta 라고 할 때, 아래 식이 성립된다
a = N, b = QP 라고 할 때, |b|cosθ 부분을 알고 있기 때문에, n dot QP 를 |n| 으로 나눈 것이 곧 QX임을 알 수 있다.
N dot QP의 결과는 양수여야 한다 - 점과 평면 사이의 거리이기 때문에..
여기에서 N = Plane의 Normal = (a, b, c) 이다.
위 식의 분자 부분을 풀면, 아래와 같다.
| (a(x1 -x0) + b(y1 - y0) + c(z1 - z0) |
= | (ax1 + by1 + cz1) - (ax0 + by0 + cz0) |
그런데 여기에서 (ax0 + by0 + cz0) 는 Plane equation 에서 -d와 같다. ( ax0 + by0 + cz0 + d = 0 )
따라서 위 식은 아래와 같이 쓸 수 있다.
이것이 Point Q로부터 Plane 까지의 거리 D를 구하는 공식이 된다.
구현
위 식을 그대로 옮긴 부분이다.
추가
점에서 가장 가까운 평면 상의 위치를 구하려면,
점의 위치를 평면의 Normal Vector에 투영해서 Normal 과 평행한 성분만을 얻어낸다.
이 위치에 Plane이 Shift 된 만큼 Plane Normal 방향으로 더해주어야 한다.
이렇게 얻어낸 Vector를 현재 위치에서 빼 준다. 이것이 Vproj(n) (= Projected Position)이다.
(GIF 상에서 평면 위에 연두색으로 표시되는 지점이다)
이렇게 하면, 평면 위에 투영된 점의 위치를 구할 수 있다. 이 위치로부터 점의 위치까지의 거리가 평면과 점 사이의 거리이다.
선과 평면의 교차점 구하기 (Line - Plane Intersection)
Line 의 Parametric Equation은 아래와 같다.
Q(t) = vt + a
이것을 아래와 같은 형태로 다시 써 본다.
Q(t) = P + dt
여기에서 P는 Line의 시작점, d는 Directional Vector 이다.
t는 상수로, 여기에 값을 대입하면 이 Line 위 한 점의 위치를 알 수 있다.
Plane의 식은 아래와 같다.
Ax + By + Cz + D = 0
Plane Normal n = (A, B, C) 이고, D는 Plane 상수이다.
위에서 본 Line Equation의 식을 x, y, z 성분으로 각각 풀어 쓰면 아래와 같다.
intersection Point 는 Line 위에 있는 동시에 Plane 위에 있어야 하므로, 위의 선분 Q 위의 점 Q(t1)를 Plane 의 식에 대입할 수 있다.
이 식에서 t1을 single out 해 본다.
분모 부분은 (A, B, C) dot ( d(x), d(y), d(z) ) 임을 알 수 있다. (dot = 두 벡터의 성분끼리 곱한 후 모두 더한 것)
즉 분모는 n dot d 이다.
분자 부분은 (A, B, C) dot ( P(x), P(y), P(z) ) 임을 알 수 있다.
즉 분자는 (n dot P) + D 이다.
따라서 아래와 같은 식을 만족하는 t1을 구해서 Line Equation에 대입하면, Line 과 Plane이 만나는 한 점을 알 수 있다.
n(=Plane Normal), P(=Line Origin), D(Plane Constant), d(Line Directional Vector)는 모두 우리가 알고 있는 수들이기 때문에, 이제 t1을 구할 수 있다.
만약 Line 과 Plane이 평행하다면, 교차하지 않기 때문에 교차점이 없다.
만약 Line 이 Plane 위에 있다면, 교차점은 무한히 많을 것이다.
구현
평면과 평면 사이의 교차 선 구하기
두 Plane 이 교차할 때 생기는 선분은, 각 Plane 의 Normal에 모두 수직이다.
이것을 이용하면, 선분의 Directional Vector는 아래와 같은 식을 만족한다.
두 벡터를 외적해서, 두 벡터에 모두 수직인 벡터를 얻는다.
이렇게 만들어진 Line 위의 모든 점은 두 Plane Equation을 모두 만족하게 된다. 이 Line 상의 한 점 R을 아래와 같이 표현할 수 있다.
여기에서 a와 b는 상수이다. R은 두 Plane 상에 모두 존재하므로, 두 Plane Equation에 모두 적용할 수 있다.
또한, Plane Equation은 아래와 같은 형태로도 표현할 수 있지만.
아래와 같은 형태로도 나타낼 수 있다.
여기에서 P는 Plane 위의 보통의 점이고, d는 변수이다.
위에서 본 점 R은 두 Plane 모두의 식을 만족하므로, 아래와 같이 나타낼 수 있다.
이 식을 전개하면 아래와 같다. 같은 벡터끼리의 내적은 magnitude의 제곱이다.
1과 2를 혼합해서 a 항을 없애면, a와 b는 최종적으로 아래와 같은 형태로 구할 수 있다.
이렇게 구한 a, b를 위에서 본 점 R 공식에 대입하면, 선분 위의 점 R을 알 수 있다.
이제 Line의 Directional Vector 와 Point를 알았기 때문에, 교차 선분의 전체 공식을 구할 수 있다.
만약 Normal Vector 가 normalized 되었다면, 길이가 1이므로 해당 항은 모두 1로 치환된다.
구현
점 R이 Intersection Line 의 Start Point (a)가 된다.
Intersection Line의 Direction Vector D는 두 평면의 Normal Vector의 외적이다.
이렇게 구한 StartPoint, Direction으로 Line 역할을 할 Cylinder의 이동, 회전 값을 넣어준다.
Intersection Line이 붉은 선으로 표시된 모습.
'Math' 카테고리의 다른 글
회전행렬 Rotation Matrix (0) | 2024.08.16 |
---|---|
Spline Mesh Deformation (1) | 2023.12.05 |
Picking & Test Intersection (0) | 2023.09.08 |