개발일지/소프트 렌더러

소프트웨어 렌더러 만들기 - 3 (스크린 좌표계, 출력 영역 조정)

hwi.middle 2023. 10. 30. 22:03

여기까지의 작업내용: https://github.com/hwi-middle/HimchanSoftwareRenderer/tree/9319547dcbf3a0961f261d0d2c9e165bb4f8e04d

 

GitHub - hwi-middle/HimchanSoftwareRenderer: C++로 구현한 소프트웨어 렌더러입니다.

C++로 구현한 소프트웨어 렌더러입니다. Contribute to hwi-middle/HimchanSoftwareRenderer development by creating an account on GitHub.

github.com

스크린 좌표계 ↔ 데카르트 좌표계 변환

struct ScreenPoint
{
    FORCEINLINE static Vector2 ScreenToCartesian(const Vector2& InPoint, const uint32 Width, const uint32 Height)
    {
        return Vector2(InPoint.X - Width * 0.5f + 0.5f, -(InPoint.Y + 0.5f) + Height * 0.5f);
    }

    FORCEINLINE static Vector2 CartesianToScreen(const Vector2& InPoint, const uint32 Width, const uint32 Height)
    {
        return Vector2(InPoint.X + Width * 0.5f, -InPoint.Y + Height * 0.5f);
    }
};

이제 스크린 좌표계와 데카르트 좌표계 사이의 변환을 구현했다. 매우 간단한 수식이라 덧붙일 설명은 없다. 픽셀을 찍을 때 편하게 데카르트 좌표계로 넘겨주면 알아서 스크린 좌표계로 찍기 위한 것이다.

문제 상황

지난 번에 구현한 Vector2 클래스를 이용해서 화면 중앙에 원을 그려보고자 했다(이득우의 게임수학 예제 3-2). 그런데 뭔가 이상했다. 분명히 원점을 기준으로 원을 찍었는데 X축과 Y축의 위치부터 원의 위치까지 모든게 원점에서 어긋나있었다.

상하좌우로 쭈욱 늘려보면 분명 창의 크기에 맞게 그려준 영역들이 창을 벗어나고 있음을 확인할 수 있었다.

나로서는 대단히 의문스러운 일이었다. 640 * 480 해상도로 창을 만들고, 640 * 480 만큼 라인을 그렸는데 왜 영역을 벗어나는 것인지 알 수 없었다.

 

콘솔 출력을 이용해서 실제 창의 크기가 런타임에 달라진 것은 아닌지, DPI와 관련된 문제는 아닌지(스택오버플로에서는 대부분 DPI를 지적했다) 등 온갖 방법을 시도해봤지만 끝내 답을 찾지 못했다.

결국 페이스북 그룹 <생활코딩>에 질문을 올렸는데, 금새 답변이 달렸다. 요지는 창의 크기에 제목표시줄 등의 영역이 포함된다는 것.

...어? 그러네...

댓글에 적어두었듯이, GetWindowRect로 창의 크기를 확인해보면 640 * 480이지만, GetClientRect로 제목표시줄 등의 영역이 제외된 크기는 또 달랐다.

 

결국에는 ClientRect를 기준으로 크기를 잡으면 알아서 WindowRect의 크기를 잡아주는 기능이 필요하게 된 것이다. 다행히 실제로 이런 기능이 제공되고 있었다.

RECT windowRect = { 0, 0, Width, Height };
AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE);

const uint16 ActualWindowWidth = windowRect.right - windowRect.left;
const uint16 ActualWindowHeight = windowRect.bottom - windowRect.top;

이렇게 작성하면 된다. Width와 Height는 전역변수로 관리되고, WinMain에서 윈도우를 등록할 때 AdjustWindowRect 함수를 통해 창의 크기를 변환할 수 있다.

 

Width와 Height는 그려질 영역(ClientRect)의 크기이고 ActualWindowWidth와 ActualWindowHeight는 전체 창(WindowRect)의 크기이다.

그리하여 만들어진 화면. 원을 우측 상단으로 옮긴 것은 의도한 것이다. 다만, 아직 사용자로부터 입력을 받아오는 부분이 구현되어있지 않아서 움직이지는 않는다.

 

다소 아쉬운 부분인데, 일단 렌더러 부분을 조금 더 깔끔하게 떼어낸 다음에 입력을 처리해볼 수 있을 것 같다.

마무리

생각보다 별 일 아닌 것에 시간을 많이 날렸는데, 이런 것도 개인 프로젝트의 매력인 것 같다. 물론 해결이 안되는 동안에는 답답해 미치겠지만 이 과정 자체가 어떻게보면 즐거운 것도 같다.