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
반응형
'수학 > 이득우의 게임 수학 - 실습 예제' 카테고리의 다른 글
| 무게중심좌표를 활용한 와이어프레임의 삼각형 색칠 (0) | 2025.12.03 |
|---|---|
| 와이어프레임 렌더링의 구현 (0) | 2025.11.26 |
| 벡터의 내적을 활용한 조명 모델의 구현 (0) | 2025.11.19 |
| 벡터의 내적을 활용한 목표물 감지 (0) | 2025.11.12 |
| 라인 클리핑 알고리즘의 구현 (0) | 2025.10.29 |