게임 수학 공부

Kyu_·2026년 9월 4일

게임수학

목록 보기
1/2

1일차

목표

3D 모델 하나가 GPU에 들어가서 모니터의 픽셀이 되기까지 무슨 일이 일어나는지 설명할 수 있다.

렌더링 파이프 라인

3D 모델의 정점
   ↓
정점 변환
   ↓
화면에 보이는 부분만 남김
   ↓
삼각형을 픽셀 후보들로 변환
   ↓
각 픽셀의 색 계산
   ↓
깊이/스텐실 등의 검사
   ↓
이미지 버퍼에 기록
   ↓
화면 출력

게임에서 메시 하나를 그린다고 했을때 대략 이런 과정을 거침
이것을 크게 세부분으로 나눈다
그래픽 처리기 -> 정점 변환 -> 레스터화와 단편 연산


GPU

GPU는 무엇을 받는가??

GPU입장에서는 기본적으로 정점(Vertex)과 정점들이 어떻게 연결되어 있는가가 중요

       V0
       /\
      /  \
     /    \
   V1------V2

삼각형 하나라면 정점 세개가 있고 이 세개를 연결해 삼각형을 만든다. 3D 모델은 이런 삼각형이 아주 많이 모여있는 형태라고 생각하면 된다.

수천~수만개가 보이면 메시가 되고, 이때 3차원 벡터가 등장한다.


CPU와 GPU는 왜 나누어져있나

CPU는 GPU에게

이 메시 그려
이 텍스처 사용해
이런 설정으로 렌더링해

같은 렌더링 작업을 전달하면 GPU가 그 작업을 진행한다.
이는 비동기적으로 작동한다 (CPU와 GPU를 억지로 계속 동기화하면 성능이 떨어진다)

CPU
 │
 │ "이거 렌더링해"
 ▼
GPU ───── 렌더링 중 ─────→
 │
 │
CPU ───── 다른 게임 로직 수행 ─────→

VRAM

GPU에도 사용할 메모리가 필요하다.
VRAM에는 여러 그래픽 데이터가 들어가는데 대표적으로

텍스처
이미지 버퍼
깊이 버퍼
스텐실 버퍼

등이있다.


Front Buffer와 Back Buffer

GPU가 화면을 그린다고 해서 모니터에 직접 픽셀 하나씩 바로 그리는 방식은 아니다.

전면 이미지 버퍼(Front Buffer)와 후면 이미지 버퍼(Back Buffer)가 있다.

Front Buffer는

현재 사용자에게 보여주고 있는 화면

Back Buffer는

GPU가 다음 화면을 그리고 있는 곳

이라고 생각하면 됨
Back Buffer가 완성되면 두 버퍼를 교체하는데 이를 Buffer Swap이라고 한다.


Depth Buffer

GPU가 단순히 픽셀 색만 그리면

먼 물체를 나중에 그렸다는 이유로
앞의 문제를 덮어버리는 문제

가 생길 수 있음 그래서 픽셀마다 깊이값을 저장한다.
Color Buffer -> (120, 50, 20) : 픽셀 색상
Depth Buffer -> 0.37 : 픽셀 깊이

개념적으로 현재 저장된 깊이 = 0.7, 새로운 픽셀 깊이 = 0.3 이렇게 새로운 픽셀이 더 앞에 있다면 그 픽셀이 살아남는다.
그래서 뒤의 물체가 앞 물체를 뚫고 보이지 않도록 만들 수 있다.

이미지의 각 픽셀에 대한 깊이 값을 담고, 가려진 표면을 제거하는데 사용하는 버퍼


Stencil Buffer

각 픽셀에 대해서 여기를 그릴지 말지 등을 제어하기 위한 정수 마스크

이미지 버퍼 각 픽셀에 대한 정수 마스크, 픽셀단위의 렌더링 활성화/비활성화에 사용


정점 변환

캐릭터 머리의 어떤 정점 좌표가 (0, 0, 100)이라고 했을때 이 좌표가 무엇을 기준으로 한건가 이게 중요하다.
그래서 3D모델에는 정점 좌표가 있다.

물체 공간
    ↓
세계 곤강
    ↓
카메라 공간
    ↓
동차절단공간
    ↓
정규화된 장치 좌표
    ↓
윈도우 공간

물체 공간

Object Space

캐릭터 모델을 만들었다고 했을때 모델 중앙이 (0, 0, 0)이라 했을때

          머리
        (0,0,2)
           │
           │
         몸통
        (0,0,1)
           │
           │
        (0,0,0)

