본문 바로가기

유니티/유니티 기타

[유니티 기타] 벡터

728x90
반응형

벡터 연산은 설명하기 쉽지만 의의로 미묘하고 강력하여 게임 프로그래밍에서 많은 용도로 사용됩니다.

이어지는 페이지에서는 코드에서 벡터를 효과적으로 사용할 수 있는 방법을 몇 가지 제안합니다.

 

1. 벡터 연산 이해

벡터 연산은 3D 그래픽스, 물리 연산 및 애니메이션에 있어 핵심적이며 Unity를 최대한 활용하기 위해서는 벡터 연산을 깊이 이해하는 것이 유용합니다. 

여기서는 주 연산 및 유용하게 사용할 수 있는 몇몇 제안을 다룹니다.

1.1 덧셈

 

두 벡터를 더하면 그 결과는 오리지널 벡터를 단계대로 하나씩 차례대로 취한 것과 동일합니다. 

두 파라미터의 순서가 바뀌어도 결과가 같으므로 순서는 상관이 없습니다.

 


첫 번째 벡터가 공간 내 하나의 점일 경우, 두 번째 벡터는 그 포지션의 오프셋 또는 그 포지션에서 점프한 것으로 해석할 수 있습니다.

예를 들어, 땅 위 어떤 위치로부터 5유닛 위에 있는 위치의 지점을 찾으려면 다음 연산을 사용하면 됩니다.

 

 var pointInAir = pointOnGround + new Vector3(0, 5, 0);


벡터가 힘을 나타내는 단위일 때 벡터를 힘의 크기인 강도와 방향으로 간주하면 더욱 직관적으로 이해할 수 있습니다. 

두 가지 힘의 벡터를 더하면 두 힘을 합한 값과 동일한 새로운 벡터가 생성됩니다. 

이 개념은 동시에 다수의 서로 다른 컴포넌트에 힘을 가할 때 유용합니다. 

예를 들어, 로켓이 추진력에 의해 앞으로 가고 있지만 역풍으로 인해 영향을 받는 경우를 구현하기 유용합니다.

 


1.2 뺄셈


벡터의 뺄셈이 가장 많이 사용되는 경우는 한 오브젝트에서 다른 오브젝트까지의 거리와 방향을 구하고자 할 때입니다. 

주의할 점은 벡터의 뺄셈에서는 두 파라미터의 순서가 매우 중요하다는 점입니다.

 

 

// The vector d has the same magnitude as c but points in the opposite direction.
var c = b - a;
var d = a - b;


숫자의 경우 음의 벡터를 더하는 것은 양의 벡터를 빼는 것과 동일합니다.

 

// These both give the same result.
var c = a - b;
var c = a + -b;


음의 벡터는 오리지널 벡터와 크기가 같고 같은 선상에서 정반대 방향을 가리키는 벡터를 말합니다.

 


1.3 스칼라 곱셈과 나눗셈

 

벡터를 논할 때, 일반적으로 보통의 수(예: 플로트 값)를 스칼라라고 부릅니다. 

스칼라라는 말의 의미는 스케일 또는 크기만을 가진다는 의미이고 한편 벡터는 크기와 방향을 모두 가집니다.

벡터에 스칼라를 곱하면 그 결과는 오리지널과 동일한 방향을 가리키는 벡터가 됩니다. 

그러나 새 벡터의 크기는 오리지널 벡터의 크기에 해당 스칼라 값을 곱한 값과 동일합니다.

마찬가지로 스칼라 나눗셈은 오리지널 벡터의 크기를 스칼라로 나누는 것입니다.

이러한 연산은 벡터가 이동 오프셋 또는 힘을 나타낼 때 유용합니다. 

이 방식을 통해 방향에는 영향을 주지 않으면서 어떤 벡터의 크기를 변경할 수 있습니다.

어떤 벡터를 자신의 크기로 나누면 그 결과는 크기가 1인 벡터가 되며, 이 벡터를 노멀라이즈 벡터라 합니다. 

노멀라이즈 벡터에 스칼라를 곱하면 결과 벡터의 크기는 해당 스칼라 값과 같아집니다. 

힘의 방향은 일정하지만 강도를 제어할 수 있는 경우에 유용합니다

