OpenGL 을 사용한 간단한 시계 애니메이션 만들기

CBeginner
지금 연습하기

소개

이 프로젝트에서는 OpenGL 과 GLUT (Graphics Library Utility Toolkit) 를 사용하여 간단한 시계 애니메이션을 만들 것입니다. 이 애니메이션은 현재 시간을 나타내기 위해 움직이는 시계 바늘이 있는 시계를 표시합니다. 시계는 실시간으로 업데이트되어 시침, 분침, 초침의 움직임을 시뮬레이션합니다. 프로젝트 파일을 설정하는 것으로 시작하여 필요한 코드를 작성해 나갈 것입니다.

👀 미리보기

Clock Opengl

🎯 과제

이 프로젝트에서 다음을 배우게 됩니다:

  • 프로젝트 파일 및 라이브러리를 설정하는 방법
  • 창을 만들고 OpenGL 을 초기화하는 방법
  • 시계 배경 및 윤곽선을 그리는 방법
  • 12 시 위치가 상단에 오도록 시계를 회전시키는 방법
  • 현재 시간을 가져와 시계 바늘의 위치를 계산하는 방법
  • 시계에 시침, 분침, 초침을 그리는 방법
  • 창 크기를 조정하고 실시간으로 시계를 표시하는 방법

🏆 성과

이 프로젝트를 완료하면 다음을 수행할 수 있습니다:

  • OpenGL 및 GLUT 를 설정하고 초기화합니다.
  • OpenGL 을 사용하여 기본 도형과 선을 그립니다.
  • OpenGL 에서 객체를 회전시킵니다.
  • 현재 시간을 가져와 객체를 애니메이션합니다.
  • 창 크기 조절 및 그래픽의 실시간 표시를 처리합니다.

프로젝트 파일 생성

먼저, 터미널에서 다음 명령을 실행하여 OpenGLGLUT 라이브러리를 설치합니다:

sudo apt update
sudo apt-get install mesa-utils freeglut3-dev -y

이렇게 하면 Mesa 3D 그래픽 라이브러리와 GLUT 라이브러리가 설치됩니다. 그 후, gcc를 사용하여 OpenGL 프로그램을 컴파일할 수 있습니다.

다음으로, clock_opengl.c라는 새 파일을 생성하고 선호하는 코드 편집기에서 엽니다.

cd ~/project
touch clock_opengl.c

헤더 포함 및 변수 정의

다음으로, 이 프로젝트를 구현하기 위해 코드를 단계별로 작성합니다.

#include <GL/gl.h>
#include <GL/glut.h>
#include <time.h>
#include <math.h>
#include <stdio.h>

const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;
const int ROTATE_DEGREES = 180;

WINDOW_WIDTH, WINDOW_HEIGHT, 및 ROTATE_DEGREES의 세 가지 정수 상수가 정의됩니다. 이러한 상수는 창의 너비와 높이, 그리고 회전 각도를 정의하는 데 사용됩니다.

윈도우 설정 및 OpenGL 초기화

이 단계에서는 창 크기를 구성하고 OpenGL 및 GLUT 를 초기화합니다.

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
    glutCreateWindow("Clock Animation");

    glutDisplayFunc(drawClockHands);
    glutReshapeFunc(reshape);
    glutIdleFunc(idle);

    glClearColor(0.0, 0.0, 0.0, 1.0);

    glutMainLoop();

    return 0;
}
  • glutInit(): GLUT 라이브러리를 초기화하는 데 사용됩니다.
  • glutInitDisplayMode(): 창의 디스플레이 모드를 설정합니다. GLUT_DOUBLE은 이중 버퍼링을 사용함을 의미하고, GLUT_RGB는 RGB 색상 모드를 사용함을 의미합니다.
  • glutInitWindowSize(): 창의 크기를 설정합니다.
  • glutCreateWindow(): 창을 생성하고 창의 제목을 Clock Animation으로 설정합니다.
  • glutDisplayFunc(): 그래프를 그릴 필요가 있을 때 drawClockHands 함수가 호출되도록 그리기 함수를 설정합니다.
  • glutReshapeFunc(): 창 크기 조정을 위한 콜백 함수를 설정합니다. 즉, 창 크기가 변경될 때 reshape 함수가 호출됩니다.
  • glutIdleFunc(): 유휴 콜백 함수를 설정합니다. 즉, 다른 이벤트 처리 작업이 없을 때 idle 함수가 호출됩니다.
  • glClearColor(0.0, 0.0, 0.0, 1.0): 빈 색상을 검정색 (RGBA 색상 값) 으로 설정합니다.
  • glutMainLoop(): GLUT 의 메인 루프에 진입합니다. 이 루프는 계속 실행되면서 윈도잉 이벤트를 처리하고, 그래프를 그리고, 사용자 입력을 받습니다.