이 좌표는 게임 세계 전체 좌표가 아님 이 캐릭터 모델 자체를 기준으로 하는 좌표이다.

물체 공간은 특정 모형에 국한되어 그 모형에 쓰이는 좌표계


세계 공간

World Space
게임 월드 전체 기준


Camera Space

화면을 만드려면 중요한 문제가 있다. 카메라가 어디있는가??
세계의 모든 물체를 카메라를 기준으로 다시 표현한다.
이를 Camera Space라고 한다.

카메라 공간을 x, y 축이 화면과 정렬되고 z축이 시선 방향과 평행한 좌표계라고 한다.

카메라 입장에서 이 물체는 어디에 있는가?


Projection

3D에서는 (x, y, z)인데
모니터는 (x, y)이다.

더 중요한거는 원근감 그래서 투영변환(Projection Transformation)을 한다.
투영 변환은 카메라에서 멀수록 물체가 작게 보이게 해 장면에 원근감을 추가하는 역할


Clipping

카메라 뒤에 있는 물체나 화면 밖에 완전히 벗어난 삼각형까지 그릴필요는 없다.

       Camera
         📷
        /   \
       /     \
      /       \
     / Visible \
    /           \

그래서 보이는 영역 밖을 잘라냄

동차절단공간의 절단이 이 의미와 연결됨


정규화된 장치 좌표

화면 해상도가 컴퓨터마다 다르기때문에 처음부터 특정 픽셀 좌표로 작업하는 것보다 일정한 범위의 좌표로 표현하는 단계가 있음

정규화된 장치 좌표의 x, y,z를 마지막에 실제 화면 크기로 바꿈


Window Space

정규화된 좌표를 마지막 실제 화면 좌표로 변환 FHD라고 했을때 화면 중앙이면 대충 960, 540 같은 위치가 됨
여기까지 오면 정점이 화면의 어디에 위치할 것인가가 결정됨

이를 위해 행렬을 배움

Object -> World -> Camera


레스터 화

정점 세개의 안쪽을 다채우는것을 레스터화 라고 함
기본 도형의 영역을 픽셀들로 채우는 과정

삼각형 -> 픽셀 후보들


Fragment

레스터화가 되면 바로 화면에 찍는게 아님
각 픽셀 위치마다 여러 정보가 생김 이를 묶어서 단편(Fragment)라고 부름

Fragment

화면 위치
깊이값
색
텍스처 좌표
등등...

대신 Fragment는 최종 픽셀이 아니다.
아직 최종적으로 화면에 기록될지 결정되지 않음
왜냐하면

다른 물체 뒤에 가려졌을 수도
스텐실 조건에 실패할 수도
화면 제한 영역 밖일수도있다.

Face Culling

레스터화 전에 필요없는 면을 제거할 수도 있다.
상자를 앞에서 보면 뒤쪽면은 어차피 보이지 않는다. 그래서 카메라 반대쪽을 향하는 면을 제거해서 불필요한 렌더링을 줄일 수 있다.

이를 면 선별(Face Culling)이라고 함

안보이는 방향의 삼각형을 미리 버릴 수 있다.


Fragment Shading

각 Fragment의 색을 계산

텍스처 색 + 빛의 세기 + 정점에서 전달된 정보

이런 정보를 이용해 Fragment의 최종 색상을 계산한다.

단편 셰이딩(Fragment Shading) 또는 픽셀 셰이딩이라고 표현한다.


Depth Test

계산된 Fragment는 여러 판정을 거친다.

Fragment
   ↓
픽셀 소유권 판정
   ↓
가위 판정
   ↓
알파 판정
   ↓
스텐실 판정
   ↓
깊이 판정
   ↓
혼합
   ↓
Image Buffer

여기서 Depth Test란 같은 화면 위치에 두 Fragment가 왔다고 했을때 Depth가 더 낮은 즉 카메라에 더 가까운 Fragment만 화면에 남는다.

3D 공간에서 어떤 물체가 앞에 있고 뒤에 있는지를 최종 픽셀에서 해결함


Blending

모든 테스트를 통과했다면 색을 이미지 버퍼에 반영
그냥 덮어 쓸수도 반투명 물체라면 기존 색과 섞을 수도 있다.
이를 Blending이라고 함


1일차 정리

① Object Space의 정점
        ↓
② World Space로 변환
        ↓
③ Camera Space로 변환
        ↓
④ Projection
        ↓
⑤ Clip
        ↓
⑥ 화면 좌표 결정
        ↓
⑦ Triangle Rasterization
        ↓
