Unity3D

벡터란?

일등하이 2018. 9. 6. 19:34
반응형

벡터란?

이 과정에서는 우리 주변의 세계를 보고 이를 코드로 영리하게 묘사하는 방법을 배웁니다. 우선 사과가 나무에서 떨어지고, 추가 공중에서 흔들리고, 지구가 태양을 중심으로 움직이는 현상 같은 기본적인 물리법칙부터 시작해 봅시다. 여기서 다루는 모든 내용에는 벡터(vector) 개념이 들어가 있습니다. 벡터는 사물의 움직임을 프로그래밍하기 위한 가장 기본적인 구성요소입니다. 지금부터 이야기를 시작해볼까요?
벡터라는 단어에는 많은 의미가 있습니다. 벡터는 1980년대 초반 캘리포니아의 새크라멘토에서 결성된 뉴웨이브 록 밴드의 이름입니다. 벡터는 캐나다 켈로그사에서 생산되는 시리얼의 이름이기도 합니다. 전염병학에서 벡터는 한 숙주로부터 다른 숙주로 감염시키는 유기체를 일컫는 말이기도 합니다. C++ 프로그래밍 언어에서 벡터(std::vector)는 유동적으로 크기 조절이 가능한 배열 자료 구조를 구현한 것입니다. 벡터의 다양한 정의는 모두 흥미롭지만 우리가 찾는 것은 아니에요. 우리가 원하는 것은 유클리디안 벡터(Euclidean vector)(그리스 수학자 유클리드의 이름을 따랐으며 기하학적 벡터(geometric vector) 로도 알려져 있음) 입니다. 이 과정에서 “벡터”라는 용어를 보면, 크기와 방향을 모두 가지는 어떤 양 으로 정의되는 유클리디안 벡터라고 생각하면 됩니다.
일반적으로 벡터는 화살표로 표현합니다. 화살표가 가리키는 쪽은 방향을 나타내며 화살표의 길이는 크기를 나타냅니다.
크기와 방향을 갖는 벡터의 그림
그림 1.1: (화살표로 그려지는)벡터에는 크기(화살표의 길이)와 방향(화살표가 가리키는 방향)이 있습니다.
위 그림에서 벡터는 점 A와 점 B를 잇는 화살표로 나타내며 A에서 B로 이동하는 한 가지 방법을 나타내고 있습니다.

왜 벡터를 사용할까요?

벡터에 관해 좀 더 자세히 살펴보기 전에 기본적인 프로그램을 살펴보고 왜 벡터에 관심을 가져야 하는지 알아봅시다. 칸아카데미에서 JS 입문자 과정을 마쳤다면 아마도 간단한 탱탱볼 프로그램 만드는 법을 배웠을 것입니다.
위 예제는 매우 단순한 세계를 묘사해 놓았습니다. 이 세계에서는 공백인 배경 위로 원(“공”)이 돌아다니는 것이 전부입니다. 이 공은 코드에서 변수로 표현되는 몇 가지 속성을 갖습니다.
위치속도
x와 yxSpeed와 ySpeed
복잡한 프로그램에는 변수가 더 많이 있습니다.
가속도타겟 위치바람마찰력
xacceleration과 yaccelerationxtarget과 ytargetxwind와 ywindxfriction과 yfriction
실제 세계의 개념 하나마다 두 개의 변수가 필요하다는 것을 알 수 있습니다. 이것은 2차원 세계의 경우인데, 3차원 세계의 경우에는 xyzxSpeedySpeedzSpeed등이 필요할 것입니다.
코드를 단순화하고 좀 더 적은 수의 변수를 사용할 수 있다면 좋지 않을까요?
원래 변수는 아래와 같습니다.
var x = 5;
var y = 10;
var xSpeed;
var ySpeed;
위 변수를 두 개로 줄여서 각 변수가 2차원의 정보를 갖는, 벡터와 유사한 객체로 만들어 봅시다.
var position;
var speed;
벡터를 사용하는 위의 첫 번째 단계에는 새로운 것이 없습니다. 단지 변수로 벡터와 유사한 객체를 이용한다고 해서 프로그램이 저절로 물리법칙을 따라 작동하지는 않습니다. 그러나 이렇게 하면 코드를 단순화시킬 수 있습니다. 또한, 움직임을 프로그래밍하는 동안 반복적인 수학 연산을 할 때 편리한 함수 집합의 역할을 합니다.
지금은 벡터 개념을 막 배우는 단계이므로 2차원 세계에 있다고 가정할 것입니다. 여기 나오는 모든 예제는 3차원으로 간단하게 확장할 수 있습니다. (우리가 사용할 객체인 PVector는 3차원 벡터를 고려한 객체입니다) 하지만 2차원부터 시작하기가 더 쉽습니다.