시계 배경 그리기

drawClockHands라는 함수를 생성합니다. 어두운 색의 원을 포함하여 시계의 배경을 그리는 코드를 추가합니다.

void drawClockHands() {
    glClear(GL_COLOR_BUFFER_BIT);

    glBegin(GL_QUADS);
    glColor3f(0.1, 0.1, 0.1);
    glVertex2f(-1, -1);
    glVertex2f(1, -1);
    glVertex2f(1, 1);
    glVertex2f(-1, 1);
    glEnd();
}
  • glClear(): 이 함수 호출은 새로운 그래프를 그리기 위해 색상 버퍼를 지웁니다. GL_COLOR_BUFFER_BIT는 색상 버퍼를 지워서 이전 그림의 내용을 새로운 그림을 그리기 전에 지우는 것을 의미합니다.
  • glBegin(): 이 함수는 요소 (이 경우 사각형) 정의의 시작을 나타냅니다. GL_QUADS는 사각형을 사용하여 그래프를 정의하려는 것을 의미합니다.
  • glColor3f(): 현재 그리기 색상의 부동 소수점 값을 RGB 색상 (빨강, 녹색, 파랑) 으로 설정합니다. 여기서는 색상을 어두운 회색으로 설정하며, RGB 값 (0.1, 0.1, 0.1)은 어두운 회색을 나타냅니다.
  • glVertex2f(): 사각형의 모서리를 정의합니다.

여기서의 목적은 OpenGL 창에 어두운 회색 사각형을 시계의 배경으로 그리는 것입니다. 네 개의 꼭짓점은 사각형의 경계를 정의하며, glBeginglEnd 사이의 코드는 그래프의 모양을 정의하는 데 사용됩니다. 이것은 시계 배경을 그리는 부분이며, 일반적으로 시침, 분침, 초침 등과 같은 다른 요소보다 먼저 그려집니다.

시계 회전

12 시 위치가 상단에 오도록 시계를 180 도 회전하는 코드를 추가합니다.

// Inside the drawClockHands() function
glPushMatrix();
glRotatef(ROTATE_DEGREES, 0, 0, 1);
  • glPushMatrix(): OpenGL 의 이 함수는 현재 행렬 상태 (일반적으로 모델 뷰 행렬) 를 스택에 넣어서 나중에 복원할 수 있도록 합니다. 이는 드로잉에서 여러 가지 행렬 변환을 해야 할 수 있고, 후속 변환이 이전 상태에 영향을 미치지 않도록 해야 하기 때문에 일반적인 관행입니다.

  • glRotatef(): 이것은 회전 변환을 수행하는 또 다른 OpenGL 함수입니다. 현재 모델 뷰 행렬을 Z 축을 기준으로 ROTATE_DEGREES만큼 회전시킵니다. (0, 0, 1)은 회전축의 정의이며, 여기서는 Z 축을 기준으로 회전함을 의미합니다. 첫 번째 값은 X 축에 대한 회전 성분, 두 번째 값은 Y 축에 대한 회전 성분, 세 번째 값은 Z 축에 대한 회전 성분입니다.

시계 외곽선 그리기

각 시간마다 눈금 표시와 함께 시계의 윤곽선을 그립니다.

// Inside the drawClockHands() function
glLineWidth(3.0);
glBegin(GL_LINE_LOOP);
for (int i = 0; i < 360; i += 6) {
    double angle = i * M_PI / 180;
    double x = 0.9 * cos(angle);
    double y = 0.9 * sin(angle);
    glColor3f(1.0, 1.0, 1.0);
    glVertex2d(x, y);
}
glEnd();

