games101——作业4

本文介绍了一种使用de Casteljau算法实现Bézier曲线的计算机图形学方法。通过递归细分控制点来精确绘制曲线,并进行了反走样处理以提高图像质量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


总览

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 算法说明如下:

  1. 考虑一个 p0, p1, … pn 为控制点序列的 Bézier 曲线。首先,将相邻的点连接
    起来以形成线段。
  2. 用 t : (1 − t) 的比例细分每个线段,并找到该分割点。
  3. 得到的分割点作为新的控制点序列,新序列的长度会减少一。
  4. 如果序列只包含一个点,则返回该点并终止。否则,使用新的控制点序列并转到步骤 1。使用 [0,1] 中的多个不同的 t 来执行上述算法,你就能得到相应的 Bézier 曲线。

开始编写

在本次作业中,你会在一个新的代码框架上编写,它比以前的代码框架小很多。和之前作业相似的是,你可以选择在自己电脑的系统或者虚拟机上完成作业。
请下载项目的框架代码,并使用以下命令像以前一样构建项目:

$ mkdir build
$ cd build
$ cmake . .
$ make

之后,你可以通过使用以下命令运行给定代码 ./BezierCurve。运行时,程序将打开一个黑色窗口。现在,你可以点击屏幕选择点来控制 Bézier 曲线。程
序将等待你在窗口中选择 4 个控制点,然后它将根据你选择的控制点来自动绘制 Bézier 曲线。代码框架中提供的实现通过使用多项式方程来计算 Bézier 曲线并绘制为红色。两张控制点对应的 Bézier 曲线如下所示:

在确保代码框架一切正常后,就可以开始完成你自己的实现了。注释掉 main 函数中 while 循环内调用 naive_bezier 函数的行,并取消对 bezier 函数的注释。要求你的实现将 Bézier 曲线绘制为绿色
如果要确保实现正确,请同时调用 naive_bezierbezier 函数,如果实现正确,则两者均应写入大致相同的像素,因此该曲线将表现为黄色。如果是这样,你可以确保实现正确。
你也可以尝试修改代码并使用不同数量的控制点,来查看不同的 Bézier 曲线。


作业代码

bezier 函数如下,主要从[0,1]迭代 t,代入 recursive_bezier 求出当前 t 对应的贝塞尔曲线上的点。

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 
    float t = 0.0f;
    for(int i=0; i<1000; ++i) {
        t += 0.001f;
        // recursive Bezier algorithm.
        cv::Point2f point = recursive_bezier(control_points, t);
        window.at<cv::Vec3b>(point.y, point.x)[1] = 255;
     }
}

recursive_bezier 函数如下,一开始是四个点,三条边,根据 t 选择三条边上的三个点,然后生成两条边,再根据 t 选择两边上的两个点,然后生成一条边,再根据 t 选择一条边上的一个点作为最后的选定的贝塞尔曲线上的点。

cv::Point2f recursive_bezier(const std::vector<cv::Point2f> &control_points, float t) 
{
    // TODO: Implement de Casteljau's algorithm
    std::vector<cv::Point2f> temp;
    std::vector<cv::Point2f> c_p(control_points);
    while(c_p.size() > 1) {
        for(int i=0; i<c_p.size()-1; ++i) {
            float distX = c_p[i+1].x - c_p[i].x;
            float distY = c_p[i+1].y - c_p[i].y;
            temp.push_back(cv::Point2f(c_p[i].x+t*distX, c_p[i].y+t*distY));
        }
        c_p.clear();
        for(auto x:temp) {
            c_p.push_back(x);
        }
        temp.clear();
    }

    return c_p[0];
}

结果如下
请添加图片描述
可以看出有明显走样。


提高作业

这里进行反走样操作,对于选中的点,判断其到周围像素中心点的哈夫曼距离,对于哈夫曼距离大于等于2的不产生影响,小于2的,其影响系数为 (2-hafuman_dist)/2。修改后的 bezier 函数如下

float get_hafuman_dist(int x, int y, cv::Point2f point)
{
    float x_center = (float)x + 0.5f;
    float y_center = (float)y + 0.5f;

    return fabs(point.x - x_center) + fabs(point.y - y_center);
}

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 
    float t = 0.0f;
    for(int i=0; i<1000; ++i) {
        t += 0.001f;
        // recursive Bezier algorithm.
        cv::Point2f point = recursive_bezier(control_points, t);
        int x = std::min(window.cols-1, std::max(0, (int)point.x));
        int y = std::min(window.rows-1, std::max(0, (int)point.y));
        int inc[3] = {0, -1, 1};
        for(int j=0; j<3; ++j)
            for(int k=0; k<3; ++k) {
                int new_x = std::min(window.cols-1, std::max(0, x+inc[j]));
                int new_y = std::min(window.rows-1, std::max(0, y+inc[k]));
                float dist = get_hafuman_dist(new_x, new_y, point);
                if(dist >= 2.0f)
                    continue;
                window.at<cv::Vec3b>(new_y, new_x)[1] = std::max(window.at<cv::Vec3b>(new_y, new_x)[1], (u_char)((2.0f-dist)*255/2.0f));
            }
    }
}

其结果明显比之前要好请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值