본문 바로가기

DirectX11

DirectX11 - Tutorial 4 (창 크기, 클라이언트 크기)

그래픽 작업을 할 때는 그리는 영역의 정확한 크기를 아는 것이 중요합니다. 지난 수업에서 우리는 창을 만들고 크기를 500 x 400으로 설정했습니다. 그러나 Direct3D가 그리는 영역은 해당 창의 경우 500 x 400이 아닙니다 .

- 그리기 영역의 실제 크기를 알아보고, 이를 보다 정확하게 설정하는 기능을 알아보자

 

CreateWindowEx()를 호출했을 때, 우리는 500과 400을 사용하여 윈도우의 크기를 설정했습니다. 그러나 이것은 클라이언트 의 크기와 다릅니다 . 클라이언트 영역은 테두리를 포함하지 않는 윈도우의 부분입니다.

여기에서 볼 수 있듯이, 창 크기는 테두리의 가장자리에서 확장되는 반면, 클라이언트 크기는 테두리의 내부로 확장됩니다. 렌더링할 때, 우리는 창의 클라이언트 영역에만 그림을 그릴 것입니다. 따라서 정확한 크기를 아는 것이 중요합니다.

 

이것이 정확히 왜 중요한가요? Direct3D를 사용하여 그릴 때 생성할 이미지의 크기를 지정하라는 메시지가 표시됩니다. 창의 클라이언트 영역이 이 이미지와 크기가 다르면 클라이언트 영역에 맞게 늘어나거나 줄어듭니다.

 

보시다시피 오른쪽 스크린샷에는 몇 가지 명백한 왜곡이 있습니다. 이는 클라이언트 영역에 맞게 이미지를 축소했을 때 생성되었습니다.

 

AdjustWindowRect() 함수

창 크기를 설정한 다음 클라이언트 크기를 결정하는 것보다 클라이언트 크기를 미리 결정한 다음 적절한 창 크기를 계산하는 것이 이상적입니다. 이를 위해 창을 만들기 전에 AdjustWindowRect() 함수를 사용합니다.

이것은 정말 간단한 기능입니다. 이 기능이 하는 일은 클라이언트 영역의 원하는 크기와 위치를 취하고, 그 클라이언트 크기를 만드는 데 필요한 창 위치와 크기를 계산하는 것입니다.

해당 함수의 프로토타입은 다음과 같습니다.

 

BOOL AdjustWindowRect(LPRECT lpRect,
                      DWORD dwStyle,
                      BOOL bMenu);

 

첫 번째 매개변수는 RECT 구조체에 대한 포인터입니다. 가리키는 RECT에는 원하는 클라이언트 영역의 좌표가 들어 있습니다. 함수가 호출되면 RECT가 수정되어 대신 창 영역의 좌표가 들어 있습니다.

두 번째 매개변수는 창 스타일입니다. 이 함수는 이 정보를 사용하여 창 테두리의 크기를 결정합니다.

세 번째 매개변수는 BOOL 값으로, 함수를 통해 메뉴를 사용하는지 여부를 알려줍니다. 메뉴는 기술적으로 클라이언트 영역의 일부가 아니므로 고려해야 합니다.

이 함수는 실제 코드에서 어떻게 보일까요? 살펴보겠습니다. 다음은 CreateWindowEx()에 대한 호출을 수정한 것입니다.

 

RECT wr = {0, 0, 500, 400}; // 크기는 설정하지만, 위치는 설정하지 않음
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE); // 크기 조정

// 창을 생성하고 그 결과를 핸들로 사용
hWnd = CreateWindowEx(NULL,
                      L"WindowClass1",
                      L"Our First Windowed Program",
                      WS_OVERLAPPEDWINDOW,
                      300, // 창의 x 위치
                      300, // 창의 y 위치
                      wr.right - wr.left,     // 창 너비
                      wr.bottom - wr.top,     // 창 높이
                      NULL,
                      NULL,
                      hInstance,
                      NULL);

 

RECT wr = {0, 0, 500, 400};

