hyrule 2022. 5. 25. 19:44

http://hyrule.tistory.com/111 

 

*** 공부 방법 ***

1. 코딩을 해야 하는 부분은 첫 부분에 변수나 함수, 메소드에 대한 선언이 코드블럭으로 표시되어 있다. //ex) MakeFunction(); 2. 코드블럭 하단에는 해당 선언에 대한 구현 로직이 작성되어 있다. 처

hyrule.tistory.com

 


[사전 지식]

- 여태까지 뒤에 자국이 남았던 이유는 한 장 도화지를 지우지 않고 계속 덮어서 그렸기 때문

- 또한 계속 깜빡거렸던 이유도 오브젝트 하나를 그릴 때마다 화면이 갱신되었기 때문이다.

- 이 현상을 해결하기 위한 것이 더블버퍼링이다.


< 구동 방식 >

1. 도화지를 두개 만든다.
2. 한 장은 주표면용(모니터에 표시되는 부분) -> 기존의 hDC
3. 다른 한 장은 백버퍼용 -> 이걸 새로 만들 것임
4. 주표면은 그냥 냅두고, 보이지 않는 백버퍼 위에 흰색 사각형을 덮어 모두 지워버린 뒤 그림을 모두 그린다.

CF) 나중에 풍경화면을 입힐 경우 흰색 사각형이 아니라 풍경화면으로 덮어버리면 된다.

5. 그림 그리는게 모두 완료되었으면 해당 그림을 복사해서 주표면의 기존 화면을 덮어버린다.

6. 화면을 주표면에 표시하고 나서 다시 4번부터 반복한다.

 

 


m_hBackBmp = CreateCompatibleBitmap(m_hDC, m_RS.Width, m_RS.Height);

- 텍스처의 경우 기존의 이미지를 로드하는 방식으로 이미지를 생성했었다.

- 하지만 이번에는 로드할 이미지가 없으므로, 직접 하나 생성을 해주어야 된다.

- HDC와 가로 세로 길이를 인자로 주면 HBITMAP 파일 을 만들어 주는 함수이다.


<실제 구현>

class CGameManager


- 백버퍼용 DC를 하나 생성한다. 텍스처용 DC를 만드는 과정과 같다.

더보기
//GameManager.h

private:
	HDC			m_hBackDC;
	HBITMAP		m_hBackBmp;
	HBITMAP		m_hBackPrevBmp;

- 이 DC에 현재 창의 해상도(m_hDC)와 같은 해상도를 전달해주기 위해 GameInfo에 Resolution 구조체를 만들어 해상도를 저장한다.

-- 이 해상도 구조체를 CGameManager에서 변수로 들고 있는다.

-- 다른 클래스에서 이 구조체를 리턴받아서 사용할 수 있게 메소드를 만들어 준다.

-- CGameManager를 초기화 할 때, 여기에 창의 사이즈를 집어넣어 주고, 이후 창을 생성할 때도 이 구조체를 사용한다.

더보기
//GameInfo.h

struct Resolution
{
	int	Width;
	int	Height;
};

 

//GameManager.h

private:
	Resolution	m_RS;

- 초기화 과정에서 백버퍼 생성 과정을 진행한다.

- 사전 지식에서의 GetCompatibleBitmap() 함수를 LoadImage() 함수로 사용한다는 것 외에는 텍스처 로딩 과정과 동일하다.

- 소멸 과정은 완전 동일하다.

 

- cf)

ReleaseDC: 현재 화면에 출력되고 있는 DC를 제거
DeleteDC: 화면에 출력되지 않은 메모리상의 DC를 제거

더보기
//GameManager.cpp

CGameManager::~CGameManager()
{
	//...다른코드...//

    SelectObject(m_hBackDC, m_hBackPrevBmp);
    DeleteObject(m_hBackBmp);
    DeleteDC(m_hBackDC);

	ReleaseDC(m_hDC);
}


bool CGameManager::Init(HINSTANCE hInst)
{

	//...기존코드...//

    // 백버퍼를 만든다.
    m_hBackDC = CreateCompatibleDC(m_hDC);

    // 윈도우 창의 크기와 동일한 크기의 백버퍼용 비트맵을 만들어준다.
    m_hBackBmp = CreateCompatibleBitmap(m_hDC, m_RS.Width, m_RS.Height);

    m_hBackPrevBmp = (HBITMAP)SelectObject(m_hBackDC, m_hBackBmp);


	return true;
}

- 여기까지 했다면, 사실상 끝이다.

 

- 기존에 짜 놓은 Render()구조가 있으므로 거기서 몇 줄만 추가 및 수정해주면 끝이기 때문이다.

 

-- 우선 백버퍼에 그림을 그리기 전에 m_BackhDC를 흰 사각형으로 덮어 내용을 깔끔히 지워준다.

 

-- 기존에는 Render() 메소드에서선 모든 출력을 m_hDC(주버퍼)에다 했었다.

--- 이걸 m_BackhDC로 바꿔 주면 모든 출력을 m_BackhDC에 그려줄 것이다. 

 

-- 그리고 백버퍼에 Render() 과정이 완료되었을 때, 백버퍼를 텍스처를 그려내듯이 BitBlt() 함수를 통해 m_hDC에 덮어버리면 된다.

더보기
void CGameManager::Render(float DeltaTime)
{
	//백버퍼를 깨끗이 지워준다.
    Rectangle(m_hBackDC, -1, -1, m_RS.Width + 1, m_RS.Height + 1);

	//그림을 그려낼 곳을 주 버퍼에서 백 버퍼로 바꿔준다.
    CSceneManager::GetInst()->Render(m_hBackDC, DeltaTime);

    // 위에서 백버퍼에 모든 오브젝트가 출력이 되었다.
    // 마지막으로 백버퍼를 주표면 버퍼에 그려낸다.
    // BitBlt : 이미지를 원하는 DC에 출력해주는 함수이다.
    // 1번인자 : 이미지를 출력해줄 DC
    // 2번인자 : 해당 DC에서의 x좌표
    // 3번인자 : 해당 DC에서의 y좌표
    // 4번인자 : 그려낼 이미지의 가로크기
    // 5번인자 : 그려낼 이미지의 세로크기
    // 6번인자 : 이미지를 출력할 DC
    // 7번인자 : 출력할 DC상에서의 시작 x위치
    // 8번인자 : 출력할 DC상에서의 시작 y위치
    // 9번인자 : 그리는 방법 지정
    BitBlt(m_hDC, 0, 0, m_RS.Width, m_RS.Height, m_hBackDC, 0, 0, SRCCOPY);

}

- 이제 드디어 화면에 잔상이 남지 않는다.

GameFrameworkStepbyStep_35_DoubleBuffer.zip
2.27MB