games101作业04:Bézier 曲线

games101作业04:Bézier 曲线

内容

Bézier 曲线是一种用于计算机图形学的参数曲线。在本次作业中,你需要实 de Casteljau 算法来绘制由 4 个控制点表示的 Bézier 曲线 (当你正确实现该算法时,你可以支持绘制由更多点来控制的 Bézier 曲线)。你需要修改的函数在提供的 main.cpp 文件中。

  1. bezier:该函数实现绘制 Bézier 曲线的功能。它使用一个控制点序列和一个OpenCV::Mat 对象作为输入,没有返回值。它会使 t 在 0 到 1 的范围内进行迭代,并在每次迭代中使 t 增加一个微小值。对于每个需要计算的 t,将调用另一个recursive_bezier,然后该函数将返回在Bézier 曲线上 t处的点。最后,将返回的点绘制在 OpenCV::Mat 对象上。
  2. recursive_bezier:该函数使用一个控制点序列和一个浮点数 t 作为输入,实现 de Casteljau 算法来返回 Bézier 曲线上对应点的坐标。、

基础内容

参考

代码

#include <chrono>
#include <iostream>
#include <opencv2/opencv.hpp>

std::vector<cv::Point2f> control_points;

void mouse_handler(int event, int x, int y, int flags, void *userdata) 
{
    if (event == cv::EVENT_LBUTTONDOWN && control_points.size() < 4) 
    {
        std::cout << "Left button of the mouse is clicked - position (" << x << ", "
        << y << ")" << '\n';
        control_points.emplace_back(x, y);
    }     
}

void naive_bezier(const std::vector<cv::Point2f> &points, cv::Mat &window) 
{
    auto &p_0 = points[0];
    auto &p_1 = points[1];
    auto &p_2 = points[2];
    auto &p_3 = points[3];

    for (double t = 0.0; t <= 1.0; t += 0.001) 
    {
        auto point = std::pow(1 - t, 3) * p_0 + 3 * t * std::pow(1 - t, 2) * p_1 +
                 3 * std::pow(t, 2) * (1 - t) * p_2 + std::pow(t, 3) * p_3;

        window.at<cv::Vec3b>(point.y, point.x)[2] = 255;
    }
}


int factorial(int x)
{
    int f;

    if (x == 0 || x == 1)
        f = 1;
    else
        f = factorial(x - 1) * x;

    return f;
}

cv::Point2f recursive_bezier(const std::vector<cv::Point2f> &control_points, float t) 
{
    // TODO: Implement de Casteljau's algorithm

    // 数学方法
    // int n = control_points.size();
    // cv::Point2f point = {0.0f, 0.0f};
    // for (int i = 0; i < n; i++)
    // {
    //     int c = factorial(n - 1) / (factorial(i) * factorial(n - 1 - i));
    //     point += c * std::pow(t, i) * std::pow(1 - t, n - 1 - i) * control_points[i];
    // }

    // 递归方法
    
    if(control_points.size() == 2)
    {
        return control_points[0] + t * (control_points[1] - control_points[0]);
    }

    std::vector<cv::Point2f> vec;
    for (int i = 0; i < control_points.size() - 1; i++)
    {
        vec.push_back(control_points[i] + t * (control_points[i + 1] - control_points[i]));
    }

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

}
//对BezierCurve进行插值
/*
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 (double t = 0.0; t <= 1.0; t += 0.001)
    {
        cv::Point2f point = recursive_bezier(control_points, t);
        window.at<cv::Vec3b>(point.y, point.x)[1] = 255;

        //anti-aliasing
        float x = point.x - std::floor(point.x);
        float y = point.y - std::floor(point.y);
        int x_flag = x < 0.5f ? -1 : 1;
        int y_flag = y < 0.5f ? -1 : 1;

        // 距离采样点最近的4个坐标点
        cv::Point2f p00 = cv::Point2f(std::floor(point.x) + 0.5f, std::floor(point.y) + 0.5f);
        cv::Point2f p01 = cv::Point2f(std::floor(point.x + x_flag * 1.0f) + 0.5f, std::floor(point.y) + 0.5f);
        cv::Point2f p10 = cv::Point2f(std::floor(point.x) + 0.5f, std::floor(point.y + y_flag * 1.0f) + 0.5f);
        cv::Point2f p11 = cv::Point2f(std::floor(point.x + x_flag * 1.0f) + 0.5f, std::floor(point.y + y_flag * 1.0f) + 0.5f);

        std::vector<cv::Point2f> vec;
        vec.push_back(p01);
        vec.push_back(p10);
        vec.push_back(p11);

        // 计算最近的坐标点与采样点距离
        cv::Point2f distance = p00 - point;
        float len = sqrt(distance.x * distance.x + distance.y * distance.y);

        // 对边缘点进行着色
        for(auto p:vec)
        {
            // 根据距离比, 计算边缘点影响系数 
            cv::Point2f d = p - point;
            float l = sqrt(d.x * d.x + d.y * d.y);
            float percnet = len / l;

            cv::Vec3d color = window.at<cv::Vec3b>(p.y, p.x);
            // 此处简单粗暴取最大值
            color[1] = std::max(color[1], (double)255 * percnet);
            window.at<cv::Vec3b>(p.y, p.x) = color;
        }
    }
}*/