여기서의 주요 목적은 시계의 배경에 흰색 원형 윤곽선을 그려 시계의 외부 프레임을 나타내는 것입니다. glLineWidth는 선의 두께를 설정하고, glBeginglEnd 사이의 루프는 원점을 중심으로 하는 일련의 점을 원 위에 그립니다. 이 점들은 연결되어 닫힌 루프를 형성하며, 이는 시계의 외부 윤곽선입니다.

현재 시간 가져오기 및 시계 바늘 위치 계산

현재 시간을 가져와 시계 바늘 (시, 분, 초) 의 위치를 계산합니다.

// Inside the drawClockHands() function
time_t rawtime;
struct tm* timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);

int hours = timeinfo->tm_hour % 12;
int minutes = timeinfo->tm_min;
int seconds = timeinfo->tm_sec;

rawtime이라는 변수는 시스템에서 가져온 rawtime 데이터를 저장하기 위해 정의됩니다. time_t는 특정 시점 (일반적으로 1970 년 1 월 1 일) 부터 현재까지의 시간을 초 단위로 나타내는 데 일반적으로 사용되는 데이터 유형입니다.

timeinfo라는 tm 구조체에 대한 포인터를 정의합니다. tm 구조체는 연도, 월, 일, 시, 분, 초 등과 같은 시간 및 날짜 정보의 다양한 구성 요소를 저장하는 데 사용됩니다.

time 함수를 사용하여 현재 시스템 시간을 가져와 rawtime 변수에 시간의 초를 저장합니다. time 함수가 반환하는 초 수는 특정 시점 (일반적으로 1970 년 1 월 1 일, UNIX 타임스탬프로도 알려짐) 부터 현재 시간까지의 초 수를 나타냅니다.

localtime 함수를 사용하여 time 함수에서 얻은 초 수 ( rawtime 변수에 저장됨) 를 구체적인 시간 및 날짜 정보로 변환하고 결과를 timeinfo 구조체에 저장합니다. localtime 함수는 초 수를 연도, 월, 일, 시, 분, 초 등과 같은 정보로 변환하고 이 정보를 timeinfo 구조체에 저장합니다.

timeinfo 구조체에서 현재 시를 가져온 다음 12 로 모듈로 연산하여 시를 12 시간 규모로 제한합니다. tm_hour는 시를 나타냅니다. timeinfo 구조체에서 현재 분을 가져옵니다. tm_min은 분을 나타냅니다. timeinfo 구조체에서 현재 초를 가져옵니다. tm_sec은 초를 나타냅니다.

시침 그리기

시계에 시침을 그립니다.

// Inside the drawClockHands() function
double hourAngle = -(90 + hours * 360 / 12) + (360 / 12) * minutes / 60;
glLineWidth(5.0); // Set line width
glBegin(GL_LINES);
glVertex2d(0, 0);
glColor3f(1.0, 0.0, 0.0); // Hour hand color
glVertex2d(0.5 * cos(hourAngle * M_PI / 180), 0.5 * sin(hourAngle * M_PI / 180));
glEnd();

먼저, 시계는 일반적으로 12 시간 규모로 표시되므로 hours의 숫자를 시계 다이얼의 각도 (Angle) 로 변환해야 합니다. 이 계산은 12 시간 규모의 시간 수를 360 도 각도 범위로 매핑하는 것으로 시작합니다. 그런 다음 minutes의 기여 (분침의 위치에 따라) 가 시침 각도에 추가됩니다. 결과 hourAngle은 12 시 방향을 기준으로 한 시침의 각도를 나타냅니다.

그런 다음, 이 경우 시침을 나타내는 선분 요소를 정의합니다. 선분의 시작점은 시계의 중심인 좌표 (0, 0) 에 위치합니다. 선분의 끝점은 삼각 함수 cossin을 사용하여 시침의 끝 위치를 계산합니다. 이후 분침과 초침도 이 방식으로 그려집니다.

분침 그리기

시계에 분침을 그리는 코드를 추가합니다.

// Inside the drawClockHands() function
double minuteAngle = -(90 + minutes * 360 / 60);
glLineWidth(3.0); // Set line width
glBegin(GL_LINES);
glVertex2d(0, 0);
glColor3f(0.0, 1.0, 0.0); // Minute hand color
glVertex2d(0.7 * cos(minuteAngle * M_PI / 180), 0.7 * sin(minuteAngle * M_PI / 180));
glEnd();

시계 다이얼은 일반적으로 60 분으로 구성되므로 minutes의 숫자를 시계 다이얼의 각도 (Angle) 로 변환해야 합니다. 이 계산은 먼저 분 수를 360 도 각도 범위로 매핑하고 90 도를 빼서 0 분을 12 시 방향에 해당하도록 합니다. 결과 minuteAngle은 12 시 방향을 기준으로 한 분침의 각도를 나타냅니다.

초침 그리기

시계에 초침을 그리는 코드를 추가합니다.

// Inside the drawClockHands() function
double secondAngle = -(90 + seconds * 360 / 60);
glLineWidth(1.0); // Set line width
glBegin(GL_LINES);
glVertex2d(0, 0);
glColor3f(0.0, 0.0, 1.0); // Second hand color
glVertex2d(0.9 * cos(secondAngle * M_PI / 180), 0.9 * sin(secondAngle * M_PI / 180));
glEnd();

시계는 일반적으로 60 초로 구성되므로 seconds의 숫자를 시계 면의 각도 (Angle) 로 변환해야 합니다. 이 계산은 먼저 초 수를 360 도의 각도 범위로 매핑하고 90 도를 빼서 0 초를 12 시 방향에 해당하도록 합니다. 결과 secondAngle은 12 시 방향을 기준으로 한 초침의 각도를 나타냅니다.

마지막으로, 원래 회전을 복원하고 시계 애니메이션을 표시합니다.

// Inside the drawClockHands() function
glPopMatrix();
glutSwapBuffers();

glPopMatrix()는 이전에 저장된 행렬 상태를 복원하는 데 사용되며, glutSwapBuffers()는 이중 버퍼링 (double buffered) 그리기를 완료하여 그려진 이미지를 화면에 렌더링하여 부드러운 애니메이션 효과를 얻는 데 사용됩니다.

창 크기 조절 및 실시간 표시

윈도우 크기가 변경될 때 호출되는 reshape 함수를 생성합니다.

void reshape(int w, int h) {
    glViewport(0, 0, w, h); // Set the OpenGL viewport for window size adjustments
    glMatrixMode(GL_PROJECTION); // Switch to projection matrix mode
    glLoadIdentity(); // Reset the current matrix
    gluOrtho2D(-1, 1, -1, 1); // Set the orthographic projection matrix
    glMatrixMode(GL_MODELVIEW); // Switch back to model-view matrix mode
}

reshape 함수의 목적은 윈도우 크기가 변경될 때 새로운 윈도우 크기에 따라 OpenGL 의 뷰포트 (viewport) 를 조정하고, 적절한 투영 행렬 (projection matrix) 을 설정하여 그려진 그래프가 새로운 윈도우 크기에서도 올바르게 표시되도록 하는 것입니다.

다음으로, 프로그램이 유휴 상태일 때 일부 작업을 수행하는 idle이라는 함수를 정의합니다.

void idle() {
    glutPostRedisplay(); // Request a redisplay when idle
}

idle 함수의 목적은 프로그램이 유휴 상태일 때 glutPostRedisplay()를 호출하는 것입니다. 윈도우의 내용을 다시 그리도록 요청하여 윈도우의 내용을 실시간으로 애니메이션하거나 업데이트하기 위함입니다.

프로젝트 컴파일 및 실행

  1. 터미널을 열고 프로젝트 디렉토리로 이동합니다.
cd ~/project
  1. 다음 명령을 사용하여 코드를 컴파일합니다.
gcc -o clock_opengl clock_opengl.c -lGL -lglut -lGLU -lm
  1. 애플리케이션을 실행합니다.
./clock_opengl

Clock Opengl

요약

이 프로젝트에서는 OpenGL 과 GLUT 를 사용하여 간단한 시계 애니메이션을 만드는 방법을 배웠습니다. 프로젝트 파일 설정, 윈도우 및 OpenGL 초기화, 시계 배경 그리기, 시계 회전, 시계 윤곽선 그리기, 현재 시간 가져오기, 시계 바늘 그리기를 다루었습니다. 이러한 단계를 따르면 현재 시간을 시각적으로 매력적인 방식으로 표시하는 기본적인 시계 애니메이션을 만들 수 있습니다.

✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습