⑧ Fragment 생성
        ↓
⑨ Fragment 색 계산
        ↓
⑩ Depth / Stencil 등의 판정
        ↓
⑪ Image Buffer 기록
        ↓
⑫ Back Buffer 완성
        ↓
⑬ Buffer Swap
        ↓
화면에 보임

줄이자면

Vertex
  ↓
Object
  ↓
World
  ↓
Camera
  ↓
Projection

벡터와 행렬은 이를 이해하기 위한 도구


연습문제

  1. 3D 모델 하나가 있다고 하자. GPU 입장에서 모델은 단순히 "캐릭터"라는 하나의 덩어리가 아니다. 모델의 표면은 일반적으로 어떤 기본도형들의 집합으로 표현되는가?

삼각형의 집합

  1. Front Buffer와 Back Buffer는 각각 어떤 역할을 하는가??

Front Buffer는 현재 화면에 보이는 화면 Back Buffer는 다음 프레임 화면을 그리고 BackBuffer가 다 그려지면 Buffer Swap을 통해서 화면에 출력한다.

  1. 화면의 동일한 픽셀 위치에 몬스터 A와 몬스터 B의 Fragment가 들어왔다. A가 카메라에 더 가깝다. 어떤 버퍼와 어떤 판정을 이용해서 A만 화면에 남도록 만들 수 있는가?

Depth Buffer에 들어있는 깊이 값으로 Depth Test를 해서 값이 더 작은 즉 카메라에 더 가까운 A만 남도록한다.

  1. 캐릭터 머리 정점의 좌표가 (0, 0, 100)이라고 하자. 이 값만 보고 "게임 월드의 (0,0,100)에 머리가 있다"고 단정할 수 없는 이유를 Object Space와 World Space를 사용해서 설명해봐.

기준이 어떤 값이냐에 따라 다르기 때문 Object Space의 경우에는 물체 기준으로 좌표를 매김, World Space는 세계 좌표를 기준으로 하기때문

  1. 다음 순서를 올바르게 배열
Object Space
World Space
Camera Space
Projection / Clip
Window Space
  1. 래스터화가 하는 일을 한 문장으로 설명해봐. 정점, 삼각형, 픽셀이라는 단어 중 적어도 두 개를 사용하면 된다.

레스터화는 삼각형의 정점만 있을때 그 내부 픽셀을 채우는것을 말한다.

개선

화면에 투영된 삼각형이 어떤 픽셀 위치들을 덮는지를 계산해서 Fragment들을 생성하는 과정

  1. Fragment와 최종 Pixel이 완전히 같은 것이라고 할 수 없는 이유는 무엇인가?

Fragment를 검증하는 단계가 더 있기 때문이다 대표적으로 오늘배운 Depth Test가 있다.

  1. 게임에서 캐릭터가 벽 뒤에 완전히 가려져 있는데도 캐릭터 색이 벽 위에 그대로 그려진다고 하자. 오늘 배운 개념 중 가장 먼저 의심해볼 부분은 무엇인가?

Projection이 의심? 혹은 Clipping이 의심된다.

오답

벽 뒤에 캐릭터가 벽 위에 그대로 그려지고 있다면 의심할것은 Depth Test/Depth Buffer다. 왜냐면 이미 캐릭터가 화면에 투영되는것 자체는 성공한 상태이기 때문, Projection은 3D위치를 화면에 어떻게 투영할까? 이고 Clipping은 카메라가 보는 영역 밖의 물체를 어떻게 잘라낼까?

  1. 가장 중요한 문제. 아래 빈칸을 네 말로 채워봐.

Object Space, World Space, Camera Space, Window Space

  1. 마지막으로 책을 보지 말고 "3D 캐릭터 하나가 화면에 나타나기까지의 과정"을 5~10줄 정도로 설명해봐.

3D캐릭터는 여러 정점과 그 정점들로 이루어진 삼각형 메시로 구성된다. 먼저 캐릭터의 정점들은 캐릭터 자신을 기준으로 하는 Object Space에 존재한다. 이 정점들에 캐릭터의 위치, 회전, 크기 등의 변환을 적용해서 World Space좌표로 바꾼다. 그 후 카메라를 기준으로 Camera Space로 변환한다.
이후 Projection을 통해 3D좌표를 화면에 표시할 수 있는 형태로 변환하고, 카메라가 볼 수 있는 영역 밖의 부분은 Clipping한다. 변환된 삼각형은 Rasterization을 통해 화면에서 차지하는 Fragment들로 만들어진다. 각 Fragment에 대해 색과 텍스처등의 값을 계산하고 Depth Test나 Stencil Test같은 판정을 수행한다. 살아남은 Fragment의 색을 이미지 버퍼에 기록한다. GPU가 Back Buffer에 한 프레임을 모두 그리면 Buffer Swap을 통해 완성된 이미지가 화면에 출력된다.


