games 101 作业4

games 101 作业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 算法说明如下:

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

提升部分
实现对 Bézier 曲线的反走样。(对于一个曲线上的点,不只把它对应于一个像
素,你需要根据到像素中心的距离来考虑与它相邻的像素的颜色。)

题解

本次作业的难度适中,用递归的方式实现n次贝塞尔曲线。

根据算法说明以及贝塞尔函数的定义,实现如下算法:给定若干个点和t,返回这若干个控制点构成的贝塞尔曲线中t时刻的点。

cv::Point2f recursive_bezier(const std::vector<cv::Point2f> &control_points, float t) 
{
    if (control_points.size() == 1)
        return control_points[0];
    std::vector<cv::Point2f> new_control_points;
    for (int i = 0; i < control_points.size() -1; i++)
    {
        cv::Point2f p = t * control_points[i] + (1 - t) * control_points[i + 1];
        new_control_points.emplace_back(p);
    }
    return recursive_bezier(new_control_points,t);
}

然后t 从0到1,步进值可自定义,本代码中设置为0.001,也就是一条贝塞尔由1001个点组成,每个点的G通道设置为255,也就是线条颜色为绿色。

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.

    int neighborXIndex[8] = { 0,1,0,-1,-1,1,-1,1};
    int neighborYIndex[8] = { 1,0,-1,0.1,1,-1,-1};
    for (double t = 0.0; t <= 1.0; t += 0.001)
    {
        auto point = recursive_bezier(control_points,t);
        window.at<cv::Vec3b>(point.y, point.x)[1] = 255;
    }
}

提升:抗锯齿
根据点的位置到所在像素中心点距离,以及到相邻像素中心点的距离,计算相邻像素的颜色值。(可以是四邻或者八邻,下面以四邻距离说明)
在这里插入图片描述
假设当前像素Q点的颜色值为 c o l o r Q color_{Q} colorQ
则ABCD的颜色值分别为
c o l o r A = ∣ P Q ∣ / ∣ P A ∣ ∗ c o l o r Q color_{A}=|PQ|/|PA| * color_{Q} colorA=PQ∣/∣PAcolorQ
c o l o r B = ∣ P Q ∣ / ∣ P B ∣ ∗ c o l o r Q color_{B}=|PQ|/|PB| * color_{Q} colorB=PQ∣/∣PBcolorQ
c o l o r C = ∣ P Q ∣ / ∣ P C ∣ ∗ c o l o r Q color_{C}=|PQ|/|PC| * color_{Q} colorC=PQ∣/∣PCcolorQ
c o l o r D = ∣ P Q ∣ / ∣ P D ∣ ∗ c o l o r Q color_{D}=|PQ|/|PD| * color_{Q} colorD=PQ∣/∣PDcolorQ
代码如下:

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.

    int neighborXIndex[4] = { 0,1,0,-1};
    int neighborYIndex[4] = { 1,0,-1,0};
    for (double t = 0.0; t <= 1.0; t += 0.001)
    {
        auto point = recursive_bezier(control_points,t);
        window.at<cv::Vec3b>(point.y, point.x)[1] = 255;
        int x = point.x;
        int y = point.y;
        cv::Point2f p1(x + 0.5, y + 0.5);
        float d1 = std::sqrt(std::pow(point.x - p1.x, 2) + std::pow(point.y - p1.y, 2));

        for (int i = 0; i < 4; i++)
        {
            cv::Point2f p(x+ neighborXIndex[i]*0.5,y+ neighborYIndex[i]*0.5);
            float d = std::sqrt(std::pow(point.x - p.x, 2) + std::pow(point.y - p.y, 2));
            window.at<cv::Vec3b>(y + neighborYIndex[i], x + neighborXIndex[i])[1] = d1 / d * 255;
        }
    }

}

总结
贝塞尔函数的定义比较简单,n次贝塞尔的函数的定义公式如下:
B ( t ) = ∑ i = 0 n ( n i ) P i ( 1 − t ) n − i 1 t i B(t)=\sum_{i=0}^n\binom{n}{i} P_i(1-t)^{n-i_1} t^i B(t)=i=0n(in)Pi(1t)ni1ti
所以也可以不用递归,而是通过这个公式实现N次贝塞尔函数。
一条贝塞尔打断成两端贝塞尔,同时保持断点初过渡平滑,可以参考博文

作业答案

本次作业的答案放在的git仓库中:作业地址

  • 16
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xhh-cy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值