Создание простой анимации часов с использованием OpenGL

CCBeginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В этом проекте мы создадим простую анимацию часов с использованием OpenGL и GLUT (Graphics Library Utility Toolkit). Эта анимация будет отображать часы с движущимися стрелками, представляющими текущее время. Часы будут обновляться в режиме реального времени, имитируя движение часов, минутных и секундных стрел. Мы начнем с настройки файлов проекта и затем перейдем к необходимому коду.

👀 Предварительный просмотр

Clock Opengl

🎯 Задачи

В этом проекте вы научитесь:

  • настраивать файлы и библиотеки проекта;
  • создавать окно и инициализировать OpenGL;
  • рисовать фон и контур часов;
  • вращать часы, чтобы 12 часов располагались вверху;
  • получать текущее время и вычислять позиции стрел часов;
  • рисовать стрелки часов, минуты и секунды на часах;
  • изменять размер окна и отображать часы в режиме реального времени.

🏆 Достижения

После завершения этого проекта вы сможете:

  • настраивать и инициализировать OpenGL и GLUT;
  • рисовать базовые формы и линии с использованием OpenGL;
  • вращать объекты в OpenGL;
  • получать текущее время и использовать его для анимации объектов;
  • обрабатывать изменение размера окна и реальное отображение графики.

Создайте файлы проекта

Сначала выполните следующую команду в терминале, чтобы установить библиотеки OpenGL и GLUT:

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

Это установит библиотеку графики Mesa 3D, а также библиотеку GLUT. После этого вы сможете компилировать программы OpenGL с использованием gcc.

Далее создайте новый файл с именем 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 в качестве фона часов. Четыре вершины определяют границы прямоугольника, а код между glBegin и glEnd используется для определения вида графика. Это часть рисования фона часов, которая обычно рисуется перед другими элементами, такими как стрелка часов, стрелка минут и стрелка секунд и т.д.

✨ Проверить решение и практиковаться

Вращайте часы

Мы добавим код для вращения часов на 180 градусов, чтобы положение 12 часов было сверху.

// Внутри функции drawClockHands()
glPushMatrix();
glRotatef(ROTATE_DEGREES, 0, 0, 1);
  • glPushMatrix(): Это функция в OpenGL, которая помещает текущее состояние матрицы (обычно матрицу моделирования вида) на стек, чтобы его можно было восстановить позже. Это распространенная практика, потому что при рисовании может потребоваться выполнить несколько различных преобразований матрицы, и нужно убедиться, чтобы последующие преобразования не влияли на предыдущие состояния.

  • glRotatef(): Это еще одна функция OpenGL, которая выполняет преобразование вращения. Она вращает текущую матрицу моделирования вида на ROTATE_DEGREES вокруг оси Z. (0, 0, 1) - это определение оси вращения, которая здесь означает вращение вокруг оси Z. Первое значение - это компонента вращения вокруг оси X, второе значение - компонента вращения вокруг оси Y, а третье значение - компонента вращения вокруг оси Z.

✨ Проверить решение и практиковаться

Нарисуйте контур часов

Мы нарисуем контур часов с делениями на каждую час.

// Внутри функции drawClockHands()
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 задает ширину линии, а цикл между glBegin и glEnd рисует серию точек на окружности, центрированной в начале координат, которые затем соединяются, образуя замкнутую петлю - внешний контур часов.

✨ Проверить решение и практиковаться

Получите текущее время и вычислите позиции стрелок часов

Мы получим текущее время и вычислим позиции стрелок часов: часовую, минутную и секундную.

// Внутри функции drawClockHands()
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 для хранения необработанных данных времени, полученных из системы. time_t - это тип данных, обычно используемый для представления количества секунд от определенной точки времени (обычно 1 января 1970) до текущего момента.

Объявлен указатель на структуру tm под названием timeinfo. Структура tm используется для хранения различных компонентов информации о времени и дате, таких как год, месяц, день, час, минута, секунда и т.д.

Используется функция time для получения текущего системного времени, сохраняя количество секунд времени в переменной rawtime. Количество секунд, возвращаемое функцией time, представляет количество секунд от определенной точки времени (обычно 1 января 1970, также называемой UNIX-меткой времени) до текущего времени.

Используется функция localtime для преобразования количества секунд, полученного из функции time (хранящегося в переменной rawtime), в конкретную информацию о времени и дате, и результат сохраняется в структуре timeinfo. Функция localtime преобразует количество секунд в информацию о годе, месяце, дне, часах, минутах, секундах и т.д., и сохраняет эту информацию в структуре timeinfo.

Из структуры timeinfo получается текущее количество часов, затем вычисляется остаток от деления на 12, чтобы ограничить количество часов до 12-часового формата, tm_hour указывает на час. Из структуры timeinfo получается текущее количество минут, tm_min указывает на минуту. Из структуры timeinfo получается текущее количество секунд, tm_sec указывает на секунду.

✨ Проверить решение и практиковаться