(예: 자동차 바퀴의 힘은 항상 앞 방향으로 차를 움직이지만 그 동력은 운전자가 제어합니다).

 

1.4 내적


내적 연산은 두 벡터를 받아 스칼라를 반환합니다. 

이 스칼라는 두 벡터의 크기를 곱하고 그 결과에 두 벡터 사이의 각에 대한 코사인을 곱한 것과 같습니다. 

두 벡터 모두 노멀라이즈 벡터일 경우 코사인은 근본적으로 첫 번째 벡터가 두 번째 벡터의 방향으로 얼마나 기울었는지를 나타냅니다(또는 그 반대도 성립합니다. 즉 파라미터의 순서는 상관이 없습니다).

 



계산기를 사용하면 각도를 찾아 그 각도에 해당하는 코사인을 찾기가 매우 쉽습니다. 

그러나 아래 다이어그램에 나타난 것처럼 몇몇 주요 코사인 값에 대해 직관적으로 이해하고 있으면 유용합니다.-

 


내적은 매우 간단한 연산으로 일부 상황에서는 Mathf.Cos 함수 또는 벡터 크기 연산을 대신하여 사용할 수 있습니다(정확히 동일한 일을 하지는 않지만 때로는 얻을 수 있는 효과가 동일합니다). 

그러나 내적 함수 연산은 CPU 시간이 훨씬 적게 걸리며 따라서 유용한 최적화 방법이 될 수 있습니다.

 

1.5 외적


다른 연산은 2D 및 3D 벡터용으로 정의되어 있으며 실제로 어떤 차원의 벡터에서도 가능합니다. 

반면 외적은 3D 벡터에서만 의미가 있습니다. 

외적 연산은 두 벡터를 입력으로 받아 결과물로 다른 벡터를 반환합니다.

결과 벡터는 두 입력 벡터에 수직입니다. 입력 벡터의 배치에 따라 나오는 결과 벡터의 방향을 기억하기 위해 “왼손 법칙”을 사용할 수 있습니다. 

첫 번째 파라미터가 왼손 엄지에 해당하고 두 번째 파라미터가 검지에 해당할 때, 결과 벡터는 중지 방향을 향합니다. 

파라미터의 순서가 바뀌면 결과 벡터는 같은 크기이면서 정반대 방향을 가리키게 됩니다.

 



결과 벡터의 크기는 입력 벡터들의 크기를 곱하고 그 결과에 두 벡터 사이각의 사인을 곱한 것과 같습니다. 사인 함수에서 자주 사용되는 값은 아래와 같습니다.

 



외적은 결과 값에 여러 유용한 정보를 합쳐놓은 것이므로 복잡해 보일 수도 있습니다. 

그러나 내적과 마찬가지로 외적은 수학적으로 매우 효율적이며, 느린 초월 함수를 써야 했을 상황에서 코드 최적화를 할 때 유용하게 사용할 수 있습니다.

 

2. 오브젝트 간 방향 및 거리


공백 내 한 포인트의 값을 다른 포인트의 값에서 빼면 그 결과는 한 오브젝트에서 다른 오브젝트를 가리키는 벡터 입니다.

 

// Gets a vector that points from the player's position to the target's.
var heading = target.position - player.position;


이 벡터는 타겟 오브젝트의 방향을 가리키고, 이 벡터의 크기는 두 포지션 사이의 거리와 같습니다. (예를 들어 발사체를 유도하기 위해)타겟의 방향과 타겟까지의 거리도 제시하는 정규화 된 벡터가 필요한 경우는 흔합니다. 

오브젝트 사이의 거리는 지향 벡터의 크기와 같고, 이 벡터를 그 크기로 나눠서 노멀라이즈할 수 있습니다.

 

var distance = heading.magnitude;
var direction = heading / distance; // This is now the normalized direction.


방법은 크기와 정규화 된 프로퍼티를 모두 별도로 사용하는 것보다 바람직합니다. 

둘 다 CPU를 많이 소모하기 때문입니다(모두 제곱근 연산을 수반함).

거리를 비교 용도로만 사용해야 하는 경우(예: 근접 검사) 크기 계산 자체를 피할 수 있습니다. 

sqrMagnitude 프로퍼티는 크기 값을 제곱을 제공하고 크기처럼 계산되지만 시간 소모적인 제곱근 연산이 불필요합니다. 