int main() 
{
    cv::Mat window = cv::Mat(700, 700, CV_8UC3, cv::Scalar(0));
    cv::cvtColor(window, window, cv::COLOR_BGR2RGB);
    cv::namedWindow("Bezier Curve", cv::WINDOW_AUTOSIZE);

    cv::setMouseCallback("Bezier Curve", mouse_handler, nullptr);

    int key = -1;
    while (key != 27) 
    {
        for (auto &point : control_points) 
        {
            cv::circle(window, point, 3, {255, 255, 255}, 3);
        }

        if (control_points.size() == 4) 
        {
            //naive_bezier(control_points, window);
            bezier(control_points, window);

            cv::imshow("Bezier Curve", window);
            cv::imwrite("my_bezier_curve.png", window);
            key = cv::waitKey(0);

            return 0;
        }

        cv::imshow("Bezier Curve", window);
        key = cv::waitKey(20);
    }

return 0;
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在这部分的课程,我们将专注于使用光线追踪来渲染图像。在光线追踪 最重要的操作之一就是找到光线与物体的交点。一旦找到光线与物体的交点,就 可以执行着色并返回像素颜色。在这次作业,我们需要两个部分:光线的 生成和光线与三角的相交。本次代码框架的工作流程为: 1. 从 main 函数开始。我们定义场景的参数,添加物体(球体或三角形)到场景 ,并设置其材质,然后将光源添加到场景。 2. 调用 Render(scene) 函数。在遍历所有像素的循环里,生成对应的光线并将 返回的颜色保存在帧缓冲区(framebuffer)。在渲染过程结束后,帧缓冲 区的信息将被保存为图像。 3. 在生成像素对应的光线后,我们调用 CastRay 函数,该函数调用 trace 来 查询光线与场景最近的对象的交点。 4. 然后,我们在此交点执行着色。我们设置了三种不同的着色情况,并且已经 为你提供了代码。 你需要修改的函数是: • Renderer.cpp 的 Render():这里你需要为每个像素生成一条对应的光 线,然后调用函数 castRay() 来得到颜色,最后将颜色存储在帧缓冲区的相 应像素。 • Triangle.hpp 的 rayTriangleIntersect(): v0, v1, v2 是三角形的三个 顶点, orig 是光线的起点, dir 是光线单位化的方向向量。 tnear, u, v 是你需 要使用我们课上推导的 Moller-Trumbore 算法来更新的参数
zier曲线一种数学曲线,由法国数学家Pierre Bézier在20世纪60年代提出。它通过控制点和控制向量来定义曲线的形状。Bézier曲线的画线原理如下: 1. 控制点:Bézier曲线由两个或多个控制点组成。对于二次Bézier曲线,有三个控制点,分别称为起点、控制点和终点。对于三次Bézier曲线,有四个控制点,分别称为起点、两个控制点和终点。 2. 连接直线:Bézier曲线的起点和终点之间可以通过直线段连接。这些直线段被称为端点切线。如果起点和终点之间存在多个控制点,则通过这些控制点来调整曲线的形状。 3. 控制向量:每个控制点都有一个关联的控制向量。控制向量定义了曲线在该控制点处的切线方向和长度。调整控制向量的长度和方向可以改变曲线在该位置的弯曲程度和形状。 4. 插值计算:根据Bézier曲线的插值计算公式,通过控制点和控制向量的组合,可以计算出曲线上的每个点的坐标。这些计算基于参数t(取值范围为0到1),通过在控制点之间进行插值计算来确定曲线上的点。 5. 平滑性:Bézier曲线的平滑性由控制点和控制向量的位置关系决定。当控制向量与端点切线相切时,曲线在该位置处是平滑的。通过调整控制向量的位置,可以获得不同程度的平滑性。 总结来说,Bézier曲线通过控制点和控制向量来定义曲线的形状,并使用插值计算来确定曲线上的点。调整控制点和控制向量的位置和长度可以改变曲线的形状和平滑性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值