게임 개발자를 향해

[OpenGL 기초] 2D 환경 구축, 사각형 출력 및 이동 본문

그래픽스/OpenGL

[OpenGL 기초] 2D 환경 구축, 사각형 출력 및 이동

뿌단이 2023. 2. 16. 16:50

안녕하세요 뿌단이입니다.

대학에서 공부했던 그래픽스로 만든 작품을 리뷰해 보려 합니다!

옛 기억이 새록새록하네요 ㅎㅎ

게임개발자가 되기위해서 공부했던 자료로 다시한번 상기시키며 포스팅을 하게 되었습니다!

 

 

일단 해당 프로젝트는 MFCOpenGL을 사용하여 제작되었습니다!

환경 설정 이후 Viewport 내부에 그래픽 표현을 위해 View 클래스에서 사용하는 함수는 아래와 같습니다.

 

<OpenGL 환경 설계 함수>

1. View 클래스의생성자(Constructor)함수 : 변수 초기화 부분

2. "OnCreate()" 메세지 핸들러함수 : 윈도우 생성 함수

3. "InitializeOpenGL()"함수 : MFC에 OpenGL 환경으로 초기화하는 함수

4. "SetupPixelFormat()" 함수 : OpenGL 픽셀 포맷 함수

5. "OnSize()" 메세지 핸들러함수 : 윈도우창 비율 조정

6. "OnEraseBkgnd()" 메세지 핸들러함수 : 화면의 깜빡거림 문제 해결을 위해 사용한 핸들러 함수

7. "OnDestroy()" 메세지 핸들러함수 : 윈도우가 파괴되기 전 처리해야할 작업 수행

 

<실질적인 그래픽 표현 함수>

1. "OnDrow()" 메세지 핸들러 함수 : Viewport를 초기화 해주는 함수

2. "RenderScene()" 생성 함수 : Viewport에 그래픽을 그리는 코드를 수행하는 함수(OnDrow()함수에서 실행하는 함수)

3. "OnTimer()" 메세지 핸들러함수 : 일정시간마다 반복되는 함수

 

 

 

< 함수 부연 설명 >


 

1. OnCreate()함수에서 SetTimer()함수를 사용하여 OnTimer()함수를 호출하며 반복 주기를 설정해줍니다.

1초마다 호출한다면 1000을 매개변수값으로 주면 되고 저는 초당 200프레임으로 5를 넣었습니다.

(TMI : 제 모니터 주사율은 75Hz라서 사실상 200Hz는 표현 못하네요..ㅠㅜ)

(1초가 1000이니까 1000 / 75를 하면 13.33..이네요 제 컴퓨터 기준으로 효율을 따진다면 13이나 14를 넣는게 맞겠죠?)

int COpenGLEnvironmentTestView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  여기에 특수화된 작성 코드를 추가합니다.
	InitializeOpenGL();
	SetTimer(0, 5.0f, NULL);
	return 0;
}

 

2. OnTimer()함수에서 InvalidateRect()함수를 실행하여 OnPaint()함수를 호출시켜 OnDrow()함수를 호출하게 만듭니다.

void COpenGLEnvironmentTestView::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
	InvalidateRect(NULL, FALSE);
	CView::OnTimer(nIDEvent);
}

 

3. OnDrow()함수에서는 버퍼를 초기화하고, 실질적으로 해당 좌표값을 이용해 그래픽을 그려주는 함수인 RenderScene()함수를 호출하고, OpenGL렌더링 파이프라인을 열어 실제로 Viewport 화면상에 그립니다.

더블버퍼링 사용을 위해 SwapBuffers 함수도 추가해줍니다.

void CMy20029999_P1View::OnDraw(CDC* pDC)
{
     CMy20029999_P1Doc* pDoc = GetDocument();
     ASSERT_VALID(pDoc);
     if (!pDoc)
          return;

     // TODO: 여기에 원시 데이터에 대한 그리기 코드를 추가합니다.
     ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );   // 버퍼를 초기화 해준다.
     
     RenderScene();   // 그래픽을 그리는 코드를 수행
     ::glFinish();   // OpenGL렌더링 파이프라인을 열어 실제로 화면상에 그린다.

     // 만약 더블 버퍼링이 사용된다면 뒤 버퍼의 내용을 앞 버퍼와 바꾼다.
     ::SwapBuffers( m_pDC->GetSafeHdc() );
}

 

4. 실질적인 그리기 코드를 수행할 함수인 RenderScene()함수를 생성했습니다.

void COpenGLEnvironmentTestView::RenderScene()
{
	// TODO: 여기에 구현 코드 추가.
}

 

 

위 사진은 코드 실행화면 Viewport 입니다.

위 사진처럼 기본적인 환경 설정은 끝났습니다!

이제 간단하게 사각형을 그려 Viewport 창 모서리에 튕기는 애니메이션을 만들어 보겠습니다.

 

일단 컴파일 시 OnCreate()가 실행되고, OnCreate()에 작성한 SetTimer()가 실행되어 OnTimer()에 작성한 코드를 초당 200번 반복하게 합니다.

 

