Erstellen einer einfachen Uhranimation mit OpenGL

CCBeginner
Jetzt üben

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

In diesem Projekt werden wir eine einfache Uhranimation mit OpenGL und GLUT (Graphics Library Utility Toolkit) erstellen. Diese Animation wird eine Uhr mit sich bewegenden Uhrzeigern anzeigen, um die aktuelle Zeit darzustellen. Die Uhr wird in Echtzeit aktualisiert und simuliert die Bewegung der Stunden-, Minuten- und Sekundenzeiger. Wir werden beginnen, indem wir die Projektdateien einrichten und dann mit dem erforderlichen Code fortfahren.

👀 Vorschau

Clock Opengl

🎯 Aufgaben

In diesem Projekt lernen Sie:

  • Wie Sie die Projektdateien und Bibliotheken einrichten
  • Wie Sie das Fenster erstellen und OpenGL initialisieren
  • Wie Sie den Uhrrücken und den Umriss der Uhr zeichnen
  • Wie Sie die Uhr drehen, sodass die 12-Uhr-Position oben ist
  • Wie Sie die aktuelle Zeit abrufen und die Positionen der Uhrzeiger berechnen
  • Wie Sie die Stunden-, Minuten- und Sekundenzeiger auf der Uhr zeichnen
  • Wie Sie das Fenster vergrößern und die Uhr in Echtzeit anzeigen

🏆 Errungenschaften

Nach Abschluss dieses Projekts werden Sie in der Lage sein:

  • OpenGL und GLUT einzurichten und zu initialisieren
  • Basische Formen und Linien mit OpenGL zu zeichnen
  • Objekte in OpenGL zu drehen
  • Die aktuelle Zeit abzurufen und sie zur Animation von Objekten zu verwenden
  • Die Fenstergröße zu verwalten und die Echtzeitanzeige von Grafiken zu ermöglichen

Erstellen der Projektdateien

Zunächst führen Sie den folgenden Befehl im Terminal aus, um die OpenGL- und GLUT-Bibliotheken zu installieren:

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

Dies installiert die Mesa 3D-Grafikbibliothek sowie die GLUT-Bibliothek. Danach können Sie OpenGL-Programme mit gcc kompilieren.

Als Nächstes erstellen Sie eine neue Datei namens clock_opengl.c und öffnen sie in Ihrem bevorzugten Code-Editor.

cd ~/project
touch clock_opengl.c
✨ Lösung prüfen und üben

Headerdateien einbinden und Variablen definieren

Als Nächstes werden wir Schritt für Schritt den Code schreiben, um dieses Projekt umzusetzen.

#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;

Es werden drei ganzzahlige Konstanten definiert: WINDOW_WIDTH, WINDOW_HEIGHT und ROTATE_DEGREES. Diese Konstanten werden verwendet, um die Breite und Höhe des Fensters sowie den Rotationswinkel zu definieren.

✨ Lösung prüfen und üben

Fenster einrichten und OpenGL initialisieren

In diesem Schritt konfigurieren wir die Fenstergröße und initialisieren OpenGL und 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(): Wird verwendet, um die GLUT-Bibliothek zu initialisieren.
  • glutInitDisplayMode(): Legt den Anzeigemodus des Fensters fest. GLUT_DOUBLE bedeutet, dass doppelte Pufferung verwendet wird, und GLUT_RGB bedeutet, dass der RGB-Farbmodus verwendet wird.
  • glutInitWindowSize(): Legt die Größe des Fensters fest.
  • glutCreateWindow(): Erstellt ein Fenster und setzt den Titel des Fensters auf Clock Animation.
  • glutDisplayFunc(): Legt die Zeichnungsfunktion fest, sodass die drawClockHands-Funktion aufgerufen wird, wenn ein Diagramm gezeichnet werden muss.
  • glutReshapeFunc(): Legt die Rückruffunktion für die Fenstergrößenänderung fest, d.h., wenn die Fenstergröße sich ändert, wird die reshape-Funktion aufgerufen.
  • glutIdleFunc(): Legt die Leerlauf-Rückruffunktion fest, d.h., die idle-Funktion wird aufgerufen, wenn keine andere Ereignisverarbeitung erfolgt.
  • glClearColor(0.0, 0.0, 0.0, 1.0): Setzt die Leerlauf-Farbe auf Schwarz (RGBA-Farbwert).
  • glutMainLoop(): Tritt in die Hauptschleife von GLUT ein, die ständig läuft, Fensterereignisse verarbeitet, Diagramme zeichnet und Benutzereingaben entgegennimmt.
