2023.11.18 - [C\C++/OpenGL] - C/C++ OpenGl을 이용한 간단한 태양계 구축 프로젝트(2)
목차
OpenGL 재질효과
OpenGL에서 재질(Material)은 물체가 빛과 상호 작용하는 방식을 제어하는 데 사용되는 속성의 힙합이먀, 재질은 주변광, 확산광, 반사광 등을 포함한 여러 광 특성을 정의한다. 그리고 이러한 특성은 물체의 색상과 광택을 결정한다고 한다.
더 쉽게 말하자면 객체 표면의 재질에 따라 빛의 반사 효과가 달라진다. 조명과 객체의 재질을 설정하는 함수에 대해 배우고, 밝게 빛나는 구들(행성들)에 대한 프로그램을 작성함으로써 배워보자!
객체의 재질(Materials)
재질의 정의에 따라 빛의 객체에 반사될 때 객체 표면에 어떤 효과가 나타나는 지가 달라진다. 즉, 객체의 재질(Material)은 해당 객체가 어떻게 빛과 상호 작용하는지를 정의하는 속성의 모음이고, 재질은 물체의 색상, 광택, 반사, 투과 등을 결정하며 렌더링 된 결과물에 객체에 현실적인 외관을 부여하는 데 사용한다. 재질 속성은 주로 광원과 함께 작동하여 조명 효과를 시뮬레이션하고, 빛에 따라 물체의 표면이 어떻게 반응하는지를 결정하는데 다양한 재질 속성은 물체의 시각적인 특성을 정한다.
OpenGL에서는 5가지의 재질 성분을 가지고 물체의 재질을 표현한다.
현재 색상에 하나 이상의 재질 속성을 지정하는 것이 가능하다
- 주변광 (Ambient Light): 주변의 전체 광을 나타냅니다. 빛의 직접적인 출처가 아닌 주변 환경에서의 광을 모방한다.
- 확산광 (Diffuse Light): 물체가 광원에서 얼마나 확산된 광을 반사하는지를 나타냅니다. 이는 물체의 기본 색상을 결정한다.
- 반사광 (Specular Light): 광택이 나타나는 부분을 나타냅니다. 빛이 표면에서 반사되는 부분으로, 물체의 광택을 조절한다.
- 반질 지수 (Shininess): 반사광이 얼마나 집중되는지를 나타내는 지수입니다. 높은 값은 작고 집중된 광택을, 낮은 값은 넓게 퍼진 광택을 나타낸다.
- 투사광 (Emission): 물체에서 방출되는 빛의 색상과 강도를 정의합니다. 이 속성은 물체 자체에서 빛을 방출하는 것처럼 보이게 한.
추가적으로 투명도 (Transparency): 물체가 얼마나 투명한지를 결정합니다. 0.0은 완전 투명하고, 1.0은 정말 불투명한 것을 나타낸다.
재질(Material)과 광택(Shininess)은 서로 다른 개념이지만, 렌더링에서 함께 사용되어 물체의 외관을 결정한다. .
- 재질(Material): 재질은 물체의 기본적인 물리적 특성을 정의합니다. 색상, 반사율, 투명도 등이 포함이 되며, 재질은 빛이 표면과 상호 작용하는 방식을 나타내는 중요한 개념이다.
- 광택(Shininess): 광택은 표면에서 반사되는 빛이 얼마나 집중되는지를 나타내는 지수이고, 높은 광택 값은 작고 집중된 광택을 나타내고, 낮은 값은 넓게 분포된 광택을 나타낸다. 즉, 광택은 표면이 얼마나 매끄럽고 빛을 집중적으로 반사하는지를 결정한다.
OpenGL에서 사용되는 주요 재질 속성
- 주변광 (Ambient Light):
- 물체에 존재하는 주변 광의 양을 나타낸다.
- glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambientColor);와 같이 설정.
- 확산광 (Diffuse Light):
- 물체가 얼마나 확산된 광을 반사하는지를 나타낸다.
- glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuseColor);와 같이 설정.
- 반사광 (Specular Light):
- 물체가 얼마나 빛을 반사하는지를 나타냅니다. 이는 표면의 광택을 제어한다.
- glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specularColor);와 같이 설정.
- 반질 지수 (Shininess):
- 반사광이 얼마나 집중되는지를 나타냅니다. 높은 값을 가지면 작고 집중된 광택이 나타난다.
- glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, shininess);와 같이 설정.
함수들...
glColorMaterial(enum face, enum mode);
- face: 색상을 지정할 면을 나타내는 매개변수로, GL_FRONT, GL_BACK, GL_FRONT_AND_BACK 중 하나를 사용하고, 이는 색상을 지정할 때 어느 쪽 면에 적용할지를 결정한다.
- mode: 머티리얼 색상을 어떻게 지정할지를 나타내는 매개변수로, GL_EMISSION, GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR, GL_AMBIENT_AND_DIFFUSE 중 하나를 사용한다. 이는 어떤 머티리얼 색상 속성에 대한 자동 색상 지정을 수행할지 결정한다.
glMaterial 함수 시리즈
void glMaterialfv(GLenum face, GLenum pname, const GLfloat *params);
--------------------------------------------------------------------
GLfloat ambient[] = {0.2f, 0.2f, 0.2f, 1.0f};
glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
머티리얼의 속성을 특정 값으로 설정하는데, face는 어느 쪽 면에 속성을 적용할지를 결정하며, pname은 설정할 속성의 종류를 나타낸다. params에는 해당 속성의 값을 담고 있는 배열을 전달한다.
void glLightfv(GLenum light, GLenum pname, const GLfloat *params);
------------------------------------------------------------------
GLfloat position[] = {100.0f, 100.0f, 100.0f, 1.0f};
glLightfv(GL_LIGHT0, GL_POSITION, position);
광원의 속성을 설정하는데, light는 어느 광원에 속성을 적용할지를 결정하며, pname은 설정할 속성의 종류를 나타낸다. params에는 해당 속성의 값을 담고 있는 배열을 전달한다.
void glLightModelfv(GLenum pname, const GLfloat *params);
--------------------------------------------------------
GLfloat ambientModel[] = {0.2f, 0.2f, 0.2f, 1.0f};
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientModel);
광원 모델의 속성을 설정하며, pname은 설정할 속성의 종류를 나타내며, params에는 해당 속성의 값을 담고 있는 배열을 전달한.
pname에 들어가는 속성들의 종류
Material Parameter | Light Source Parameters | Lighting Model Parameters |
(Material) | (Light) | (LightModel) |
AMBIENT | AMBIENT | LIGHT_MODEL_AMBIENT |
DIFFUSE | DIFFUSE | LIGHT_MODEL_LOCAL_VIEWER |
AMBIENT_AND_DIFFUSE | SPECULAR | LIGHT_MODEL_TWO_SIDE |
SPECULAR | POSITION | LIGHT_MODEL_COLOR_CONTROL |
EMISSION | SPOT_DIRECTION | |
SHININESS | SPOT_EXPONENT | |
COLOR_INDEXES | SPOT_CUTOFF | |
CONSTANT_ATTENUATION | ||
LINEAR_ATTENUATION | ||
QUADRATIC_ATTENUATION | ||
Material Parameters (재질에 관련된 매개변수) | |
AMBIENT | 재질의 주변 광 반사 계수를 나타낸다. 즉, 주변 광원으로부터의 빛이 재질에 얼마나 반사되는지를 조절한다. |
DIFFUSE | 재질의 분산 광 반사 계수를 나타내며, 재질이 얼마나 밝게(분산되게) 보이는지를 결정한다. |
AMBIENT_AND_DIFFUSE | MBIENT와 DIFFUSE를 결합한 것으로, 주변 광 반사 및 분산 광 반사를 동시에 나타낸다. |
SPECULAR | 재질의 반사 광 반사 계수를 나타내며, 재질이 얼마나 광택나게 보이는지를 결정한다. |
EMISSION | 재질의 방사 광(자체 발광) 계수를 나타내며, 0 이상의 값에서는 재질이 스스로 빛을 내는 효과를 활성화한다. |
SHININESS | 반사광이 얼마나 경면인지를 나타내는 값으로, 높은 값일수록 반사광이 작은 지역에 집중되어 더 뾰족한 광택을 나타낸다. |
COLOR_INDEXES | 색상 인덱스 재질의 색상을 나타내는데 현재는 거의 사용되지 않는다고 한다. |
Light Source Parameters (광원에 관련된 매개변수) | |
AMBIENT | 광원의 주변 광 성분을 나타내는데 다시 말해, 광원 주변에 얼마나 빛이 있는지를 활성화하여 나타낸다. |
DIFFUSE | 광원의 분산 광 성분을 나타내며, 광원이 어느 정도 밝은지를 결정한다. |
SPECULAR | 광원의 반사 광 성분을 나타내며, 광원이 얼마나 광택나게 보이는지를 활성화한다. |
POSITION | 광원의 위치를 나타내며, 포인트 광원의 위치를 지정하는 데 사용한다. |
SPOT_DIRECTION | 광원의 스포트 라이트 방향을 나타낸다. |
SPOT_EXPONENT | 광원의 스포트 라이트가 얼마나 뾰족한지를 나타낸다. |
SPOT_CUTOFF | 광원의 스포트 라이트의 컷오프 각도를 나타낸다. |
CONSTANT_ATTENUATION, LINEAR_ATTENUATION, QUADRATIC_ATTENUATION | 감쇄(Attenuation) 계수를 나타내며, 포인트 광원이나 스포트 라이트가 어떻게 감쇄되는지를 결정한다. |
Lighting Model Parameters (광원 모델 매개변수) | |
LIGHT_MODEL_AMBIENT | 씬 전체에 대한 주변 광의 성분을 나타내고, 모든 광원에 의해 발생한 주변 광 성분을 합한 값이다. |
LIGHT_MODEL_LOCAL_VIEWER | 지역 뷰어 모드를 나타내며, 비활성화할 경우 무한 원근 효과가 활성화된다. |
LIGHT_MODEL_TWO_SIDE | 양면 조명 모델을 나타내며, 씬의 양면 모든 면에서 광원이 발생하는지 여부를 결정한다. |
LIGHT_MODEL_COLOR_CONTROL | 색상 제어 모드를 나타낸다. 기본 값으로 GL_SINGLE_COLOR로 설정되어 있다. |
그러면 조명과 재질효과는 어떻게?
전반사광을 설정하려면 우선 광원에 전반사광을 추가해야 하는데, 주변광이나 난반사광 경우와 동일하다.
// 전반사광의 컬러 및 밝기를 설정한다.
// 모든 값을 1.0f로 설정하면 태양빛과 비슷한 효과가 난다.
GLfloat specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
…
// 광원에 전반사광을 추가한다.
glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
// 광원을 활성화한다.
glEnable(GL_LIGHT0);
태양빛을 모델링할 때는 일반적으로 위와 같은 밝은 백색 광원을 사용한다.
재질 속성에 전반사 반사율을 추가한다.
// 전반사광의 반사율을 설정한다.
// 모든 값이 1이면 거의 모든 입사 광선을 반사한다.
GLfloat specref[] = {1.0f, 1.0f, 1.0f, 1.0f};
…
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
// 이후에 정의되는 모든 표면에 위에서 정의한 반사율을 추가한다.
glMaterialfv(GL_FRONT, GL_SPECULAR, specref);
// 전반사 지수를 추가한다.
glMateriali(GL_FRONT, GL_SHINESS, 128);
전반사 지수 값이 0일 경우 하이라이트가 초점을 갖지 않아 골고루 밝게 나타나며, 128에 가까워질수록 하이라이트의 초점이 모아져서 밝게 빛나는 면과 그렇지 않은 면이 뚜렷하게 나타난다.
이렇게 코드를 적용하다 보면 3D 효과를 볼 수 있다.
int COpenGLView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
int nPixelFormat;
m_hDC = ::GetDC(m_hWnd);
static PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
24,
0, 0, 0, 0, 0, 0,
0, 0,
0, 0, 0, 0, 0,
32,
0,
0,
PFD_MAIN_PLANE,
0,
0, 0, 0
};
nPixelFormat = ChoosePixelFormat(m_hDC, &pfd);
VERIFY(SetPixelFormat(m_hDC, nPixelFormat, &pfd));
m_hRC = wglCreateContext(m_hDC);
VERIFY(wglMakeCurrent(m_hDC, m_hRC));
SetupRC();
SetTimer(1, 50, NULL);
SetTimer(2, 17, NULL);
return 0;
}
void COpenGLView::OnDestroy()
{
VERIFY(wglMakeCurrent(NULL, NULL));
wglDeleteContext(m_hRC);
::ReleaseDC(m_hWnd, m_hDC);
gluDeleteQuadric(m_objQuad);
KillTimer(1);
KillTimer(2);
CView::OnDestroy();
// TODO: 여기에 메시지 처리기 코드를 추가합니다.
}
void COpenGLView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
GLfloat fAspect;
if (cy == 0) cy = 1;
glViewport(0, 0, cx, cy);
fAspect = (GLfloat)cx / (GLfloat)cy;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(90.0f, fAspect, 1.0f, -400.0f); // 시야각을 60도로 조절하고, 최대 시야 거리를 늘림
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// 카메라를 뒤로 이동
m_cameraDistance = -1000.0f; // 초기 카메라 거리 설정
UpdateCameraPosition();
}
// 주행 행성 및 태양을 렌더링하는 함수
void COpenGLView::RenderScene()
{
// 색상 및 깊이 버퍼 지우기
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 양면 제거 및 깊이 테스트 활성화
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
// 쿼드릭 노말을 플랫으로 설정
gluQuadricNormals(m_objQuad, GLU_FLAT);
// 현재 행렬 모드를 모델뷰로 설정
glMatrixMode(GL_MODELVIEW);
// 나중에 복원하기 위해 현재 행렬 저장
glPushMatrix();
// 광원 위치 설정 (LIGHT0 사용 중인 것으로 가정)
glLightfv(GL_LIGHT0, GL_POSITION, m_lightPos); // 2023-11-14 추가
UpdateCameraPosition();
// 태양을 나타내는 빨간 구 그리기
glColor3ub(255, 0, 0);
gluSphere(m_objQuad, sunRadius, 64, 64);
// 광원 위치 다시 설정 (LIGHT0 사용 중인 것으로 가정)
glLightfv(GL_LIGHT0, GL_POSITION, m_lightPos); // 2023-11-14 추가
// 수성 그리기
glPushMatrix();
glRotatef(m_fMercury, 0.0f, 0.1f, 0.0f); // 수성의 자전
glTranslatef(300.0f, 0.0f, 0.0f); // 조절된 거리
glColor3ub(200, 100, 0); // 수성의 색상
gluSphere(m_objQuad, mercuryRadius, 64, 64);
glPopMatrix();
// 금성 그리기
glPushMatrix();
glRotatef(m_fVenus, 0.0f, 0.1f, 0.0f); // 금성의 자전
glTranslatef(400.0f, 0.0f, 0.0f); // 조절된 거리
glColor3ub(255, 200, 0); // 금성의 색상
gluSphere(m_objQuad, venusRadius, 64, 64);
glPopMatrix();
// 지구 그리기
glPushMatrix();
glRotatef(m_fEarth, 0.0f, 0.1f, 0.0f); // 지구의 자전
glTranslatef(500.0f, 0.0f, 0.0f); // 조절된 거리
glColor3ub(0, 0, 255); // 지구의 색상
gluSphere(m_objQuad, earthRadius, 64, 64);
// 달의 색상 설정
glColor3ub(200, 200, 200);
glRotatef(m_fMoon, 0.0f, 1.0f, 0.0f); // 달의 자전
glTranslatef(50.0f, 0.0f, 0.0f); // 조절된 거리
gluSphere(m_objQuad, moonRadius, 64, 64);
glPopMatrix();
// 화성 그리기
glPushMatrix();
glRotatef(m_fMars, 0.0f, 0.1f, 0.0f); // 화성의 자전
glTranslatef(600.0f, 0.0f, 0.0f); // 조절된 거리
glColor3ub(255, 0, 0); // 화성의 색상
gluSphere(m_objQuad, marsRadius, 64, 64);
glPopMatrix();
// 목성 그리기
glPushMatrix();
glRotatef(m_fJupiter, 0.0f, 0.1f, 0.0f); // 목성의 자전
glTranslatef(700.0f, 0.0f, 0.0f); // 조절된 거리
glColor3ub(255, 165, 0); // 목성의 색상 (주황색)
gluSphere(m_objQuad, jupiterRadius, 64, 64);
glPopMatrix();
// 토성 그리기
glPushMatrix();
glRotatef(m_fSaturn, 0.0f, 0.1f, 0.0f); // 토성의 자전
glTranslatef(800.0f, 0.0f, 0.0f); // 조절된 거리
glColor3ub(210, 180, 140); // 토성의 색상 (밝은 갈색)
gluSphere(m_objQuad, saturnRadius, 64, 64);
// 토성의 고리 그리기
RenderSaturnRing(); // 토성의 고리 렌더링 함수 호출
glPopMatrix();
// 천왕성 그리기
glPushMatrix();
glRotatef(m_fUranus, 0.0f, 0.1f, 0.0f); // 천왕성의 자전
glTranslatef(900.0f, 0.0f, 0.0f); // 조절된 거리
glColor3ub(173, 216, 230); // 천왕성의 색상 (연한 파란색)
gluSphere(m_objQuad, uranusRadius, 64, 64);
glPopMatrix();
// 해왕성 그리기
glPushMatrix();
glRotatef(m_fNeptune, 0.0f, 0.1f, 0.0f); // 해왕성의 자전
glTranslatef(1000.0f, 0.0f, 0.0f); // 조절된 거리
glColor3ub(0, 0, 139); // 해왕성의 색상 (진한 파란색)
gluSphere(m_objQuad, neptuneRadius, 64, 64);
glPopMatrix();
// 명왕성 그리기
glPushMatrix();
glRotatef(m_fPluto, 0.0f, 0.1f, 0.0f); // 명왕성의 자전
glTranslatef(1100.0f, 0.0f, 0.0f); // 조절된 거리
glColor3ub(128, 128, 128); // 명왕성의 색상 (회색)
gluSphere(m_objQuad, plutoRadius, 64, 64);
glPopMatrix();
}
#define DEG_TO_RAD 0.0174532925199432957
// 토성의 고리를 렌더링하는 함수
void COpenGLView::RenderSaturnRing()
{
// 블렌딩 활성화
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 토성의 고리 크기 조절
float saturnRingInnerRadius = 60.0f; // 내부 반지름 조절
float saturnRingOuterRadius = 70.0f; // 외부 반지름 조절
float ringThickness = 5.0f; // 고리 두께 조절
// 고리를 그리기 위한 루프
glBegin(GL_QUADS);
for (float angle = 0; angle < 360; angle += 0.01) {
// 현재 각도에 대한 내부 반지름에서의 점의 좌표 계산
float xInner = saturnRingInnerRadius * cosf(angle * DEG_TO_RAD);
float zInner = saturnRingInnerRadius * sinf(angle * DEG_TO_RAD);
// 내부 반지름의 점 추가
glVertex3f(xInner, 0, zInner);
// 현재 각도에 대한 내부 반지름에서의 두꺼운 점의 좌표 계산
float xInnerThick = (saturnRingInnerRadius - ringThickness) * cosf(angle * DEG_TO_RAD);
float zInnerThick = (saturnRingInnerRadius - ringThickness) * sinf(angle * DEG_TO_RAD);
// 내부 반지름의 두꺼운 점 추가
glVertex3f(xInnerThick, 0, zInnerThick);
// 현재 각도에 대한 외부 반지름에서의 두꺼운 점의 좌표 계산
float xOuterThick = (saturnRingOuterRadius + ringThickness) * cosf(angle * DEG_TO_RAD);
float zOuterThick = (saturnRingOuterRadius + ringThickness) * sinf(angle * DEG_TO_RAD);
// 외부 반지름의 두꺼운 점 추가
glVertex3f(xOuterThick, 0, zOuterThick);
// 현재 각도에 대한 외부 반지름에서의 점의 좌표 계산
float xOuter = saturnRingOuterRadius * cosf(angle * DEG_TO_RAD);
float zOuter = saturnRingOuterRadius * sinf(angle * DEG_TO_RAD);
// 외부 반지름의 점 추가
glVertex3f(xOuter, 0, zOuter);
}
glEnd();
// 2D 텍스처 매핑 및 블렌딩 비활성화
glDisable(GL_BLEND);
}
void COpenGLView::SetupRC()
{
// Face culling 및 깊이 테스트 활성화
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_FLAT);
glFrontFace(GL_CCW);
// Quadric 객체 생성
m_objQuad = gluNewQuadric();
gluQuadricNormals(m_objQuad, GLU_FLAT);
// 조명 활성화
glEnable(GL_LIGHTING);
// 광원 속성 설정
glLightfv(GL_LIGHT0, GL_AMBIENT, m_ambientLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, m_diffuseLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, m_specularLight);
glLightfv(GL_LIGHT0, GL_POSITION, m_lightPos);
// LIGHT0 활성화
glEnable(GL_LIGHT0);
// 토성의 고리 텍스처 로딩
// (ringTexture 변수에 텍스처를 로딩하는 코드)
// 토성의 고리의 내부 반지름 설정
gluQuadricDrawStyle(m_objQuad, GLU_FILL);
gluQuadricTexture(m_objQuad, GL_TRUE);
gluQuadricNormals(m_objQuad, GLU_SMOOTH);
gluQuadricOrientation(m_objQuad, GLU_OUTSIDE);
// 색상 자재 활성화 및 설정
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glMaterialfv(GL_FRONT, GL_SPECULAR, m_specref);
glMateriali(GL_FRONT, GL_SHININESS, 128);
// 배경색 설정
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
void COpenGLView::OnTimer(UINT_PTR nIDEvent)
{
if (nIDEvent == 1) {
// 지구의 회전 속도 업데이트
m_fEarth += 3.0f;
if (m_fEarth > 360.0f)
m_fEarth = 6.0f;
// 수성의 회전 속도 업데이트
m_fMercury += 5.0f;
if (m_fMercury > 360.0f)
m_fMercury -= 360.0f;
// 금성의 회전 속도 업데이트
m_fVenus += 2.0f;
if (m_fVenus > 360.0f)
m_fVenus -= 360.0f;
// 화성의 회전 속도 업데이트
m_fMars += 1.5f;
if (m_fMars > 360.0f)
m_fMars -= 360.0f;
// 목성의 회전 속도 업데이트
m_fJupiter += 0.5f;
if (m_fJupiter > 360.0f)
m_fJupiter -= 360.0f;
// 토성의 회전 속도 업데이트
m_fSaturn += 0.3f;
if (m_fSaturn > 360.0f)
m_fSaturn -= 360.0f;
// 해왕성의 회전 속도 업데이트
m_fNeptune += 0.2f;
if (m_fNeptune > 360.0f)
m_fNeptune -= 360.0f;
// 천왕성의 회전 속도 업데이트
m_fUranus += 0.4f;
if (m_fUranus > 360.0f)
m_fUranus -= 360.0f;
// 명왕성(플루토)의 회전 속도 업데이트
m_fPluto += 0.1f;
if (m_fPluto > 360.0f)
m_fPluto -= 360.0f;
// ... (이하 추가 행성들에 대한 회전 속도 업데이트)
InvalidateRect(NULL);
}
else if (nIDEvent == 2) {
// 달의 회전 속도 업데이트
m_fMoon += 3.0f;
if (m_fMoon > 360.0f)
m_fMoon -= 360.0f;
// ... (이하 추가 달들에 대한 회전 속도 업데이트)
InvalidateRect(NULL);
}
CView::OnTimer(nIDEvent);
}
이렇게 태양계 구축이 완료되었다.
코드를 작성하면서 여러 어려움에 부딪혔습니다. 특히, 행성을 추가하는 과정에서 조명이 올바르게 적용되지 않거나, 태양계를 옆면과 윗면에서 제대로 볼 수 있게 하는 버튼 추가 작업에서 변수 초기화 문제 등 여러 가지 문제에 직면했습니다.
조명 문제
처음으로 행성을 추가할 때, 조명이 올바르게 동작하지 않아 행성들이 일종의 2D로 보이는 문제가 발생했습니다. 이 문제를 해결하려고 각 행성에 대한 조명과 재질을 올바르게 설정해야 했고 변수 초기화 위치와 값 설정이 정확해야 했는데, 몇 차례의 시도 끝에 각 행성에 대한 초기화를 정확한 위치에 놓아 문제를 해결할 수 있었습니다.
버튼 추가와 변수 초기화 문제
태양계를 다양한 각도에서 관찰할 수 있도록 옆면과 윗면에서의 보기를 전환하는 버튼을 추가하려 했습니다. 그러나 이 작업에서 버튼을 누를 때마다 변수들이 초기화되어 조명과 재질이 다시 설정되어 버리는 문제가 발생했습니다.
이 문제를 해결하기 위해, 버튼 동작에 필요한 변수들이 매번 초기화되지 않도록 면밀한 코드 리뷰와 수정을 거쳐야 했습니다. 특히, 버튼 동작에 관련된 부분과 조명 설정 부분의 충돌을 방지하기 위해 변수의 범위와 생명주기를 신중하게 다뤄야 했습니다.
예를 들어 Runtime Error라든지... Stack around Error라든지..
마무리
그렇게 시행착오 끝에 최종적으로 우리의 태양계 모델이 원하는 대로 동작하게 되었고 코드 작성 중 발생한 다양한 어려움들을 극복하며 더 나은 프로그래머로 성장할 수 있었던 소중한 경험이었습니다. 앞으로의 프로젝트에서는 초기 구상 단계부터 변수와 함수의 역할, 생명주기를 정확히 파악하여 코드 작성에 있어 불필요한 복잡성을 줄이고자 노력할 것이며,
코드를 통해 새로운 지식을 습득하고, 발생하는 문제들을 해결하는 과정에서의 성장은 끊임없이 이어지리라 믿고 미완성된 코드 속에서 끝까지 도전하는 것은 항상 보람찬 경험이 되었습니다.
작성자: [권순욱] 날짜: [2023-11-24]
'C, C++ > OpenGL' 카테고리의 다른 글
C/C++ OpenGl을 이용한 간단한 태양계 구축 프로젝트(2) (1) | 2023.11.18 |
---|---|
C/C++ OpenGl을 이용한 간단한 태양계 구축 프로젝트(1) (3) | 2023.11.08 |