OnTimer()에는 InvalidateRect()가 실행되어 OnDrow()를 호출합니다.

 

그럼 결과적으로 초당 200번 주기마다 OnDrow()를 호출하여 화면을 초기화가 되는 겁니다.

 

이때, OnDrow()는 초기화할 때 마다 실질적으로 그리는 함수들을 작성할 함수인 RenderScene()를 호출합니다.

 

저희는 "사각형을 그리고 사각형이 움직이게 만드는 것"이 목표입니다. 그러므로 OnTimer()에서 사각형 데이터의 좌표값을 반복적으로 변경하고, OnDrow() -> RenderScene()을 이용하여 바뀐 좌표값으로 사각형의 위치를 초기화해줍니다.

 

결론적으로 저희가 코딩해야할 부분은 RenderScene()함수OnTimer()함수입니다.

 

 

먼저 RenderScene() 함수를 아래와 같이 코딩합니다.

void COpenGLEnvironmentTestView::RenderScene()
{
	// TODO: 여기에 구현 코드 추가.
	glColor3f(1.0f, 0.0f, 0.0f);
	glRectf(m_fPosX, m_fPosY, m_fPosX + m_nSizeRect, m_fPosY + m_nSizeRect);
}

위 함수의 기능은 아래와 같습니다.


glRectf(PosX, PosY, SizeX, SizeY) :  해당 좌표로 사각형을 그립니다.

glColor3f(R, G, B) : 그리고자 하는 그래픽의 색깔 지정( glRectf() 위에 선언 해주어야 합니다.)

매개변수는 Red, Green, Blue 이며 빛의 3원색입니다.

만약 매개변수로 1, 0, 0을 준다면 빨간색이 나오고, 1, 1, 0을 준다면 빨강초록이 섞인 노란색이 나옵니다.


 

glRectf()는 그래프의 점의 좌표로 나타내게 됩니다.

 

위 코드는 X(가로), Y(세로) 좌표를 기준으로 SizeX, SizeY까지 픽셀을 glColor3f() 색깔로 나타내는 코드입니다. 

(제가 사용한 OpenGL의 변수 자료형은 GLFloat입니다. View 클래스에 자료형의 변수를 위 매개변수의 이름으로 선언했고, 생성자 함수에 초기화 하였습니다.)

 

 

위 코드만 추가하고 실행해보겠습니다.

 

 

 

뷰포트에 빨간 사각형이 생겼습니다.

해당 위 설명과정을 이어 OnTimer()함수에 변수값을 주기적으로 변경하고 초기화해보겠습니다.

 

void COpenGLEnvironmentTestView::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
	if (m_fPosX > m_fWinWidth - m_nSizeRect || m_fPosX < 0)
		m_fStepX = -m_fStepX;

	if (m_fPosY > m_fWinHeight - m_nSizeRect || m_fPosY < 0)
		m_fStepY = -m_fStepY;

	if (m_fPosX > m_fWinWidth - m_nSizeRect)
		m_fPosX = m_fWinWidth - m_nSizeRect - 1;

	if (m_fPosY > m_fWinHeight - m_nSizeRect)
		m_fPosY = m_fWinHeight - m_nSizeRect - 1;

	m_fPosX += m_fStepX;
	m_fPosY += m_fStepY;

	InvalidateRect(NULL, FALSE);
	CView::OnTimer(nIDEvent);
}

 

일단 m_fStepX, m_fStepY는 사각형이 Timer 주기마다 이동하는 거리입니다.

주기마다 위 변수를 사각형 좌표에 더해주고 화면을 그립니다.

해당 작업을 지정한 주기마다 반복을 하게 뙴으로써 사각형이 움직이는것 처럼 보이게 됩니다.

 

m_fWinWidthm_fWinHeight는 윈도우 창의 폭(Width)높이(Height)를 나타내는 변수입니다.


 

 

 위 코드를 설명하자면 일단, 사각형의 기준 좌표인 (PosX, PosY)가 주기적으로 StepX, StepY를 더해지고 있습니다.

 

그런데 Window Viewport 모서리에 닿게 된다면 StepY를 음수로 변경을 합니다.

 

그러면 PosX는 동일하게 주기적으로 StepX만큼 더해지고, PosY는 음수로 변경하였기 때문에 주기적으로 StepY를 빼주게 됩니다.

 

결과적으론 사각형이 Viewport에 닿으면 물리적으로 모서리에서 튕겨나가는것 처럼 보이게 됩니다.

 


 

 

 ↓    실행 결과 영상입니다!      

https://www.youtube.com/watch?v=QDWXIkIiQlY

 

 

내 인생 목표를 향해!

다음 포스팅에서 뵙겠습니다!

읽어주셔서 감사합니다!

 

 

 

 

'그래픽스 > OpenGL' 카테고리의 다른 글

[OpenGL] OpenGL을 이용한 "2D Stacking game"  (0) 2023.03.12