Programming lesson
Chaikin- und Bézier-Kurven in C++: So erstellst du Cartoon-Skizzen wie ein Profi
Lerne, wie du mit Chaikin- und Bézier-Algorithmen in C++ aus einem Foto eine cartoonartige Skizze erstellst – inklusive 3D-Rotation und hierarchischer Modellierung. Perfekt für CMSI 281/371.
Chaikin- und Bézier-Kurven in C++: So erstellst du Cartoon-Skizzen wie ein Profi
Stell dir vor, du könntest aus einem einfachen Selfie eine stylische Cartoon-Skizze zaubern – genau das ermöglichen dir die Chaikin- und Bézier-Kurvenalgorithmen. In diesem Tutorial zeige ich dir, wie du in C++ mit diesen Algorithmen arbeitest, um aus einem Foto eine künstlerische Linienzeichnung zu generieren. Wir nutzen dabei OpenGL für die Darstellung und bauen Schritt für Schritt die Funktionen auf, die du für deine CMSI 281/371 Assignment 1-4 Lösungen benötigst.
Dieses Tutorial ist wie ein Trend-Check für dein Programmier-Wissen: So wie TikTok-Filter Gesichter in Echtzeit verwandeln, verwandelst du hier mit Code ein Porträt in eine Cartoon-Version. Keine Sorge, wir gehen alles im Detail durch – von den mathematischen Grundlagen bis zur praktischen Implementierung.
Was sind Chaikin- und Bézier-Kurven?
Bézier-Kurven sind parametrische Kurven, die durch Kontrollpunkte definiert werden. Stell sie dir wie einen Gaming-Charakter-Editor vor: Du setzt ein paar Punkte, und der Algorithmus berechnet eine glatte Kurve dazwischen. Chaikin-Kurven hingegen sind eine spezielle Form von Bézier-Kurven, die durch wiederholtes „Schneiden und Verbinden“ von Liniensegmenten entstehen. Je mehr Iterationen du durchführst, desto glatter wird die Kurve – perfekt, um aus einem groben Polygon eine weiche Linie zu machen.
In der Praxis bedeutet das: Du zeichnest ein paar Kontrollpunkte entlang der Konturen eines Gesichts (z.B. von Ed Sheeran), und der Algorithmus generiert daraus eine geschlossene oder offene Kurve, die das Gesicht cartoonartig nachzeichnet. Das ist nicht nur für Assignments spannend, sondern auch für KI-gestützte Kunst-Apps oder Fintech-Visualisierungen, wo glatte Kurven Daten verschönern.
Deine Aufgaben im Überblick
Im Assignment 1 geht es um die Implementierung von zwei Kernfunktionen: generate_points und draw_curve. Du bekommst einen Satz Kontrollpunkte (als std::vector) und musst daraus neue Punkte berechnen. Der Parameter n_iter bestimmt, wie oft der Algorithmus durchlaufen wird. Je mehr Iterationen, desto glatter das Ergebnis – aber auch mehr Rechenzeit.
Für Assignment 2 erweiterst du das Ganze um 3D-Rotation: Du hast einen Würfel, den du um die x-, y- oder z-Achse drehen kannst. Hier lernst du, wie man mit homogenen Koordinaten arbeitet und Rotationsmatrizen erstellt. Das ist wie ein 360-Grad-Produktviewer in einem Onlineshop – nur dass du den Code selbst schreibst.
Assignment 3 und 4 gehen noch tiefer: Hier baust du aus Grundelementen (z.B. Quadraten) komplexe Szenen, indem du hierarchische Modellierung nutzt. Du erstellst einen Würfel, transformierst ihn und setzt daraus Objekte wie einen Tisch oder ein Haus zusammen. Das erinnert an Minecraft-Building, nur mit Matrizen statt Blöcken.
Schritt-für-Schritt zur Cartoon-Skizze
Lass uns mit der generate_points-Funktion für Chaikin-Kurven beginnen. Der Algorithmus funktioniert so: Für jedes Liniensegment zwischen zwei Kontrollpunkten P0 und P1 berechnest du zwei neue Punkte Q und R:
- Q = 0.75 * P0 + 0.25 * P1
- R = 0.25 * P0 + 0.75 * P1
Diese neuen Punkte ersetzt du in der Liste. Nach einer Iteration hast du doppelt so viele Punkte, aber die Kurve ist schon glatter. Nach mehreren Iterationen entsteht eine weiche Kurve.
Hier ein Code-Ausschnitt für Chaikin:
std::vector<Vertex> generate_points(const std::vector<Vertex>& control_points) {
std::vector<Vertex> new_points;
for (size_t i = 0; i < control_points.size() - 1; ++i) {
Vertex p0 = control_points[i];
Vertex p1 = control_points[i+1];
Vertex q, r;
q.x = 0.75f * p0.x + 0.25f * p1.x;
q.y = 0.75f * p0.y + 0.25f * p1.y;
r.x = 0.25f * p0.x + 0.75f * p1.x;
r.y = 0.25f * p0.y + 0.75f * p1.y;
new_points.push_back(q);
new_points.push_back(r);
}
// Für geschlossene Kurven: letzten Punkt mit erstem verbinden
Vertex p0 = control_points.back();
Vertex p1 = control_points.front();
// ... ähnlich
return new_points;
}Für Bézier-Kurven verwendest du das De-Casteljau-Verfahren. Du interpolierst schrittweise zwischen den Kontrollpunkten. Der Vorteil: Du kannst die Kurve exakt steuern, auch wenn die Anzahl der Punkte variiert.
Die draw_curve-Funktion ruft dann generate_points mehrfach auf und verbindet die resultierenden Punkte mit Linien. So entsteht die fertige Skizze.
Von 2D zu 3D: Rotation und homogene Koordinaten
Im zweiten Assignment geht es in die dritte Dimension. Du bekommst einen Würfel, der aus 8 Eckpunkten besteht. Deine Aufgabe: Implementiere Rotationsmatrizen für x-, y- und z-Achse und wende sie auf die Punkte an. Dazu musst du die kartesischen Koordinaten (x, y, z) in homogene Koordinaten (x, y, z, 1) umwandeln, damit du die 4x4-Matrix multiplizieren kannst. Das ist wie ein AR-Filter, der ein Objekt im Raum dreht – nur dass du die Mathematik selbst berechnest.
Hier ein Beispiel für die Rotationsmatrix um die y-Achse:
std::vector<std::vector<GLfloat>> rotation_matrix_y(GLfloat theta) {
GLfloat rad = deg2rad(theta);
std::vector<std::vector<GLfloat>> mat = {
{cos(rad), 0, sin(rad), 0},
{0, 1, 0, 0},
{-sin(rad), 0, cos(rad), 0},
{0, 0, 0, 1}
};
return mat;
}Die mat_mult-Funktion multipliziert dann die Transformationsmatrix mit der gesamten Punktematrix – effizienter als Punkt für Punkt.
Hierarchische Modellierung: Aus Quadraten werden Welten
In den späteren Assignments baust du aus einem einfachen Quadrat einen Würfel, und aus Würfeln komplexe Szenen. Das Prinzip: Du definierst ein Grundelement (z.B. ein Quadrat), transformierst es (verschieben, drehen, skalieren) und setzt es zu größeren Objekten zusammen. Das ist genau wie in 3D-Design-Tools wie Blender oder Game-Engines wie Unity – nur dass du alles selbst programmierst.
Ein Tipp: Verwende eine build_cube-Funktion, die aus sechs Quadraten einen Würfel erstellt. Dann kannst du diesen Würfel beliebig oft klonen und in der Szene platzieren. Vergiss nicht, nach dem Rendern den Speicher freizugeben, den vector2array allokiert hat – sonst gibt es Memory Leaks.
Praktische Tipps für dein Assignment
- Kompilierung: Nutze den Befehl
gcc -o assignment1 assignment1.cpp -std=c++14 -lGL -lGLU -lglut. Achte darauf, dass keine Fehler auftreten – 80% der Punkte hängen von der Korrektheit ab. - Skizze: Für die 20% Kreativität solltest du dir Mühe geben, das Gesicht erkennbar zu zeichnen. Ein einfacher Smiley reicht nicht – versuche, die charakteristischen Züge (z.B. Ed Sheerans Bart oder Brille) nachzubilden.
- Abgabe: Vergiss nicht, sowohl den Code als auch die resultierende Skizze und das Originalfoto einzureichen. Die Dateinamen müssen exakt stimmen:
assignment1.cpp,results.jpeg,photo.jpeg.
Trends und Anwendungen
Warum ist das relevant? KI-Kunstgeneratoren wie DALL·E oder Midjourney nutzen ähnliche Kurvenalgorithmen, um glatte Linien zu erzeugen. Auch in der Datenvisualisierung werden Bézier-Kurven verwendet, um Trends in Finanzdaten darzustellen. Und in der Spieleentwicklung sind sie die Grundlage für Charakteranimationen und Umgebungen. Mit diesem Wissen bist du bestens gerüstet für die nächste Coding-Challenge – egal ob fürs Studium oder für dein nächstes Hackathon-Projekt.
Fazit
Chaikin- und Bézier-Kurven sind mächtige Werkzeuge, um aus groben Punkten elegante Linien zu zaubern. Mit den gezeigten Funktionen und ein wenig Übung wirst du im Handumdrehen Cartoon-Skizzen erstellen können. Denk daran: Übung macht den Meister – also setz dich ran und probier es aus!