간단한 문장입니다. 우리는 rect(저는 이것을 'wr'로 명명했습니다. 창 rect)를 만들고, 원하는 클라이언트 영역의 크기로 초기화합니다. 우리는 'left'와 'top' 값에 위치를 넣지 않습니다. 왜냐하면 우리가 하는 일에 그것들이 필요하지 않기 때문입니다.

AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);

RECT가 초기화된 후 AdjustWindowRect() 함수를 호출합니다. RECT 주소, 창 스타일, FALSE로 채워서 메뉴가 없음을 나타냅니다.

wr.right - wr.left,
wr.bottom - wr.top,

AdjustWindowRect()가 호출되면 창 너비는 오른쪽과 왼쪽의 차이가 되고, 높이는 아래쪽과 위쪽의 차이가 됩니다.

창의 너비와 높이에 대한 두 가지 표현식을 사용하면 창의 정확한 크기를 알 수 있습니다.

 

현재까지의 전체 코드이다!!

 

// 기본 Windows 헤더 파일 포함
#include <windows.h>
#include <windowsx.h>

// WindowProc 함수 프로토타입
LRESULT 콜백 WindowProc(HWND hWnd,
                         UINT message,
                         WPARAM wParam,
                         LPARAM lParam);

// 모든 Windows 프로그램의 진입점
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
    // 함수로 채워진 창의 핸들
    HWND hWnd;
    // 이 구조체는 창 클래스에 대한 정보를 보관합니다
    WNDCLASSEX wc;

    // 사용을 위해 창 클래스를 지웁니다
    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    // 필요한 정보로 구조체를 채웁니다
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.lpszClassName = L"WindowClass1";

    // 윈도우 클래스 등록
    RegisterClassEx(&wc);

    // 클라이언트 영역 RECT의 크기를 계산
    wr = {0, 0, 500, 400}; // 크기는 설정하지만 위치는 설정하지 않음
    AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE); // 크기 조정


    // 창 생성 및 결과 핸들로 사용
    hWnd = CreateWindowEx(NULL,
                          L"WindowClass1", // 창 클래스 이름
                          L"Our First Windowed Program", // 창 제목
                          WS_OVERLAPPEDWINDOW, // 창 스타일
                          300, // 창의 x 위치
                          300, // 창의 y 위치
                          wr.right - wr.left,     // 창 너비
                          wr.bottom - wr.top,     // 창 높이
                          NULL, // 부모 창이 없음, NULL
                          NULL, // 메뉴를 사용하지 않음, NULL
                          hInstance, // 애플리케이션 핸들
                          NULL); // 여러 창과 함께 사용, NULL

    // 화면에 창 표시
    ShowWindow(hWnd, nCmdShow);

    // 메인 루프로 이동:

    // 이 구조체는 Windows 이벤트 메시지를 보관합니다
    MSG msg;

    // 큐의 다음 메시지를 기다리고 결과를 'msg'에 저장합니다.
    while(GetMessage(&msg, NULL, 0, 0))
    {
        // 키 입력 메시지를 올바른 형식으로 변환합니다
        .TranslateMessage(&msg);

        // 메시지를 WindowProc 함수로 보냅니다
        .DispatchMessage(&msg);
    }

    // WM_QUIT 메시지의 이 부분을 Windows로 반환합니다
    .return msg.wParam;
}

// 이것은 프로그램의 주요 메시지 핸들러입니다
.LRESULT 콜백 WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    // 정렬하여 주어진 메시지에 대해 실행할 코드를 찾습니다
    .switch(message)
    {
        // 이 메시지는 창이 닫힐 때 읽힙니다.case
        WM_DESTROY:
            {
                // 응용 프로그램을 완전히 닫습니다
                .PostQuitMessage(0);
                return 0;
            } break;
    }

    // switch 문에서
    반환하지 않은 모든 메시지를 처리합니다.DefWindowProc (hWnd, message, wParam, lParam);
}

 

 

어렵긴하다...;; 그래도 하나하나씩 알아가는 재미가 있는 듯 하다..ㅋㅋ

 

728x90