Нарисуйте стрелку часов

Мы нарисуем стрелку часов на часах.

// Внутри функции drawClockHands()
double hourAngle = -(90 + hours * 360 / 12) + (360 / 12) * minutes / 60;
glLineWidth(5.0); // Установите ширину линии
glBegin(GL_LINES);
glVertex2d(0, 0);
glColor3f(1.0, 0.0, 0.0); // Цвет стрелки часов
glVertex2d(0.5 * cos(hourAngle * M_PI / 180), 0.5 * sin(hourAngle * M_PI / 180));
glEnd();

Во - первых, часы обычно имеют 12 - часовой формат, поэтому количество часов нужно преобразовать в угол на циферблате. Этот расчет начинается с отображения количества часов из 12 - часового формата в угловой диапазон в 360 градусов. Затем добавляется вклад минут (по позиции минутной стрелки) к углу часов. Полученный hourAngle представляет угол стрелки часов относительно направления 12 часов.

Затем определяется элемент, в данном случае отрезок прямой, представляющий стрелку часов. Точка начала отрезка прямой находится в координатах (0, 0), центре часов. Точка конца отрезка прямой вычисляется с использованием тригонометрических функций cos и sin для определения конечной позиции стрелки часов. Подsequent минутные и секундные стрелки также рисуются таким же образом.

✨ Проверить решение и практиковаться

Нарисуйте минутную стрелку

Мы добавим код для рисования минутной стрелки на часах.

// Внутри функции drawClockHands()
double minuteAngle = -(90 + minutes * 360 / 60);
glLineWidth(3.0); // Установите ширину линии
glBegin(GL_LINES);
glVertex2d(0, 0);
glColor3f(0.0, 1.0, 0.0); // Цвет минутной стрелки
glVertex2d(0.7 * cos(minuteAngle * M_PI / 180), 0.7 * sin(minuteAngle * M_PI / 180));
glEnd();

Циферблат часов обычно имеет 60 минут, поэтому количество минут нужно преобразовать в угол на циферблате. Этот расчет сначала отображает количество минут в угловой диапазон в 360 градусов и вычитает 90 градусов, чтобы 0 минут соответствовало направлению 12 часов. Полученный minuteAngle представляет угол минутной стрелки относительно направления 12 часов.

✨ Проверить решение и практиковаться

Нарисуйте секундную стрелку

Мы добавим код для рисования секундной стрелки на часах.

// Внутри функции drawClockHands()
double secondAngle = -(90 + seconds * 360 / 60);
glLineWidth(1.0); // Установите ширину линии
glBegin(GL_LINES);
glVertex2d(0, 0);
glColor3f(0.0, 0.0, 1.0); // Цвет секундной стрелки
glVertex2d(0.9 * cos(secondAngle * M_PI / 180), 0.9 * sin(secondAngle * M_PI / 180));
glEnd();

Часы обычно имеют 60 секунд, поэтому количество секунд нужно преобразовать в угол на表盘е. Этот расчет сначала отображает количество секунд в угловой диапазон в 360 градусов и вычитает 90 градусов, чтобы 0 секунд соответствовало направлению 12 часов. Полученный secondAngle представляет угол секундной стрелки относительно направления 12 часов.

Наконец, мы восстановим исходную вращение и отобразим анимацию часов.

// Внутри функции drawClockHands()
glPopMatrix();
glutSwapBuffers();

glPopMatrix() используется для восстановления ранее сохраненного состояния матрицы, а glutSwapBuffers() используется для завершения двойной буферизации рисования, отображая нарисованное изображение на экране, чтобы достичь гладких анимационных эффектов.

✨ Проверить решение и практиковаться

Изменение размера окна и реальное отображение

Мы создадим функцию reshape, которая вызывается при изменении размера окна.

void reshape(int w, int h) {
    glViewport(0, 0, w, h); // Установите область просмотра OpenGL для调整 размера окна
    glMatrixMode(GL_PROJECTION); // Переключитесь в режим матрицы проекции
    glLoadIdentity(); // Сбросьте текущую матрицу
    gluOrtho2D(-1, 1, -1, 1); // Установите матрицу ортографической проекции
    glMatrixMode(GL_MODELVIEW); // Переключитесь обратно в режим матрицы вида
}

Цель этой функции reshape - это调整 область просмотра OpenGL в соответствии с новым размером окна при изменении размера окна и установить соответствующую матрицу проекции, чтобы обеспечить правильное отображение нарисованного графика при новом размере окна.

Далее мы определяем функцию idle, которая выполняет некоторые операции, когда программа находится в неактивном состоянии.

void idle() {
    glutPostRedisplay(); // Запросите перерисовку, когда программа находится в неактивном состоянии
}

Цель этой функции 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, рисование фона часов, вращение часов, рисование контура часов, получение текущего времени и рисование стрелок часов. Следуя этим шагам, вы можете создать базовую анимацию часов, которая отображает текущее время в визуально привлекательном виде.