[게임수학] 6-1. 내적(Dot product)

2025. 9. 12. 17:00·게임수학
728x90

→ 이 글은 「이득우의 게임 수학」을 바탕으로 작성했습니다.

■ 벡터의 내적(Dot Product)

내적은 같은 차원의 두 벡터가 주어졌을 때 구성하는 성분을 곱한 후 더해 "스칼라"로 만든다.

$$ \vec u \cdot \vec v = x_1\cdot x_2 + y_1\cdot y_2 $$

 

▼ 내적의 성질

곱셈과 덧셈으로 구성되어 있어 교환법칙이 성립한다. $\vec u \cdot \vec v = \vec v \cdot \vec u$
결과가 스칼라이기 때문에 결합법칙은 성립하지 않는다.  $\vec w \cdot (\vec u \cdot \vec v) \neq (\vec w \cdot \vec u) \cdot \vec v$
덧셈에 대한 분배법칙이 성립한다. $\vec w \cdot (\vec u + \vec v) = \vec w \cdot \vec u + \vec w \cdot \vec v$
자기 자신을 내적한 결과는 크기를 제곱한 결과가 나온다. $\vec v \cdot \vec v = (x^2 + y^2)$
두 벡터의 합의 내적은, 두 벡터의 크기의 합이다. $(\vec v + \vec u) \cdot (\vec v + \vec u) = |\vec v|^2 + |\vec u|^2 + 2(\vec v \cdot \vec u)$

 

1. 내적과 삼각함수

두 백터의 사잇각을 $\theta$라 한다면, 내적과 $\cos$함수의 관계는 다음과 같다.

$$ \vec v\cdot \vec u = |\vec v||\vec u| \cos \theta $$

[삼각형의 설정]

위 수식을 유도하기 위해 데카르트 좌표계에 세 점$(A,B,C)$으로 구성된 삼각형을 생성하고, 각 점을 마주 보는 벡터$(\vec a, \vec b, \vec c)$와 원점에 위치한 사잇각$(\theta)$로 구성하자.

 

그럼, 앞서 배운 삼각함수의 성질을 사용해 각 점의 좌표와 벡터는 다음과 같이 구할 수 있다.

삼각형 점의 좌표 변을 구성하는 벡터
$A = (|\vec c| \cos \theta, |\vec c|\sin \theta)$ 밑변 : $\vec a = C - B = (|\vec a| - 0, 0) = (|\vec a|, 0)$
$B = (0,0)$ 높이 : $\vec b = C - A = (|\vec a| - |\vec c| \cos \theta, |\vec c|\sin\theta)$
$C = (|\vec a|, 0)$ 빗변 : $\vec c = A - B = (|\vec c|\cos\theta, |\vec c|\sin\theta)$

 

[벡터의 뺄셈으로 표현]

$$ \vec b = C - A = \vec a + (-\vec c) $$

여기서, 벡터 $\vec b$는 점 $C-A$로 구한 벡터이다. 점 $C$의 위치벡터가 $\vec a$. 점 $A$의 위치벡터가 $\vec c$이므로, 위와 같이 표현할 수 있다.

  • 벡터의 정의상 출발점이 같은지 여부가 아니라, 좌표평면 어딘가에 그려졌더라도 “방향과 크기”만 같다면 같은 벡터로 취급한다.

 

이제 벡터$\vec b$의 크기를 제곱해보자. 벡터 $\vec b = (|\vec a|-|\vec c| \cos \theta, |\vec c|\sin\theta)$ 이므로, 벡터 $\vec b$를 제곱하게 되면

$$ |\vec b|^2 = (|\vec a| - |\vec c| \cos \theta)^2 + |\vec c|^2 \sin^2\theta \\ = |\vec a|^2 -2|\vec a||\vec c|\cos \theta + |\vec c|^2\cos^2\theta + |\vec c|^2 \sin^2\theta $$

 

위 식에서 $|\vec c|^2\cos^2\theta + |\vec c|^2 \sin^2\theta$ 이 부분은 아래와 같이 정리된다.

