games101 作业4
-
任务说明
Bézier 曲线是一种用于计算机图形学的参数曲线。在本次作业中,你需要实现 de Casteljau 算法来绘制由 4 个控制点表示的 Bézier 曲线 (当你正确实现该算法时,你可以支持绘制由更多点来控制的 Bézier 曲线)。你需要修改的函数在提供的 main.cpp 文件中。
• bezier:该函数实现绘制 Bézier 曲线的功能。它使用一个控制点序列和一个OpenCV::Mat 对象作为输入,没有返回值。它会使 t 在 0 到 1 的范围内进行迭代,并在每次迭代中使 t 增加一个微小值。对于每个需要计算的 t,将调用另一个函数 recursive_bezier,然后该函数将返回在 Bézier 曲线上 t处的点。最后,将返回的点绘制在 OpenCV ::Mat 对象上。
• recursive_bezier:该函数使用一个控制点序列和一个浮点数 t 作为输入,实现 de Casteljau 算法来返回 Bézier 曲线上对应点的坐标。 -
算法说明
De Casteljau 算法说明如下:- 考虑一个 p0 , p1 , … pn 为控制点序列的 Bézier 曲线。首先,将相邻的点连接起来以形成线段。
- 用 t : (1 − t) 的比例细分每个线段,并找到该分割点。
- 得到的分割点作为新的控制点序列,新序列的长度会减少一。
- 如果序列只包含一个点,则返回该点并终止。否则,使用新的控制点序列并转到步骤 1。使用 [0,1] 中的多个不同的 t 来执行上述算法,你就能得到相应的 Bézier 曲线。
代码
首先是recursive_bezier函数
cv::Point2f recursive_bezier(const std::vector<cv::Point2f> &control_points, float t)
{
// TODO: Implement de Casteljau's algorithm
/*
//如果序列只包含一个点,则该点返回并中止
if(control_points.size() ==0){
return control_points[0];
}
//先得到四个控制点的位置
cv::Point2f p0 = control_points[0];
cv::Point2f p1 = control_points[1];
cv::Point2f p2 = control_points[2];
cv::Point2f p3 = control_points[3];
//然后得到四个控制点连成的三条线段的三个分割点
cv::Point2f p10 = (1 - t) * p0 + t * p1;
cv::Point2f p11 = (1 - t) * p1 + t * p2;
cv::Point2f p12 = (1 - t) * p2 + t * p3;
//得到三个分割点连成的两条线段的两个分割点
cv::Point2f p20 = (1 - t) * p10 + t * p11;
cv::Point2f p21 = (1 - t) * p11 + t * p12;
//得到两个分割点连成的一条线段的一个分割点
cv::Point2f p30 = (1 - t) * p20 + t * p21;
*/
//从上面的逐步分析可以直接推倒并简化成递归形式,具体算法如下:
if(control_points.size() == 1){
return control_points[0];
}
std::vector<cv::Point2f> myPoint;
for (int i = 0; i < control_points.size() - 1;i++){
myPoint.push_back((1 - t) * control_points[i] + t * control_points[i+1]);
}
//最后返回最后一条线段上的分割点
return recursive_bezier(myPoint, t);
}
然后是bezier函数:
void bezier(const std::vector<cv::Point2f> &control_points, cv::Mat &window)
{
// TODO: Iterate through all t = 0 to t = 1 with small steps, and call de Casteljau's
// recursive Bezier algorithm.
for (float i = 0; i < 1;i+=0.0001){
cv::Point2f point = recursive_bezier(control_points, i);
//因为PDF中要求将贝塞尔曲线绘制成绿色的,所以将RGB中的G通道设置成255
window.at<cv::Vec3b>(point.y, point.x)[1] = 255;
}
}
注意,在调用我们自己写的bezier函数的时候需要,将main函数中的naive_bezier函数那行注释掉,加上我们自己的beizer函数调用。
调用naive_bezier函数的效果图如下:
调用beizer函数的效果图如下: