일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- linux
- 보로노이다이어그램
- Unity
- Graham Scan
- GJK
- 내적
- 유니티
- C++
- 다이나믹 프로그래밍
- PS
- uclidean algorithm
- 충돌 알고리즘
- Doubly Connected Edge List
- c#
- dp
- 분할축 이론
- C
- 우분투
- 벡터
- 문제풀이
- 알고리즘
- 백준
- ubuntu
- 외적
- SOH
- 리눅스
- Vector
- Expanding Polytope Algorithm
- AABB
- 수학
- Today
- Total
마이 플밍 블로그
벡터 기초와 내적 본문
벡터의 스칼라 구하기 (정규화 normalize)
벡터의 방향은 유지하면서 길이만 1로 만들기
$$ \dot{v} = (\frac{v_{x}}{\left \| v \right \|},\frac{v_{y}}{\left \| v \right \|},\frac{v_{z}}{\left \| v \right \|}) $$
v를 정규화 하려면 각각의 성분을 길이로 나누어 주기만 하면 됨
길이를 구하는 공식은 피타고라스 정리를 이용하면 되는데 다음과같다
$$v = (1,3,4)$$
$$\left \| v \right \| = \sqrt{1^{2} + 3^{2}+ 4^{2}} = \sqrt{26}$$
백터 내적
두 벡터를 가지고 한 스칼라 값을 구하는 연산
다음과 같이 정의된다
$$ u \cdot w = \left \| v \right \| \cdot \left \| w \right \| \cdot cos\theta $$
세타는 두 벡터사이의 각이다
만약 두 벡터가 길이가 1인 단위 벡터라면 내적의 값은 단순히 두 벡터 사이각의 cos 값이다
||V||는 벡터의 길이로 나타낸다
$$ v \cdot w = v_{x} \cdot w_{x} + v_{y} \cdot w_{y} + v_{z} \cdot w_{z} $$
또다른 식으로는 각 성분을 곱해서 더하면 된다
$$\left \| v \right \| \cdot \left \| w \right \|\cdot cos\theta = v_{x} \cdot w_{x} + v_{y} \cdot w_{y} + v_{z} \cdot w_{z}$$
$$cos \theta=(v_{x} \cdot w_{x} + v_{y} \cdot w_{y} + v_{z} \cdot w_{z})/(\left \| v \right \| \cdot \left \| w \right \|)$$
$$\theta = arccos((v_{x} \cdot w_{x} + v_{y} \cdot w_{y} + v_{z} \cdot w_{z})/(\left \| v \right \| \cdot \left \| w \right \|))$$
이처럼 사이각의 cos값이나 cos의 역함수 arccos를 사용하여 사이각을 구할 수 있다.
실제론 두 벡터 사이의 사이각보다 cos값을 아는 것으로 충분한 경우가 많다.
$$ cos \theta = v_{x} \cdot w_{x} + v_{y} \cdot w_{y} + v_{z} \cdot w_{z} $$
$$\theta = arccos(v_{x} \cdot w_{x} + v_{y} \cdot w_{y} + v_{z} \cdot w_{z})$$
두 벡터가 모두 단위 벡터일 경우 위와같이 간단해진다
$$v\cdot v = v_{x}^{2} + v_{y}^{2}+v_{z}^{2} = \left \| v \right \|^{2}$$
어떤 벡터를 자기 자신과 내적하게 된다면
그 벡터의 길이의 제곱과 같다.
실제 프로그래밍에서는 벡터의 길이보다 길이의 제곱을 사용할 경우가 더 많다
그 이유는 제곱근을 구하는 것이 느리기 때문에 꼭 필요한 경우가 아니면 제곱값을 그냥 사용한다.
따라서 코드에서 벡터의 길이의 제곱값이 필요할 때 그 자신과 내적시키는 것을 종종 볼 수 있다.
내적 계산 사용하는 곳
내적은 두 벡터의 사이각의 cos 값에 영향을 받는다
내적을 통해서 두 벡터의 방향 관계에 대해 알 수 있다.
만약 두 벡터가 모두 영백터가 아닐 때 내적이 0이면 두 벡터의 사이각은 반드시 90도이다
또한, 영 벡터(모든 단위가 0인 벡터)가 아닌 두 벡터의 길이는 항상 양이므로 내적이 0보다 크면 사이각이 90도보다 작고, 반대로 0보다 작으면 90도 보다 크다는 것을 알 수 있다.
적이 주인공 앞에 있는지 뒤에 있는지 알고 싶다면 주인공의 방향 벡터와 주인공의 위치에서 적을 향하는 벡터를 내적하여 결과가 양이면 적이 주인공 앞에 있는 것이고 반대로 음이면 주인공 뒤에 있는 것이다
(일정 시야각에 있는지는 밑에)
만약 두 벡터가 모두 단위 벡터라면 조금더 재미있는 정보를 얻을 수 있다.
두 단위 벡터가 서로 평행인지 아닌지를 판별할 수 있다.
두 단위 벡터가 평행이라면 사이각이 0도 이거나 180도 이어야 한다 즉 cos 값이 1이거나 -1이면 두 단위 벡터는 평행이다. 단위 벡터들의 경우, 내적이 바로 두 단위 벡터들 사이각의 cos 값이다 따라서 내적의 절대값이 1이라면 두 단위 벡터가 평행임을 알 수 있다. 즉
$$\left | \dot{a}\cdot \dot{w} \right | = 1$$
이면 v와 w는 평행이다.
하지만 이때 두 벡터 모두가 단위 벡터가 아닐 경우 정규화를 해야 하는 부담이 있다.
내적의 경우 5번의 연산(3번의 곱셈과 2번의 덧셈)으로 충분 하지만 정규화를 하려면 여기에 각 벡터당 9번의 연산(3번의 곱셈, 2번의 덕셈, 1번의 제곱근 그리고 3번의 나눗셈)을 더 필요로 한다. 따라서 두 벡터가 단위 벡터가 아닌 경우에는 내적을 이용하기보다는 외적을 이용하여 두 벡터의 평행성을 따지는 것이 더 효율적이다.
내적은 또한 어떤 벡터를 중심으로 정해진 각 범위 내에 물체가 존재하는지 판별 하는 데도 유용하다
앞에서 주인공의 앞과 뒤를 따졌던 예제를 조금 더 확장해보자.
이번에는 적에게 시야각이 있다고 하고 주인공이 적의 눈에 띄었는지를 알고 싶다고 해보자. 주인공은A 적을 B,
B의 방향 벡터를 v, B에서 A로 향하는 벡터를 w 끝으로 적의 사야각을 a라고 하면 다음과 같이 그림으로 나타낼 수 있다.
A로 향하는 백터 w와 적이 바라보는 방향 v의 사이각이 G라고 하면 이것이 a/2보다 작을 때 B의 사야각에 A가 들어옴을 알 수 있다. 이는 cos(G)의 값이 cos(a/2) 값보다 큰경우와 같다.
코드로하면 아래와 같다
float angle = transform.eulerAngles.z;
angle *= Mathf.Deg2Rad;
// 적이 바라보는 방향 v
Vector2 dir = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)).normalized;
// A로의 방향 w의 단위벡터
Vector2 targetDir = (target.transform.position - transform.position).normalized;
// v와 w의 사이각
float inner = dir.x * targetDir.x + dir.y * targetDir.y;
float cosA = Mathf.Cos(angleRange * 0.5f * Mathf.Deg2Rad);
// cos(사이각)이 cos(a/2)보다 크다면 시야각에 있음
bool innerAngle = inner > cosA ? true : false;
마지막으로 반드시 짚고 넘어가야할 내적의 활용 예가 하나 더 있다.
바로 어떤 벡터를 다른 벡터에 두영한 결과 벡터를 계산할 때이다. 예를 들어
벡터 v를 벡터 w에 투영한 결과 벡터를 vp라고 하자 이때 vp를 구하러면 어떻게 해야할까?
일단 그 크기는 다음과 같이 구할 수 있다.
$$ v\cdot w=\left \| v \right \|\cdot \left \| w \right \|\cdot cos\theta$$
$$ =\left \| v \right \|\cdot \left \| w \right \|\cdot \frac{\left \| v_{proj} \right \|}{\left \| v \right \|}$$
$$= \left \| w \right \|\cdot \left \| v_{proj} \right \|$$
$$\left \| v_{proj} \right \| = \frac{v \cdot w }{\left \| w \right \|}$$
이제 방향이 필요하다 이는 vproj가 w와 방향이 같으므로 w를 정규화 해서 단위 벡터를 얻은 후, 그것과 위에서 구한 크기를 곱해서 vproj를 구하는 공식을 찾을 수 있다.
$$v_{proj} = \left \| v_{proj} \right \| \cdot \dot{w} = \frac{v\cdot w}{\left \| w \right \|}\cdot \frac{w}{\left \| w \right \|} = \frac{v\cdot w}{\left \| w \right \|^{2}}\cdot w$$
위 수식을 통해서 결국에는 벡터 w에 적당한 스칼라 v*w / ||w||^2값을 곱해서 평행하지만 길이가 다른 백터vproj를 구하고 있음을 알 수 있다. 이때 만약 w가 애초에 단위 벡터였다면 다음과 같이 간단해진다.
$$v_{proj} = (v\cdot \dot{w})\cdot \dot{w}$$
이 투영 벡터 공식은 유용하니 숙지해야한다. 예를 들어, 어떤 벡터를 새로운 좌표계, 즉 새로운 표준 기저 벡터들로 표현한다고 해보자. 이때 벡터를 새로운 각 기저 벡터에 투영해야한다. 기저 벡터들은 모두 단위 벡터이므로 바로 위의 수식을 그대로 사용할 수 있다. 또한, 충돌 검사에서도 한 벡터를 다른 벡터에 투영하는 경우가 자주 나온다.