2일차

목표

벡터가 무엇인지, 벡터끼리 더하고 빼는것이 무슨 의미인지, 벡터의 길이를 어떻게 구하는지, 정규화가 무엇이고 왜 하는지를 설명할 수 있다.


벡터란 무엇인가?

벡터를 기본적으로 여러개의 실수를 하나로 묶은것을 정의한다.

FVector PlayerPosition(100, 200 ,50);

숫자 세개로 3D공간의 무언가를 표현할 수 있다.
반대로 방향으로 해석할 수도 있다.

FVector Forward(1, 0, 0);

벡터는 크게 두가지 정보가 있다고 생각하면 편하다 어느 방향인가 + 얼마나 긴가


벡터 덧셈

두 벡터를 더할때는 같은 성분끼리 그냥 더함
게임에서는 현재 위치에 이동량을 추가한다 라고 이해하면 된다.


벡터 뺄셈

얘도 성분끼리 빼는건 같다.
플레이어 P = (2, 3, 0) 이라고하고
적이 E = (7, 5, 0) 이라고 했을때

E - P = (5, 2, 0)은 플레이어에서 적까지 얼마나, 어느 방향으로 이동해야 하는가를 뜻한다.
게임에서도 Target - Current는 결론적으로 Current에서 Target으로 향하는 벡터를 만드는구나라고 생각하면 된다.


스칼라란?

벡터와 함께 등장하는 스칼라는 그냥 숫자 하나라고 생각
스칼라는 벡터에 곱해서 사용을 하는데 벡터의 방향이나 크기를 조절할때 사용한다.
Velocity = Direction * Speed;
만약에 Direction(1, 0, 0), Speed = 500이라고 했을때 Velocity = (500, 0, 0)이 된다.
Direction은 어느쪽으로 갈지, Speed는 얼마나 빠르게 갈지를 담당


벡터의 길이

|V| = x2+y2+z2\sqrt{x^{2} + y^{2} + z^{2}}

벡터의 길이는 이렇게 계산함 2D개념의 피타고라스를 3D로 확장

당연히 위치 두개 사이의 거리도 이거를 착안해서 나옴

Player=(1,1,0)
Enemy=(4,5,0)

Enemy - Player = (3, 4, 0)

32+42+02\sqrt{3^{2} + 4^{2} + 0^{2}} = 5

따라서 플레이어와 적 사이의 거리는 5

Distance = |EnemyPosition - PlayerPosition|

단위벡터

벡터의 길이가 정확히 1이면 단위벡터라고 한다
방향만 필요할때 편하게 사용

V=(3,4,0)
|V| = 5

정규화 = V / |V| -> (0.6, 0.8, 0) 이런식으로 된다.

정규화

1이아닌 벡터를 단위벡터로 만드는것을 정규화라고 한다.
크기만 사라지고 방향만 남음

정규화를 게임에서 왜 많이 쓰는가?

예시로 플레이어 위치와 적 위치가 있는데

FVector ToEnemy = EnemyPosition - PlayerPosition;

이 상태의 ToEnemy에는 두 정보가 섞여있다..
적이 어느 방향인가? + 적이 얼마나 멀리 있는가?

그런데 AI에게 적 방향으로 총알을 발사해 라고 한다면 거리정보는 필요가 없다.

FVector Direction = ToEnemy.GetSafeNormal();
velocity = Direction * BulletSpeed;

방향만남기고 스피드를 곱하는 식으로
영벡터는 정규화할 수 없다 0으로 나눠야 하기때문 그래서 일반적으로 정규화하기전에 길이가 0인지 확인해야함

헷갈리는 용어

Normalize는 벡터의 길이를 1로 만드는 연산이고
Normal Vector는 어떤 표면에 수직인 벡터를 말함


연습문제

  1. 벡터 V = (3, 4, 0)의 길이를 구하고, 이 벡터를 정규화한 결과도 구해봐.

5
(0.6, 0.8, 0)

  1. P = (2, -1, 3), Q = (1, 4, -2)일 때 P + Q와 P - Q를 각각 계산해봐.

