수학/이득우의 게임 수학 - 실습 예제

벡터의 내적을 활용한 벡터의 투영 구현

monstro 2025. 11. 19. 15:48
728x90
반응형

- 개요

벡터 투영 과정의 시각화

 

벡터의 내적어떤 벡터를 다른 벡터에 직교 투영하는 용도로도 사용할 수 있다

위의 이미지처럼 카메라가 만드는 공간을 분석하는 경우 다음과 같은 변수를 사용한다

  • Distance : 카메라부터 물체 사이의 거리
  • Depth : 카메라부터 물체 이미지 속의 각 픽셀까지의 거리
  • 이때 Depth 값을 구하기 위해 내적을 활용한 투영 벡터를 계산하는 공식을 사용

 

투영 벡터 v`를 계산하는 방법은 상황에 따라 다음과 같이 분류할 수 있다

  • 1) 투영 벡터 v`크기를 알고 있는 경우 : v` = |v`| · v^
    • v^ = v 벡터의 단위벡터
  • 2) 투영 벡터 v`크기를 모르는 경우 : v` = {(u · v) / (v · v)} · v
  • 3) 2)의 식에서 v의 크기가 1인 경우 : v` = (u · v) · v

 

- 예제

...

// 게임 로직과 렌더링 로직이 공유하는 변수
// 카메라 피사체의 위치 / 피사체를 투영할 선의 시작점과 끝점 
Vector2 point(0.f, 250.f);
Vector2 lineStart(-400.f, 0.f);
Vector2 lineEnd(400.f, 0.f);

// 매 프레임마다 호출하는 함수
void SoftRenderer::Update2D(float InDeltaSeconds)
{
    // 사용자로부터 입력을 받기 위한 레퍼런스, 가져오되 사용하지는 않음
    auto& g = Get2DGameEngine();
    const InputManager& input = g.GetInputManager();

    // 게임 로직의 로컬 변수
    // 피사체를 투영할 선을 유지하는 최대 시간 / 경과 시간 / 피사체의 각도 / 피사체의 왕복속도 / 피사체가 원점으로부터 떨어진 거리 
    // 랜덤 시드 / 랜덤 시드 기반의 랜덤 엔진 / uniform_real_distribution 클래스를 사용하여 균등한 확률로 난수를 생성
    static float duration = 6.f;
    static float elapsedTime = 0.f;
    static float currentDegree = 0.f;
    static float rotateSpeed = 180.f;
    static float distance = 250.f;
    static std::random_device rd;
    static std::mt19937 mt(rd());
    static std::uniform_real_distribution<float> randomY(-200.f, 200.f);

    // 경과 시간동안 피사체를 투영할 선을 유지
    // 경과 시간을 넘으면 투영할 선을 랜덤한 값으로 초기화
    elapsedTime = Math::Clamp(elapsedTime + InDeltaSeconds, 0.f, duration);
    if (elapsedTime == duration)
    {
        lineStart = Vector2(-400.f, randomY(mt));
        lineEnd = Vector2(400.f, randomY(mt));
        elapsedTime = 0.f;
    }

    // 시간에 따라 각속도 180°로 회전하도록 피사체의 위치를 계산
    currentDegree = Math::FMod(currentDegree + rotateSpeed * InDeltaSeconds, 360.f);
    float sin = 0.f, cos = 0.f;
    Math::GetSinCos(sin, cos, currentDegree);
    point = Vector2(cos, sin) * distance;
}

...

 

게임 로직을 담당하는 Update2D 함수의 로직은 위와 같다

매 프레임마다 피사체와 피사체의 투영할 선을 유지하면서

피사체를 원점을 기준으로 각속도 180°의 속도왕복하도록 설정한다

 

...

// 화면에 렌더링하는 함수
void SoftRenderer::Render2D()
{
   // 화면에 렌더하기 위해 가져올 레퍼런스
    auto& r = GetRenderer();
    const auto& g = Get2DGameEngine();

    // 피사체를 표현하는 점의 반지름과 점을 구성하는 좌표를 저장하는 로컬 변수 
    static float radius = 5.f;
    static std::vector<Vector2> circle;

    // 좌표를 저장하는 컨테이너를 채우는 로직
    if (circle.empty())
    {
        for (float x = -radius; x <= radius; ++x)
        {
            for (float y = -radius; y <= radius; ++y)
            {
                Vector2 target(x, y);
                float sizeSquared = target.SizeSquared();
                float rr = radius * radius;
                if (sizeSquared < rr)
                {
                    circle.push_back(target);
                }
            }
        }
    }

    // 피사체의 점들을 붉은색으로 칠하는 로직
    for (auto const& v : circle)
    {
        r.DrawPoint(v + point, LinearColor::Red);
    }

    // 피사체를 투영할 라인을 그리는 로직
    r.DrawLine(lineStart, lineEnd, LinearColor::Black);
    r.DrawLine(lineStart, point, LinearColor::Red);

    // 투영할 선의 벡터를 정규화하여 unitV 벡터로 저장 
    Vector2 unitV = (lineEnd - lineStart).GetNormalize();
    // 투영할 선의 시작점부터 피사체까지의 거리를 u 벡터로 저장
    Vector2 u = point - lineStart;

    // unitV를 곱하여 투영된 방향으로 투영 벡터 projV 생성
    // 투영된 피사체의 위치 + 피사체와 투영된 피사체 사이의 거리를 생성
    Vector2 projV = unitV * (u.Dot(unitV));
    Vector2 projectedPoint = lineStart + projV;
    float distance = (projectedPoint - point).Size();

    // 투영된 피사체의 점들을 파란색으로 칠하는 로직
    for (auto const& v : circle)
    {
        r.DrawPoint(v + projectedPoint, LinearColor::Blue);
    }

    // 투영 라인 그리기
    r.DrawLine(projectedPoint, point, LinearColor::Gray);

    // 관련 데이터 화면 출력
    r.PushStatisticText(std::string("Point : ") + point.ToString());
    r.PushStatisticText(std::string("Projected Point : ") + projectedPoint.ToString());
    r.PushStatisticText(std::string("Distance : ") + std::to_string(distance));
}

...

 

결과물을 화면에 표현하는 Render2D 함수의 로직은 위와 같다

피사체투영된 피사체 2개의 원을 사용하여 투영 벡터를 표현하는데,

이때 (투영할 선의 벡터단위 벡터) · (투영할 선의 시작점부터 피사체까지의 벡터)의 값을 사용하여 투영한다

 

- 최종 실행 결과

728x90
반응형