PVector를 이용한 프로그래밍

벡터를 생각하는 한 가지 방법은 두 점 사이의 차이입니다. 한 점에서 다른 점으로 걸어가라는 지시에 대해 걸어가는 방법에 관해 생각해 보기 바랍니다.
다음은 여러 가지 벡터와 각 성분으로 나눈 것을 나타낸 그림입니다.
벡터들에 관한 그림
그림 1.2
| (-15, 3) | 서쪽으로 15 걸음으로 걸어간 후 돌아서 북쪽으로 3 걸음 걸어갑니다. | | (3, 4) | 동쪽으로 3 걸음 걸어간 후 돌아서 북쪽으로 4 걸음 걸어갑니다. | | (2, -1) | 동쪽으로 2 걸음 걸어간 후 돌아서 남쪽으로 1 걸음 걸어갑니다. |
지금처럼 움직임을 프로그래밍하기 전에도 본 기억이 있을 겁니다. 애니메이션의 각 프레임(ProcessingJS의 draw() 반복문의 한 주기를 떠올리세요)마다 화면에 있는 객체는 특정 수 픽셀만큼 가로 및 세로로 움직이도록 프로그래밍 합니다.
벡터를 이용하여 새로운 위치 예측하기
그림 1.3
각 프레임마다:
새로운 위치 = 현 위치에 속도를 적용한 값
속도가 벡터(두 지점 사이 거리의 차)라면, 위치는 무엇일까요? 위치도 벡터일까요? 엄밀히 말하자면, 위치와 벡터는 같은 개념이 아니라는 주장도 맞습니다. 왜냐하면, 위치가 한 점에서 다른 점으로 움직이는 방법을 나타내는 것은 아니기 때문입니다. 위치는 단순히 공간 내 한 지점을 나타내는 개념입니다.
그렇지만 위치를 원점에서 일정한 경로로 움직여서 도달한 장소라고 정의할 수도 있습니다. 그래서 위치는 위치와 원점 사이의 차이를 표현하는 벡터라고 할 수 있습니다.
벡터로 나타낸 위치의 개념
그림 1.4
위치와 속도의 기본이 되는 데이터를 검토해 보도록 하겠습니다. 탱탱볼 예제에서는 다음이 존재했습니다:
위치속도
x와 yxSpeed와 ySpeed
위치와 속도에 같은 데이터인 부동 소수점 수 x 와 y를 저장한다는 점을 잘 봐두세요. 벡터 클래스를 처음부터 작성한다면 다음과 같은 기본적인 코드부터 시작할 것입니다:
var Vector = function(x, y) {
    this.x = x;
    this.y = y;
};
본질적으로 PVector는 두 개(3차원에서는 세 개)의 값을 저장하기에 편리한 방법입니다.
그래서 다음 코드는…
var x = 100;
var y = 100;
var xSpeed = 1;
var ySpeed = 3.3;
이렇게 됩니다…
var position = new PVector(100,100);
var velocity = new PVector(1,3.3);
이제 두 개의 벡터 객체(위치와 속도)를 만들었으므로, 움직임에 대한 알고리즘인 location = location + velocity를 구현할 수 있습니다. 예제 1.1에서 벡터를 쓰지 않고 다음과 같이 나타냈습니다:
x = x + xSpeed;
y = y + ySpeed;
위의 수식을 이상적으로 표현하면 다음과 같이 나타낼 수 있습니다.
position = position + velocity;
그러나 JavaScript에서 덧셈 연산자 +는 기본 값(숫자, 스트링)에만 쓸 수 있습니다. 일부 프로그래밍 언어에서 연산자가 "중복정의(overloaded)"될 수 있으나 JavaScript에서는 그렇지 않습니다. 다행히 PVector 객체는 add()와 같은 일반적인 수학 연산 메소드를 포함하고 있습니다.

벡터 덧셈