$$ |\vec c|^2\cos^2\theta + |\vec c|^2 \sin^2\theta \\ = |\vec c|^2(\cos ^2 \theta + \sin ^2 \theta) $$

 

여기서 삼각함수 항등식에 의해 $(\cos ^2 \theta + \sin ^2 \theta) = 1$이 되므로 최종적으로 식은 아래와 같이 정리된다.

$$ |\vec b|^2 = |\vec a|^2 + |\vec c|^2 - 2|\vec a||\vec c|\cos\theta $$

[벡터 크기의 제곱을 활용한 식(1)]

 

이번엔 같은 벡터를 내적하면 벡터 크기의 제곱이 되는 성질을 활용하여 식을 전개해 보자.

$$ |\vec b|^2 = \vec b \cdot \vec b \\ =(\vec a + (-\vec c))\cdot (\vec a + (-\vec c)) \\ = |a|^2 + |\vec c|^2 - 2\vec a\cdot \vec c $$

[자기 자신과 내적(2)]

 

그렇다면 식 1의 $- 2|\vec a||\vec c|\cos\theta$ 부분과, 식 2의 $-2\vec a\cdot \vec c$ 부분은 동일한 값이므로 아래의 식이 성립한다.

$$ \vec a \cdot \vec c = |\vec a||\vec c|\cos\theta $$

 

만약 두 벡터의 크기가 모두 1이라면, 두 벡터의 내적은 결국 cos 함수가 된다.

$$ \vec v\cdot \vec u = \cos \theta $$

 

두 단위벡터를 내적 했을 때, 그 값이 0이 되는 조건은 cos함숫값이 90 혹은 270(-90)이다. 따라서, 두 단위벡터의 내적값이 0이라면 두 벡터는 "직교한다".

  • 또한 두 표준기저벡터가 각 세타만큼 회전한 결과$(\cos \theta, \sin \theta),\ (-\sin\theta, \cos \theta)$의 내적도 언제나 0이 나온다.

 

2. 행렬 곱으로 내적을 표현

$$ \begin{bmatrix} a & b \\ c& d\end{bmatrix}\begin{bmatrix} x \\ y\end{bmatrix} = \begin{bmatrix} ax + by \\ cx + dy\end{bmatrix} = \begin{bmatrix} (a,b) \cdot (x,y) \\ (c,d)\cdot (x,y)\end{bmatrix} $$

 

행렬을 구성하는 2개의 행벡터와 벡터를 구성하는 열벡터를 내적으로 위와 같이 표현이 가능하다.

 

$$ \begin{bmatrix} a & b \\ c& d\end{bmatrix}\begin{bmatrix} e & f \\ g& h\end{bmatrix} = \begin{bmatrix} (ae + bg) & (af +bh) \\ (ce+cg) & (df + dh)\end{bmatrix} = \begin{bmatrix} (a,b)\cdot (e,g) &(a,b)\cdot(f,h) \\ (c,d) \cdot (e,g) & (c,d) \cdot (f,h)\end{bmatrix} $$

 

또한, 행렬의 곱셈 역시 위와 같이 내적으로 표현이 가능하다.

 

▶ 직교 행렬

$$ Q = \begin{bmatrix} a & c\\ b & d\end{bmatrix} $$

열백터와 행벡터를 구성하는 모든 요소의 크기가 1이고, $(a,b)$와 $(c,d)$가 서로 직교하는 행렬을 직교 행렬(Orthogonal matrix)라 한다.

 

이러한 직교행렬의 특징은 직교행렬의 전치행렬$(Q^T)$는 역행렬이 된다.

따라서 $Q\cdot Q^T = I$ 즉, 직교행렬과 그 역행렬의 곱은 항등행렬이 되고 이는 내적을 통해 증명할 수 있다.

 