P+Q = (3, 3, 1)
P-Q = (1, -5, 5)

  1. 플레이어 위치가 (2, 3, 0), 적의 위치가 (8, 11, 0)이다.플레이어에서 적을 향하는 벡터를 구하고, 둘 사이의 거리도 구해봐.

E-P = (6, 8, 0), 거리 10

  1. 벡터 V=(2,0,0)에 각각 3, 0.5, -1을 곱하면 결과가 어떻게 되는가? 그리고 세 결과가 원래 V와 비교해서 방향과 길이가 어떻게 달라졌는지도 설명해봐.

(6, 0, 0)
(1, 0, 0)
(-2, 0, 0)
3배, 0.5배, -1배, -1의 경우 방향이 바뀌었음, 나머지는 길이가 늘거나 줄어듬

  1. 다음 두 벡터가 있다.
   A = (10, 0, 0)
   B = (1, 0, 0)

둘은 값이 다른데도 "같은 방향을 가리킨다"고 말할 수 있는 이유는 무엇인가? 그리고 A를 정규화하면 무엇이 되는가?


정규화하면 (1, 0, 0)이 되기때문에 둘의 방향은 같다 둘다 x에만 값이 있기도하고

  1. 다음 코드가 어떤 계산을 하는지 설명해봐.
FVector ToTarget = TargetPosition - PlayerPosition;
FVector Direction = ToTarget.GetSafeNormal();
FVector Velocity = Direction \* 500.0f;

특히 ToTarget과 Direction이 가지고 있는 정보의 차이를 설명해봐.


ToTarget이라는 플레이어 위치에서 타겟 위치를 향하는 벡터가 있다. 이는 방향과 얼마나 떨어져있는지가 벡터의 크기 형태로 함께들어있고, Direction은 이를 정규화해서 방향만 남기고 길이는 1로 만듬

  1. (0,0,0)을 일반적인 방식으로 정규화하려 하면 왜 문제가 생기는가?

0으로 나눠야하기때문에 문제가 생긴다.

  1. 오늘의 핵심 문제. 총알을 플레이어 위치에서 적을 향해 속력 1000으로 발사한다고 하자. 필요한 계산 과정을 코드가 아니라 벡터 개념만 사용해서 순서대로 설명해봐.

  1. ToTarget = EnemyPosition - PlayerPosition

    적까지의 방향 + 거리

  2. Normalize(ToTarget)

    적 방향만 남김, 길이 = 1

  3. Direction × 1000

    적 방향으로 속력 1000을 가진 Velocity 생성


3일차

목표

  1. 내적을 계산할 수 있다
  2. 내적의 결과가 왜 벡터가 아니라 숫자인지 안다.
  3. 내적의 부호로 두 방향의 관계를 판단할 수 있다.
  4. 게임에서 "적이 내 앞에 있는가?"를 내적으로 판단할 수 있다.

내적이란?/

P=(Px​,Py​,Pz​)
Q=(Qx​,Qy​,Qz​)

두 벡터의 내적은
P⋅Q=Px​Qx​+Py​Qy​+Pz​Qz​
이다.

P=(1,2,3)
Q=(4,5,6)

P⋅Q = 1×4 + 2×5 + 3×6 = 4 + 10 + 18 = 32

여기서 중요한것은 두 벡터를 내적하면 스칼라가 반환이 된다. 즉 숫자 하나가 반환된다.

이 계산은 왜할까?

내적의 핵심 공식은

P⋅Q = ∥P∥∥Q∥cosθ
여기서 (\theta)는 두 벡터 사이의 각도

즉 내적은 두 벡터 사이의 각도와 연결이 되어있다.

우선 단위벡터끼리 생각하자

정규화를 하면
∥P∥ = ∥Q∥ = 1

그러면 결국 P⋅Q = 1 x 1 x cosθ = cosθ

같은 방향

두 벡터가 정확히 같은 방향이면 cos 0 = 1
따라서 정규화된 두 벡터라면 내적이 1이다.
내적이 1에 가까울수록 같은 방향을 보고 있다.

직각

cos90 = 0이므로 두벡터의 내적이 0이면 두 벡터는 서로 수직이다.

반대 방향

cos180 = −1 이므로 -1이면 완전히 반대방향

왜 정규화를 하는가?

A = (1,0,0)
B = (1,0,0)
이면

A⋅B = 1

A = (100,0,0)
B = (100,0,0)
이면

A⋅B = 100×100 = 10000

벡터의 길이도 결과에 들어가버린다.
그래서 방향 관계만 보고 싶으면 정규화를 해서 확인해야 한다.

정리