PVector객체와 add() 메소드를 살펴보기 전에 수학과 물리 교과서에 나오는 일반적인 표기법을 이용하여 벡터의 덧셈을 살펴봅시다.
일반적으로 벡터는 볼드체(굵은 글씨) 로 표시하거나 위에 화살표를 써서 나타냅니다. 여기에서는 벡터(vector) 와 스칼라(scalar) (스칼라 는 정수나 부동 소숫점 수와 같은 단일 값입니다.)를 구분하기 위해, 화살표 표기법을 사용하겠습니다.
  • 벡터: u, with, vector, on top
  • 스칼라: x ​ 
다음과 같이 어떤 두 벡터가 있다고 가정해 봅시다.
벡터 2 개를 나타낸 그림
그림 1.5
각 벡터에는 두 개의 성분 x와 y가 있습니다. 두 벡터를 더한다는 것은 단순히 x의 값과 y의 값을 더하는 것입니다. 
그림 1.6
다른 말로 하면
w, with, vector, on top, equals, u, with, vector, on top, plus, v, with, vector, on top
위 식은 다음과 같이 나타낼 수 있습니다.
wxux+vx=wy=uy+vy
그런 다음 u와 v를 그림 1.6의 값으로 바꿉니다.
wx=5+3wy=2+4
이를 간단하게 하면,
wx=8wy=6
마지막으로 이 값을 벡터로 나타내면 다음과 같습니다.
w, with, vector, on top, equals, left parenthesis, 8, comma, 6, right parenthesis
이제 두 벡터의 덧셈을 어떻게 하는지 이해했으므로, 덧셈을 PVector 객체에 어떻게 구현하는지를 살펴보겠습니다. 또 다른 PVector 객체를 인수로 입력받아 x와 y 성분을 단순히 더해주는 메소드 add()를 만들어 봅시다:
var Vector = function(x, y) {
    this.x = x;
    this.y = y;
};

Vector.prototype.add = function(v) {
  this.y = this.y + v.y;
  this.x = this.x + v.x;
};
add()가 PVector의 내부에 어떻게 입력되었는지 살펴보았습니다. location + velocity 알고리즘을 갖는 탱탱볼 예제로 돌아가서 벡터 덧셈을 구현해 봅시다:
position.add(velocity);
그리고 이제 PVector 객체를 이용하여 탱탱볼 예제를 다시 만들 준비가 되었습니다! 코드를 보고 이전 코드와의 차이점을 한 번 찾아보세요.
위와 같이 바꾼 코딩 방법에서 한 가지 알아야 할 중요한 부분이 있습니다. PVector 객체를 위치의 x와 y 값, 속도의 x와 y 값 두 개를 나타내는데 사용했지만, 보통 PVector의 x와 y 성분을 개별적으로 참조해야 할 일이 자주 있습니다. ProcessingJS에서 객체를 그릴 때는 다음과 같이 표현할 수 없습니다:
ellipse(position, 16, 16);
ellipse() 함수는 PVector를 인수로 허용하지 않습니다. 타원은 두 개의 스칼라 값인 x-좌표와 y-좌표로만 그릴 수 있습니다. 그래서 반드시 PVector 객체에 접근하여 객체지향적 점 표기법을 이용하여 x와 y 성분을 빼야 합니다.
ellipse(position.x, position.y, 16, 16);
원이 창의 가장자리에 걸쳐있을 때도 똑같은 문제가 발생합니다. 이 때도 두 벡터인 location과 velocity의 개별 성분에 접근해야 합니다:
if ((position.x > width) || (position.x < 0)) {
  velocity.x = velocity.x * -1;
}
실망하셨나요? 벡터를 사용하는 것이 오히려 코드를 복잡하게 만든 것으로 보일 수 있습니다. 충분히 그렇게 생각할 수 있지만, 아직 벡터를 사용한 프로그램의 힘을 다 보지 않아서 그렇습니다. 간단한 탱탱볼에 벡터 덧셈을 구현한 것은 첫걸음에 불과합니다.
이제 더 많은 객체들과 힘이 존재하는 경우(곧 배웁니다)를 알아볼 텐데, 그럴수록 PVector의 장점이 더욱 분명해질 것입니다. 계속해보세요!

본 "내추럴 시뮬레이션" 과정은 다니엘 쉬프만(Daniel Shiffman)이 저술한 "The Nature of Code"의 내용을 차용한 것이며, 본 내용물의 저작권은 Creative Commons Attribution-NonCommercial 3.0 Unported License를 적용합니다.


반응형