$$ \begin{bmatrix}a &c \\ b & d \end{bmatrix} \cdot \begin{bmatrix} a & b \\ c & d\end{bmatrix} = \begin{bmatrix}(a,c)\cdot(a,c) & (a,c)\cdot(b,d)\\(b,d)\cdot(a,c) & (b,d)\cdot(b,d) \end{bmatrix} = \begin{bmatrix} 1&0 \\ 0&1\end{bmatrix} $$

자기 자신을 내적한 결과는 크기를 제곱한 결과이므로 1, 서로 직교하는 두 벡터를 내적 하면 0이므로 결과적으로 언제나 항등 행렬임을 보장한다.

 

$$ R_\theta = \begin{bmatrix}\cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix} \\ R_\theta^T \cdot R_\theta = \begin{bmatrix}\cos^2\theta + \sin^2\theta &0 \\ 0 & \cos^2\theta + \sin^2\theta\end{bmatrix} = I $$

회전 변환행렬 역시 행벡터와 열벡터의 크기가 1이고 서로 직교하므로 직교행렬이다.

 

회전 변환과 같이 변환 후에도 물체의 형태가 그대로 유지되는 변환을 강체 변환이라고 한다. 강체 변환의 조건은 다음과 같다.

  1. 변화된 기저벡터의 크기는 1이다.
  2. 모든 기저벡터는 서로 직교한다.
  3. 행렬식 값이 1이다. $det(R) = \cos^2\theta + \sin^2\theta = 1$

 

■ 내적의 응용

1. 시야 판별

게임 제작에서 내적을 활용하여 다양한 시스템을 구축할 수 있는데, 그중 하나가 바로 목표물이 캐릭터의 앞에 있는지 뒤에 있는지 판단할 수 있다.

[-180,180 범위의 cos 함수 그래프]

캐릭터의 정면(시선 방향)을 첫 번째 벡터로, 캐릭터에서 목표물을 향하는 벡터를 두 번째 벡터로 사용해 보자.

 

이 두 벡터를 내적 하여 사잇각을 구했을 때, 그 값이 양의 부호를 가진다면 캐릭터의 앞에, 음의 부호를 가진다면 캐릭터의 뒤에 있다고 판단할 수 있다.

 

public class DotProduct : MonoBehaviour
{
    [SerializeField] private RotateAround target;
    [SerializeField] private TextMeshProUGUI value;
    
    private Vector3 _forward;
    private Vector3 _targetDir;

    private void Start()
    {
        _forward = transform.forward;
    }

    private void Update()
    {
        _targetDir = (target.transform.position - transform.position).normalized;
        var dotProduct = Vector3.Dot(_forward, _targetDir);
        
        if (dotProduct > 0)
        {
            // Do Something..
        }
        else
        {
        	// Do Something..
        }
    }
}

[내적을 통한 앞뒤 판별]

내적을 통한 앞뒤 판별은 정말 쉽게 구현할 수 있다. 캐릭터에서 타겟으로 향하는 벡터를 구하고 정규화 한 뒤, 캐릭터의 정면 방향벡터와 내적값을 구한다.

 

그 뒤, 이 값이 양의 부호라면 캐릭터 앞 음의 부호라면 캐릭터 뒤에 있다고 판단할 수 있다.

 

2. 시야각 판별

 

[시야각 판별]

이번엔 단순 앞뒤 판별이 아닌 시야각을 지정하여 이 시야각 안에 물체가 들어왔는지 판별해 보자. 시야각 판별의 구현 순서는 다음과 같다.

  1. 시야각의 절반$(\frac{\theta}{2})$에 해당하는 $\cos$값을 계산한다.
  2. 시선 벡터와 목표로 향하는 벡터를 정규화시켜 내적값을 구한다.
  3. 1번의 값과 2번의 값을 비교한다.

[시야각 판별]

캐릭터의 정면 벡터를 $\vec f$, 타겟까지 향하는 벡터를 $\vec t$라고 했을 때, 이 두 벡터가 단위 벡터라면 두 벡터의 내적 결과는 결국 $\vec f$와 $\vec t$가 이루는 사잇각의 $\cos$ 값(위 사진에서 d값)이 계산된다.