내적의미
1완전히 같은 방향
0보다 큼대체로 같은 쪽
090도
0보다 작음반대쪽
-1완전히 반대 방향

두 벡터를 정규화하면 -1 ~ 1사이로 나옴


예제 - 적이 내 앞에 있는가?

플레이어가 오른쪽을 바라본다고 가정하고 적이 플레이어 오른쪽 앞에 있다고 했을때

Player Forward = (1, 0)

ToEnemy = EnemyPosition - PlayerPosition;

저번에 배운대로 적의 위치 벡터 - 플레이어 위치 벡터를해서 ToEnemy 벡터를 구함

Direction = Normalize(ToEnemy)

이를 정규화해서 방향만 남김

Dot(PlayerForward, Direction)

내적을해서 결과를 구함 예를들어 0.8이 나온다 하면
적이 플레이어가 바라보는쪽에 있다.
-0.7이라고 하면 적이 플레이어 뒤쪽에 있다는 뜻

앞이라기에는 너무 넓지 않나?

그래서 실제 게임에서는

if (Dot > 0.8f)
{
    // 꽤 정면에 있음
}

이런식으로 0.8정도 값을 해서 일정 각도 안에 들어오면 시야에 들어왔다고 판단할 수도 있다.


결론

x끼리 곱하고 y끼리 곱하고 z끼리 곱해서 더한다. 는 단순 계산법

두 벡터가 얼마나 같은 방향을 향하고 있는지를 숫자 하나로 알아내는 연산

을 내적이라고 생각하는게 좋다.


4일차

목표

내적으로 두 벡터 사이의 각도를 구할 수 있다.
벡터 투영이 무엇인지 설명할 수 있다.
어떤 이동 벡터에서 특정 방향 성분만 뽑아낼 수 있다.


내적으로 각도 구하기

A⋅B=∥A∥∥B∥cosθ
내적 식은 이랬다. 벡터 길이 x 길이 x 코사인 세타
이는 다시말하면

cosθ = A⋅B​ / ∥A∥∥B∥

가 될 수 있다.

여기서 세타만 남기면

θ=cos−1(∥A∥∥B∥A⋅B​)

가 될수있다. (-1승)

A=(1,0)
B=(0,1)
라고 했을때 내적은 0
즉, 코사인 세타는 0 / 1x1 = 0

θ=cos−1(0)=90∘

Dot값과 각도의 관계

정규화된 벡터 기준으로

각도Dot
1
60°0.5
90°0
120°-0.5
180°-1

벡터 투영

Projection, 투영이다.
벡터를 다른 벡터위에 투영

어떤 벡터에서 원하는 방향으로 향하는 성분만 뽑아내는 것

내적은 두벡터가 얼마나 같은 방향을 향하고 있는가를 알려준다. 그러니 P안에 Q방향이 얼마나 들어있는가? 를 계산할때 내적이 사용되는건 자연스럽다.

예시

벡터 P가 벡터 Q방향으로 얼마나 들어있는지를 벡터 형태로 뽑아낸것?
번역이 조금 이상한데 예시를 보면

P = (3, 4)
Q = (1, 0)

이라고 했을때 x축방향 Q에 P를 투영하면 P에서 Q방향 성분은 당연히 (3, 0)이 된다.


Q가 (0.8, 0.6)같은 대각이면 어떨까?
어떤 성분이 그 방향에 평행한지 눈으로 바로 구하기가 어렵다.
그래서 내적을 사용

벡터 P와 Q 사이 각도를 θ라고 하면

P가 Q방향으로 가진 길이는
∥P∥cosθ이다.

그런데 내적 공식은 P⋅Q=∥P∥∥Q∥cosθ
양변을 |Q|로 나누면
P⋅Q / |Q| = ∥P∥cosθ가 된다.

Q방향으로 투영된 P의 길이를 내적으로 구할 수 있다.

궁금한거

P가 Q방향으로 가진 길이가 왜 |P|cosθ 이거인가

            P
           /|
          / |
         /  |
        /   |
       / θ  |
------●-----●------------→ Q
      <---->
      Q 방향 성분

여기서 P를 Q방향으로 내려서 직각삼각형을 만듬
빗변 = P의 길이 = |P|
세타의 이웃변 = P가 Q방향으로 가지고 있는 길이
나머지변 = Q의 수직 방향의 길이

코사인세타는 빗변분의 이웃변, 이웃변/빗변
즉, 코사인 세타 = P의 Q방향 성분 길이 / |P|

왜 Q의 방향 단위벡터가 (\frac{Q}{|Q|})인가?