크기를 알려진 거리에 비교하지 않고 크기의 제곱을 거리의 제곱에 비교할 수 있습니다.

 

if (heading.sqrMagnitude < maxRange * maxRange) {
    // Target is within range.
}


그러면 실제 크기를 비교에 사용하는 것보다 훨씬 더 효율적입니다.

때로는 타겟을 향하는 지상 방향이 필요할 수 있습니다. 

예를 들어 땅 위에 서있는 플레이어가 공중에 떠있는 타겟에 접근해야 하는 경우를 상상해 보아야 합니다. 플레이어의 포지션을 타겟의 포지션에서 빼면 결과 벡터가 타겟 방향으로 위쪽을 향합니다. 

이 벡터는 플레이어의 트랜스폼 방향을 지정하는 데 적합하지 않습니다. 

이 역시 위쪽을 향하기 때문입니다. 

실제로 필요한 것은 플레이어의 포지션에서 타겟 바로 밑에 있는 지상 포지션까지의 벡터입니다. 

이 벡터는 뺄셈 결과를 사용하고 Y 좌표를 0으로 설정하여 쉽게 얻을 수 있습니다.

 

var heading = target.position - player.position;
heading.y = 0;  // This is the overground heading.

 

3. 노멀/수직 벡터 컴퓨팅


노멀 벡터(즉, 평면에 수직인 벡터)는 메시 생성 중에 자주 필요하며 경로 추적 및 다른 상황에서도 유용합니다. 

평면의 세 점(예: 메시 삼각형의 코너 점)이 주어질 경우, 노멀을 쉽게 찾을 수 있습니다. 

세 점 중 아무 점이나 하나 선택하고, 이 점을 다른 두 점에서 각각 빼면 두 개의 백터를 구할 수 있습니다.

 

 

 

var a: Vector3;
var b: Vector3;
var c: Vector3;

var side1: Vector3 = b - a;
var side2: Vector3 = c - a;


이 두 벡터의 외적은 표면에 수직인 세 번째 벡터를 제공합니다. 

왼손 규칙은 두 벡터를 외적 함수에 전달해야 하는 순서를 결정하는 데 사용할 수 있습니다.

표면의 윗면을 내려다 볼 때(즉 노멀이 가리키며 뻗어나가는 쪽에서 평면 방향을 보면) 첫 번째 벡터는 시계 방향으로 두 번째 벡터로 돌아야 합니다.

 

var perp: Vector3 = Vector3.Cross(side1, side2);


입력 벡터의 순서가 바뀌면 결과는 정확히 반대 방향을 가리킵니다.

메시의 경우 노멀 벡터도 정규화되어야 합니다. 정규화된 프로퍼티를 사용하여 이 작업을 수행할 수 있지만 때때로 유용한 또 다른 트릭이 있습니다. 

직각 벡터를 자체 크기로 나눠 정규화할 수도 있습니다.

 

var perpLength = perp.magnitude;
perp /= perpLength;


결과적으로 삼각형의 영역은 perpLength / 2와 동일합니다. 

메시의 전체 표면 영역을 알고 싶을 때, 또는 상대 영역을 기반으로 삼각형을 임의의 확률로 선택하려는 경우에 유용합니다.

 

4. 다른 벡터 방향 위에 놓인 벡터의 크기 측정


자동차 속도계는 일반적으로 바퀴의 회전 속도를 측정하여 표시합니다. 

간혹 자동차는 전진하지 않을 수도 있는데(옆으로 미끌어질 수도 있으므로), 이 경우 해당 운동 방향은 속도계가 측정할 수 없습니다. 오브젝트의 rigidbody.velocity 벡터의 크기는 전체 운동의 해당 방향에 대한 속도를 알려주므로 전진 속도만을 알고 싶은 경우 벡터 내적을 사용해야 합니다.

 

var fwdSpeed = Vector3.Dot(rigidbody.velocity, transform.forward);


물론 방향은 어떤 방향이든 무관하지만, 이 연산을 하려면 방향 벡터를 정규화해야 합니다.

결과가 속도 크기를 더 정확하게 측정할 수 있을 뿐만 아니라, 크기를 계산하는 동안 느린 제곱근 연산을 피할 수 있도록 하기 때문입니다.

728x90
반응형