✨ Lösung prüfen und üben

Zeichnen des Uhrrückens

Erstellen Sie eine Funktion namens drawClockHands. Wir werden Code hinzufügen, um den Uhrrücken zu zeichnen, einschließlich eines kreisförmigen Elements in dunkler Farbe.

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(): Dieser Funktionsaufruf bereinigt den Farbpuffer, um die Vorbereitung für das Zeichnen eines neuen Diagramms zu treffen. GL_COLOR_BUFFER_BIT bedeutet, den Farbpuffer zu bereinigen, sodass der Inhalt der vorherigen Zeichnung gelöscht wird, bevor ein neues gezeichnet wird.
  • glBegin(): Diese Funktion markiert den Beginn der Definition eines Elements (in diesem Fall eines Vierecks). GL_QUADS bedeutet, dass wir Vierecke verwenden möchten, um das Diagramm zu definieren.
  • glColor3f(): Setzt den Gleitpunktwert der aktuellen Zeichnungsfarbe auf RGB-Farben (Rot, Grün, Blau). Hier wird die Farbe auf dunkelgrau gesetzt, wobei die RGB-Werte (0.1, 0.1, 0.1) dunkelgrau repräsentieren.
  • glVertex2f(): Definiert die Ecken eines Vierecks.

Zweck hier ist es, ein dunkelgraues Rechteck im OpenGL-Fenster als Hintergrund der Uhr zu zeichnen. Die vier Eckpunkte definieren die Grenzen des Rechtecks, und der Code zwischen glBegin und glEnd wird verwendet, um das Aussehen des Diagramms zu definieren. Dies ist ein Teil der Zeichnung des Uhrrückens, der normalerweise vor anderen Elementen wie der Stundenzeiger, der Minutenzeiger, der Sekundenzeiger usw. gezeichnet wird.

✨ Lösung prüfen und üben

Drehen der Uhr

Wir werden Code hinzufügen, um die Uhr um 180 Grad zu drehen, sodass die 12-Uhr-Position oben ist.

// Inside the drawClockHands() function
glPushMatrix();
glRotatef(ROTATE_DEGREES, 0, 0, 1);
  • glPushMatrix(): Dies ist eine Funktion in OpenGL, die den aktuellen Matrix-Zustand (gewöhnlich die Modellansicht-Matrix) auf den Stapel legt, damit er später wiederhergestellt werden kann. Dies ist eine übliche Praxis, da Sie möglicherweise mehrere verschiedene Matrix-Transformationen in einem Zeichenvorgang vornehmen müssen und Sie sicherstellen müssen, dass nachfolgende Transformationen nicht den vorherigen Zuständen zuwiderlaufen.

  • glRotatef(): Dies ist eine weitere OpenGL-Funktion, die die Rotationstransformation durchführt. Sie rotiert die aktuelle Modellansicht-Matrix um ROTATE_DEGREES um die Z-Achse. (0, 0, 1) ist die Definition der Rotationsachse, was hier bedeutet, dass die Rotation um die Z-Achse erfolgt. Der erste Wert ist der Rotationsanteil um die X-Achse, der zweite Wert ist der Rotationsanteil um die Y-Achse und der dritte Wert ist der Rotationsanteil um die Z-Achse.

✨ Lösung prüfen und üben

Zeichnen des Uhrumrisses

Wir werden den Uhrumriss mit Strichen für jede Stunde zeichnen.

// 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();