Q가 (6, 8)이라고 했을때
Q의 길이는 10, 현재 길이가 10이니까 전체를 10으로 나누면

Q / 10 = (0.6, 0.8)
이 벡터의 길이는 0.62+0.82\sqrt{0.6^{2} + 0.8^{2}} = 1
그래서 Q / |Q|는 Q와 같은 방향을 바라보면서 길이만 1로만든 단위벡터


5일차

목표

외적을 하면 무엇이 나오는가
외적을 어떻게 계산하는가
왜 결과가 두 벡터에 수직인가?
왜 A x B와 B x A의 방향이 반대인가?


내적과 외적의 차이

내적은 숫자 하나가 반환된다.

A · B  → 숫자 하나

A · B = 0이면 두 벡터가 90도라는걸 알 수 있었다.

반면 외적은

A x B -> 새로운 벡터

즉,

내적은 두방향을 비교한다 -> 스칼라
외적은 두 방향으로부터 새로운 방향을 만든다 -> 벡터

외적의 핵심은 둘다에 수직인 방향을 구한다 이다.


게임 예시

B
↑
│
│
A ──────→ C

A에서 C로 향하는 벡터와, A에서 B로 향하는 벡터가 있다.
두 벡터 모두 바닥에 붙어있다.

그렇다면 바닥에서 수직으로 튀어나오는 방향이 하나 필요하다.

이 삼각형의 앞면이 어디인가?
빛을 어느 방향으로 반사해야 하는가?
이 표면의 Normal은 무엇인가?

이때 외적을 사용한다.
두벡터가 만드는 평면에 수직인 벡터를 만들어주는 것


외적 계산 공식

A×B=(Ay​Bz​−Az​By​,Az​Bx​−Ax​Bz​,Ax​By​−Ay​Bx​)

외적또한 방향뿐아니라 크기에도 의미가 있다.

외적의 크기 -> ∣A×B∣=∣A∣∣B∣sinθ

       B
      /|
     / |
    /  | height
   / θ |
  /____|
     A

A의 길이 |A|, B에서 A에 수직인 높이는 |B|sinθ

외적의 방향은 두 벡터에 수직인 방향이고
외적의 크기는 두 벡터가 만드는 평행사변형의 넓이이다.


오른손 법칙

X, Y축 모두에 수직인 방향은 생각해보면 +Z, -Z축 두개가 있다.
이때 오른손 법칙이 등장한다.
오른손으로 A에서 B쪽으로 손가락을 감았을때 엄지가 향하는쪽이 외적방향이다.

A x B = -(B x A)가 성립한다.

평행한 벡터 외적

A = (1, 0, 0)
B = (3, 0, 0)

외적 크기는
|AxB| = |A||B|sin0∘

근데 sin0은 0이기때문에
두벡터가 평행하면 영벡터가 된다. A x B = (0, 0, 0)

────────────→
───────────────→

두벡터가 이렇게 평행하게 있을때 두 벡터가 만드는 평행사변형은 높이가 0이다 그래서 넓이, 크기 모두 0


Unreal에서

FVector A(2.f, 0.f, 0.f);
FVector B(0.f, 3.f, 0.f);

FVector Cross = FVector::CrossProduct(A, B);
FVector Normal = Cross.GetSafeNormal();

같은 형태로 볼 수 있다.


결론

결과가 벡터다.
결과는 A, B 둘다에 수직
크기는 A벡터의 크기 곱하기 B벡터의 크기 곱하기 sin세타
외적은 순서가 중요하다
평행하면 0이다

       C
      / \
     /   \
    A-----B

B-A는 A에서 B로가는 방향과 길이 벡터
C-A는 A에서 C로가는 방향과 길이 벡터를 말한다.


6일차

목표

삼각형에서 Normal을 만드는 방법
정점 순서를 바꾸면 왜 Normal이 뒤집히는지
외적으로 삼각형 넓이를 구하는 방법
플레이어 기준으로 대상이 왼쪽/오른쪽인지 판정하는 방법


삼각형은 Normal은 왜 필요한가?

3D모델은 결국 수많은 삼각형으로 이루어져 있다.

       P2
       *
      / \
     /   \
    /     \
P0 *-------* P1

삼각형이 공간에서 어느 방향을 보고 있는지 알고 싶다.
예를들어 빛이 들어왔을때

Light
   ↓
   ↓
---------
Triangle
   ↑
 Normal

빛과 표면의 방향 관계를 계산하려면 표면에 수직인 방향이 필요하다.
그게 Normal이다


점세개만 가지고 Normal을 어떻게 만들까??

