Programming lesson
From Photo to Sketch: Mastering Chaikin and Bézier Curves in C++ for CMSI 281/371
Learn how to implement Chaikin and Bézier curve algorithms in C++ to generate cartoon sketches from photos. This tutorial covers generate_points, draw_curve, and multi-iteration smoothing with practical examples and OpenGL integration.
Introduction to Curve Algorithms in Computer Graphics
In computer graphics, curves are essential for modeling smooth shapes. Two popular techniques are the Chaikin curve algorithm and the Bézier curve algorithm. Both generate a smooth curve from a set of control points. The Chaikin algorithm works by cutting corners iteratively, while Bézier uses Bernstein polynomials. In this tutorial, you'll implement these algorithms in C++ for a CMSI 281/371 assignment, turning a headshot photo into a cartoon sketch.
Setting Up the Skeleton Code
You are provided with a Vertex class to store x and y coordinates. The skeleton uses a std::vector<Vertex> to hold control points. Key functions to implement: generate_points and draw_curve. The n_iter parameter controls how many times the algorithm runs, producing finer curves each iteration.
Implementing generate_points for Chaikin Curves
The Chaikin algorithm refines a polygon by inserting new points at 1/4 and 3/4 of each edge, then removing the original vertices. For a closed curve, connect the last point to the first. Pseudocode:
vector<Vertex> generate_points(vector<Vertex> pts) {
vector<Vertex> newPts;
for (size_t i = 0; i < pts.size(); i++) {
size_t j = (i + 1) % pts.size(); // wrap around
Vertex p1 = { 0.75*pts[i].x + 0.25*pts[j].x, 0.75*pts[i].y + 0.25*pts[j].y };
Vertex p2 = { 0.25*pts[i].x + 0.75*pts[j].x, 0.25*pts[i].y + 0.75*pts[j].y };
newPts.push_back(p1);
newPts.push_back(p2);
}
return newPts;
}For Bézier curves, use the de Casteljau algorithm: recursively split the control polygon at t=0.5.
Implementing draw_curve
This function calls generate_points repeatedly for n_iter iterations, then connects the resulting points with line segments. Example:
void draw_curve(vector<Vertex> pts, int n_iter) {
for (int i = 0; i < n_iter; i++) {
pts = generate_points(pts);
}
glBegin(GL_LINE_STRIP);
for (Vertex v : pts) {
glVertex2f(v.x, v.y);
}
glEnd();
}For closed curves, use GL_LINE_LOOP.
Trend Example: Smoothing like AI Filters
Just as AI apps like TikTok's cartoon filter smooth facial features, Chaikin and Bézier curves smooth jagged control points into elegant lines. Think of each iteration as a 'filter' that refines the sketch, similar to how stable diffusion models generate art from rough prompts.
Testing with a Headshot
Plot control points around the photo's features (eyes, nose, mouth). Run 3-5 iterations. The more iterations, the smoother the curve, but too many may oversmooth details. Aim for a balance that captures the likeness, like Ed Sheeran's curly hair and beard.
Common Pitfalls
- Forgetting to handle the closed curve wrap-around in
generate_points. - Using
GL_LINE_STRIPinstead ofGL_LINE_LOOPfor closed shapes. - Not converting degrees to radians for rotation in later assignments.
Extending to 3D: Cube Rotation
In assignment 2, you apply rotation matrices to a cube. The same generate_points logic extends to 3D homogeneous coordinates. Implement rotation_matrix_x, rotation_matrix_y, and rotation_matrix_z using the standard rotation formulas. Use deg2rad to convert the global theta.
Hierarchical Modeling with Prisms
Assignment 3 builds prisms from planes. Start with init_plane to create a unit square, then extrude into a cube. Use transformations to assemble objects like a chair or table. The mat_mult function multiplies a transformation matrix by the entire points matrix for efficiency.
Conclusion
Mastering curve algorithms and 3D transformations in C++ is crucial for computer graphics. By implementing Chaikin and Bézier curves, you gain skills used in vector graphics, animation, and game development. Practice with different control points and iterations to see how curves evolve, just like how generative AI creates art from simple inputs.