Der Hauptzweck hier ist es, einen weißen Kreisumriss auf dem Hintergrund der Uhr zu zeichnen, der den Außenrahmen der Uhr darstellt. glLineWidth setzt die Linienbreite, und die Schleife zwischen glBegin und glEnd zeichnet eine Reihe von Punkten auf einem Kreis um den Ursprung, die dann verbunden werden, um eine geschlossene Schleife, den Außenumriss der Uhr, zu bilden.

✨ Lösung prüfen und üben

Aktuelle Zeit abrufen und Positionen der Uhrzeiger berechnen

Wir werden die aktuelle Zeit abrufen und die Positionen der Uhrzeiger (Stunde, Minute und Sekunde) berechnen.

// 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;

Eine Variable namens rawtime wird definiert, um die von dem System abgerufen Daten der rohen Zeit zu speichern. time_t ist ein Datentyp, der üblicherweise verwendet wird, um die Anzahl der Sekunden in der Zeit von einem bestimmten Zeitpunkt (gewöhnlich dem 1. Januar 1970) bis zum gegenwärtigen Zeitpunkt darzustellen.

Definiert einen Zeiger auf eine tm-Struktur namens timeinfo. Die tm-Struktur wird verwendet, um die verschiedenen Komponenten der Zeit- und Datumsinformationen, wie Jahr, Monat, Tag, Stunde, Minute, Sekunde usw., zu speichern.

Verwendet die time-Funktion, um die aktuelle Systemzeit zu erhalten und die Sekunden der Zeit in der rawtime-Variable zu speichern. Die Anzahl der Sekunden, die von der time-Funktion zurückgegeben wird, stellt die Anzahl der Sekunden von einem bestimmten Zeitpunkt (gewöhnlich dem 1. Januar 1970, auch bekannt als UNIX-Timestamp) bis zur aktuellen Zeit dar.

Verwendet die localtime-Funktion, um die Anzahl der Sekunden, die von der time-Funktion erhalten wurden (gespeichert in der rawtime-Variable), in konkrete Zeit- und Datumsinformationen umzuwandeln und das Ergebnis in einer timeinfo-Struktur zu speichern. Die localtime-Funktion wandelt die Anzahl der Sekunden in Informationen wie Jahr, Monat, Tag, Stunde, Minute, Sekunde usw. um und speichert diese Informationen in der timeinfo-Struktur.

Ruft die aktuelle Anzahl der Stunden aus der timeinfo-Struktur ab und moduliert dann 12, um die Anzahl der Stunden auf eine 12-Stunden-Skala zu begrenzen, tm_hour gibt die Stunde an. Ruft die aktuelle Anzahl der Minuten aus der timeinfo-Struktur ab, tm_min gibt die Minute an. Ruft die aktuelle Anzahl der Sekunden aus der timeinfo-Struktur ab, tm_sec gibt die Sekunde an.

✨ Lösung prüfen und üben

Zeichnen des Stundenzeigers

Wir werden den Stundenzeiger auf der Uhr zeichnen.

// 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();

Zunächst sind Uhren normalerweise auf einer 12-Stunden-Skala, daher muss die Anzahl der Stunden in den Winkel auf der Uhrmacherfläche umgewandelt werden. Diese Berechnung beginnt mit der Zuordnung der Anzahl der Stunden von einer 12-Stunden-Skala zu einem 360-Grad-Winkelbereich. Anschließend wird der Beitrag der Minuten (durch die Position der Minutenzeigers) zum Stundenwinkel hinzugefügt. Der resultierende hourAngle repräsentiert den Winkel des Stundenzeigers in Bezug auf die 12-Uhr-Richtung.

Anschließend wird ein Element, in diesem Fall ein Liniensegment, definiert, das den Stundenzeiger darstellt. Der Startpunkt des Liniensegments befindet sich am Koordinatenpunkt (0, 0), dem Zentrum der Uhr. Der Endpunkt des Liniensegments wird mithilfe der trigonometrischen Funktionen cos und sin berechnet, um die Endposition des Stundenzeigers zu bestimmen. Die folgenden Minuten- und Sekundenzeiger werden ebenfalls auf diese Weise gezeichnet.