삼각형의 세점이 p0, p1, p2라고 했을때
점 자체끼리 바로 외적하는게 아님, 먼저 삼각형의 두 변을 만든다.
E1 = P1 - P0, E2 = P2 - P0
여기서 P1 - P0은 P0에서 P1로 가는 벡터

        P2
        *
       ↗
      / E2
     /
P0 *────────→ * P1
         E1

따라서 E1과 E2는 삼각형 표면위에 있다.
그러면 E1 x E2를 하면 둘 모두에 수직인 벡터가 나온다.

삼각형이 놓인 평면의 법선을 N = (P1 - P0) x (P2 - P0) 형태로 구함


예시

P0 = (0, 0, 0)
P1 = (4, 0, 0)
P2 = (0, 3, 0)

E1 = P1 - P0 = (4, 0, 0)
E2 = P2 - P0 = (0, 3, 0)

외적 E1 x E2 = (4, 0, 0) x (0, 3, 0) = (0, 0, 12)
즉, 표면에서 +Z 방향으로 튀어나온다.
길이가 12라서 조명등에 사용할 방향만 필요하면 정규화해서 N = (0, 0, 12) / 12 = (0, 0, 1)


Raw Normal과 Unit Normal을 구분하자

게임 코드에서는 보통 조명, 반사, 충돌 방향등에 사용할때 단위 Normal을 원한다.


정점 순서가 왜 중요할까?

(P1 - P0) x (P2 - P0) 는 +Z가 나왔었다.
그런데 두번째 세번째 정점을 바꾸면 (P2 - P0) x (P1 - P0) = (0, 3, 0) x (4, 0, 0) 이면 (0, 0, -12)가 된다.
왜냐면 외적은 A x B = -(B x A) 이기때문에


외적의 크기는 삼각형 상태도 알려준다.

삼각형 세점이

P0 ------ P1 ------ P2

이렇게 한직선위에있다고 가정해보자

E1 x E2 = 0이고 Area = 0이기 때문에 이 삼각형은 실제 면을 만들지 못한다.
이를 Degenerate Triangle 즉, 퇴화 삼각형이라고 한다.
게임/그래픽 코드에서 외적 크기가 거의 0인지 검사하면

이 세점이 제대로 된 삼각형을 만드는가?

를 확인하는데 사용할 수 있다.


외적으로 왼쪽/오른쪽을 판정할 수 있다.

플레이어가 +X방향을 보고있다고 가정

Forward = +X
Right = +Y
Up = +Z

라고 했을때 플레이어 Forward F = (1, 0, 0)
적이 플레이어의 오른쪽에 있다면 T = (0, 1, 0)

외적 F x T = (1, 0, 0) x (0, 1, 0) = (0, 0, 1) -> 즉 +Z
반대로 적이 왼쪽이면 T = (0, -1, 0), F x T = (1, 0, 0) x (0, -1, 0) = (0, 0, -1) -> -Z

따라서 이 상황에서는 Cross의 Z > 0 오른쪽, Z < 0 왼쪽 -> 이렇게 판단할 수 있다.


정리

삼각형의 Normal

E1​=P1​−P0​
E2​=P2​−P0​
N=E1​×E2​

방향만 필요하면 Normalize


정점 순서

E1 x E2 = -(E2 x E1)
정점 순서를 뒤집으면 Normal도 뒤집힌다.


삼각형 넓이는

Area = 1/2 * |E1 x E2|


퇴화 삼각형

|E1 x E2| = 0
0에 가까울수록, 삼각형의 면적이 거의 0이다


좌우 판정

Side=(Forward×ToTarget)⋅Up

  1. (3, 0, 0), (0, 2, 0), (0, 0, 6), (0, 0, 1)
  2. -Z를 바라본다. 외적은 A x B = -(B x A) 이기 때문에
  3. 10, 외적해서 (0, 0, 20) 의 길이 20 을 반으로 (삼각형이니까)
  4. E1, E2벡터를 구함 P0에서 P1, P2로 향하는 벡터, 그것을 외적을 한다. 둘다에 수직인 벡터를 구함 이의 크기를 구하면 이것은 삼각형이 아니고 평행사변형이다. 이를 반으로 나누면 삼각형의 넓이
  5. 같은 방향에 있기때문에 사실상 변을 구할수가없다 외적해도 0이 나오므로 크기가 0
  6. 음수 -Z 즉 왼쪽이다.
  7. 같은 결과가 이니다 방향이다름 위에서 했던 외적의 성질때문

0개의 댓글