$\cos$함수는 각$\theta$가 커질수록 작은 값을 반환하고, 반대로 각이 커질수록 큰 값을 반환한다.

  • $\cos(0^\circ) = 1$
  • $\cos(90^\circ) = 0$
  • $\cos(180^\circ) = -1$

 

따라서, 시야각의 절반에 해당되는 $\cos$값을 $v$라 한다면,

  • 내적값보다 시야각 값이 더 크다면$(d > v)$ 내적한 두 벡터가 이루는 각이 더 작음.(시야 안에 존재)
  • 내적값보다 시야각 값이 더 작다면$(d < v)$ 내적한 두 벡터가 이루는 각이 더 큼.(시야 밖에 존재)
using System;
using UnityEngine;

public class FOVDetect : MonoBehaviour
{
    [SerializeField] private float angle = 60f;
    [SerializeField] private RotateAround target;

    private Vector3 _playerForward;
    private float _halfAngle;
    private void Start()
    {
        _playerForward = transform.forward;
        
        // 시야각 절반에 해당하는 값 미리 저장
        _halfAngle = angle * 0.5f;
    }

    private void Update()
    {
        var dir = (target.transform.position - transform.position).normalized;
        
        var dot = Vector3.Dot(_playerForward, dir);
        var fov = Mathf.Cos(_halfAngle * Mathf.Deg2Rad);
        
        DrawView();

        // 시야각 안에 목표 없음
        if (dot < fov)
        {
            target.ChangeMaterials(1);
            return;
        }
        // 시야각 안에 목표 있음
        target.ChangeMaterials(0);
    }

    private void DrawView()
    {
        var pos = transform.position;
        var left = Quaternion.AngleAxis(-_halfAngle, Vector3.up) * _playerForward;
        var right = Quaternion.AngleAxis(_halfAngle, Vector3.up) * _playerForward;
        
        Debug.DrawLine(pos, pos + left * 30f, Color.red);
        Debug.DrawLine(pos, pos + right * 30f, Color.red);
    }
}

[시야각 판별 구현]

유니티로 구현 시, Mathf.Cos() 함수는 파라미터로 라디안 값을 받기 때문에, 기존 시야각도의 절반에 Mathf.Deg2Rad를 곱해 라디안 각도로 변환해 주면 된다.

728x90

'게임수학' 카테고리의 다른 글

[게임수학] 6-2. 투영 벡터  (0) 2025.09.12
[게임수학] 5.아핀공간  (1) 2025.09.08
[게임수학] 4. 행렬  (1) 2025.09.05
[게임수학] 3. 삼각함수  (2) 2025.08.31
[게임수학] 2. 벡터(Vector)  (2) 2025.08.27
'게임수학' 카테고리의 다른 글
  • [게임수학] 6-2. 투영 벡터
  • [게임수학] 5.아핀공간
  • [게임수학] 4. 행렬
  • [게임수학] 3. 삼각함수
브라더스톤
브라더스톤
유티니, C#과 관련한 여러 정보를 끄적여둔 블로그입니다. Email : dkavmdk98@gmail.com
  • 브라더스톤
    젊은 프로그래머의 슬픔
    브라더스톤
  • 전체
    오늘
    어제
    • 개발 노트 (34) N
      • Unity,C# (27)
        • Unity 정보 (5)
        • 알고리즘 (11)
        • 자료구조 (2)
        • 절차적생성(PCG) (9)
      • 게임수학 (7) N
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    게임수학
    C#
    최단경로찾기
    알고리즘
    앞뒤판별
    PerlinNoise
    정렬알고리즘
    절차적지형생성
    절차적던전생성
    BSP
    이진공간분할
    경사로이동
    기저벡터
    ProjectOnPlane
    이동변환
    아핀공간
    시야판별
    자료구조
    pcg
    unity
  • 최근 댓글

  • 최근 글

  • 250x250
  • hELLO· Designed By정상우.v4.10.3
브라더스톤
[게임수학] 6-1. 내적(Dot product)
상단으로

티스토리툴바