✨ Lösung prüfen und üben

Zeichnen des Minutenzeigers

Wir werden Code hinzufügen, um den Minutenzeiger auf der Uhr zu zeichnen.

// 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();

Die Uhrmacherfläche ist normalerweise 60 Minuten groß, daher muss die Anzahl der Minuten in den Winkel auf der Uhrmacherfläche umgewandelt werden. Diese Berechnung ordnet zunächst die Anzahl der Minuten einem 360-Grad-Winkelbereich zu und subtrahiert 90 Grad, um 0 Minuten der 12-Uhr-Richtung zuzuordnen. Der resultierende minuteAngle repräsentiert den Winkel des Minutenzeigers in Bezug auf die 12-Uhr-Richtung.

✨ Lösung prüfen und üben

Zeichnen des Sekundenzeigers

Wir werden Code hinzufügen, um den Sekundenzeiger auf der Uhr zu zeichnen.

// 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();

Uhren haben normalerweise 60 Sekunden, daher muss die Anzahl der Sekunden in den Winkel auf der Uhrmacherfläche umgewandelt werden. Diese Berechnung ordnet zunächst die Anzahl der Sekunden einem Winkelbereich von 360 Grad zu und subtrahiert 90 Grad, um 0 Sekunden der 12-Uhr-Richtung zuzuordnen. Der resultierende secondAngle repräsentiert den Winkel des Sekundenzeigers in Bezug auf die 12-Uhr-Richtung.

Schließlich werden wir die ursprüngliche Rotation wiederherstellen und die Uhranimation anzeigen.

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

glPopMatrix() wird verwendet, um den zuvor gespeicherten Matrixzustand wiederherzustellen, und glutSwapBuffers() wird verwendet, um das doppelt gepufferte Zeichnen abzuschließen und das gezeichnete Bild auf den Bildschirm zu rendern, um glatte Animationseffekte zu erzielen.

✨ Lösung prüfen und üben

Fenstergröße ändern und Echtzeitanzeige

Wir werden eine reshape-Funktion erstellen, die aufgerufen wird, wenn sich die Fenstergröße ändert.

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
}

Zweck dieser reshape-Funktion ist es, die OpenGL-Ansichtsoberfläche gemäß der neuen Fenstergröße anzupassen, wenn sich die Fenstergröße ändert, und die passende Projektionsmatrix festzulegen, um sicherzustellen, dass das gezeichnete Diagramm auch unter der neuen Fenstergröße richtig angezeigt wird.

Als nächstes definieren wir eine Funktion namens idle, um einige Operationen durchzuführen, wenn das Programm inaktiv ist.

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

Zweck dieser idle-Funktion ist es, glutPostRedisplay() aufzurufen, wenn das Programm inaktiv ist, um eine Neuzeichnung des Inhalts des Fensters zu verlangen, um die Inhalte des Fensters in Echtzeit zu animieren oder zu aktualisieren.

✨ Lösung prüfen und üben

Projekt kompilieren und ausführen

  1. Öffnen Sie Ihren Terminal und navigieren Sie zum Projektverzeichnis.
cd ~/project
  1. Kompilieren Sie den Code mit dem folgenden Befehl:
gcc -o clock_opengl clock_opengl.c -lGL -lglut -lGLU -lm
  1. Führen Sie die Anwendung aus:
./clock_opengl
Clock Opengl
✨ Lösung prüfen und üben

Zusammenfassung

In diesem Projekt haben Sie gelernt, wie Sie eine einfache Uhranimation mit OpenGL und GLUT erstellen. Wir haben die Einrichtung der Projektdateien, die Initialisierung des Fensters und von OpenGL, das Zeichnen des Uhrrands, das Drehen der Uhr, das Zeichnen des Uhrumrisses, das Abrufen der aktuellen Zeit und das Zeichnen der Uhrzeiger behandelt. Indem Sie diese Schritte befolgen, können Sie eine grundlegende Uhranimation erstellen, die die aktuelle Zeit auf visuell ansprechende Weise anzeigt.