总览

  • 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 曲线上对应点的坐标。

实现

  • naive_bezier

    • 数学公式

    • 代码

      void AActor_BezierCuve::naive_bezier()
      {
      FVector& p_0 = m_points[0];
      FVector& p_1 = m_points[1];
      FVector& p_2 = m_points[2];
      FVector& p_3 = m_points[3];
      FVector& p_4 = m_points[4];
      for (double t = 0.0; t <= 1.0; t += 0.001)
      {
      auto point = std::pow(1 - t, 4) * p_0 + 4 * t * std::pow(1 - t, 3) * p_1 +
      6 * std::pow(t, 2) * std::pow((1 - t), 2) * p_2 + 4 * std::pow(t, 3) * (1 - t) * p_3 + std::pow(t, 4) * p_4;
      DrawDebugPoint(GetWorld(), point, 2.0f, FColor::Green,true,5.0f);
      //UKismetSystemLibrary::PrintString(GetWorld(), point.ToString());
      } }
  • recursive_bezier

    • De Casteljau 算法说明如下:

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

      void AActor_BezierCuve::bezier()
      {
      for (double t = 0.0; t <= 1.0; t += 0.001)
      {
      FVector point = recursive_bezier(m_points, t);
      DrawDebugPoint(GetWorld(), point, 2.0f, FColor(10,214,255,255),true,5.0f);
      }
      } // De Casteljau 算法,递归
      FVector AActor_BezierCuve::recursive_bezier(TArray<FVector>& points, float t)
      {
      if (points.Num() < 3) {
      return (1 - t) * points[0] + t * points[1];
      } TArray<FVector> newPoint;
      for (int i = 0; i < points.Num() - 1; i++) {
      newPoint.Add((1 - t) * points[i] + t * points[i + 1]);
      }
      return recursive_bezier(newPoint, t);
      }
  • 最终效果

附录

所有代码

  • Actor_BezierCuve.h

    点击查看代码
      ```cpp
    UCLASS()
    class GAMES101_API AActor_BezierCuve : public AActor
    {
    GENERATED_BODY() public:
    // Sets default values for this actor's properties
    AActor_BezierCuve(); protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override; public:
    // Called every frame
    virtual void Tick(float DeltaTime) override; UFUNCTION(BlueprintCallable)
    void naive_bezier(); UFUNCTION(BlueprintCallable)
    void bezier(); UFUNCTION(BlueprintCallable)
    FVector recursive_bezier(TArray<FVector>& points,float t); public:
    UPROPERTY(VisibleAnywhere)
    USceneComponent* root;
    UPROPERTY(VisibleAnywhere)
    UStaticMeshComponent* point0;
    UPROPERTY(VisibleAnywhere)
    UStaticMeshComponent* point1;
    UPROPERTY(VisibleAnywhere)
    UStaticMeshComponent* point2;
    UPROPERTY(VisibleAnywhere)
    UStaticMeshComponent* point3;
    UPROPERTY(VisibleAnywhere)
    UStaticMeshComponent* point4; UPROPERTY();
    TArray<FVector> m_points; UPROPERTY(EditAnywhere);
    bool m_bUseRecursiveBezier; };
    ```
  • AActor_BezierCuve.cpp

    点击查看代码
    #include "Actor_BezierCuve.h"
    #include "DrawDebugHelpers.h"
    #include <cmath>
    #include "Kismet/KismetSystemLibrary.h" // Sets default values
    AActor_BezierCuve::AActor_BezierCuve()
    {
    // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;
    root = CreateDefaultSubobject<USceneComponent>(TEXT("root"));
    SetRootComponent(root); point0 = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("point0"));
    point1 = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("point1"));
    point2 = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("point2"));
    point3 = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("point3"));
    point4 = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("point4")); point0->SetupAttachment(root);
    point1->SetupAttachment(root);
    point2->SetupAttachment(root);
    point3->SetupAttachment(root);
    point4->SetupAttachment(root);
    m_points.Init(FVector::ZeroVector, 5); m_bUseRecursiveBezier = false;
    } // Called when the game starts or when spawned
    void AActor_BezierCuve::BeginPlay()
    {
    Super::BeginPlay();
    m_points[0] = point0->GetComponentLocation();
    m_points[1] = point1->GetComponentLocation();
    m_points[2] = point2->GetComponentLocation();
    m_points[3] = point3->GetComponentLocation();
    m_points[4] = point4->GetComponentLocation(); if (!m_bUseRecursiveBezier)
    naive_bezier();
    else
    bezier();
    } // Called every frame
    void AActor_BezierCuve::Tick(float DeltaTime)
    {
    Super::Tick(DeltaTime); } // 多项式
    void AActor_BezierCuve::naive_bezier()
    {
    FVector& p_0 = m_points[0];
    FVector& p_1 = m_points[1];
    FVector& p_2 = m_points[2];
    FVector& p_3 = m_points[3];
    FVector& p_4 = m_points[4];
    for (double t = 0.0; t <= 1.0; t += 0.001)
    {
    auto point = std::pow(1 - t, 4) * p_0 + 4 * t * std::pow(1 - t, 3) * p_1 +
    6 * std::pow(t, 2) * std::pow((1 - t), 2) * p_2 + 4 * std::pow(t, 3) * (1 - t) * p_3 + std::pow(t, 4) * p_4;
    DrawDebugPoint(GetWorld(), point, 2.0f, FColor::Green,true,5.0f);
    //UKismetSystemLibrary::PrintString(GetWorld(), point.ToString());
    } } void AActor_BezierCuve::bezier()
    {
    for (double t = 0.0; t <= 1.0; t += 0.001)
    {
    FVector point = recursive_bezier(m_points, t);
    DrawDebugPoint(GetWorld(), point, 2.0f, FColor(10,214,255,255),true,5.0f);
    }
    } // De Casteljau 算法,递归
    FVector AActor_BezierCuve::recursive_bezier(TArray<FVector>& points, float t)
    {
    if (points.Num() < 3) {
    return (1 - t) * points[0] + t * points[1];
    } TArray<FVector> newPoint;
    for (int i = 0; i < points.Num() - 1; i++) {
    newPoint.Add((1 - t) * points[i] + t * points[i + 1]);
    }
    return recursive_bezier(newPoint, t);
    }

【UE4】GAMES101 图形学作业4:贝塞尔曲线的更多相关文章

  1. 【UE4】GAMES101 图形学作业2:光栅化和深度缓存

    总览 在上次作业中,虽然我们在屏幕上画出一个线框三角形,但这看起来并不是那么的有趣.所以这一次我们继续推进一步--在屏幕上画出一个实心三角形,换言之,栅格化一个三角形.上一次作业中,在视口变化之后,我 ...

  2. 【UE4】GAMES101 图形学作业5:光线与物体相交(球、三角面)

    总览 在这部分的课程中,我们将专注于使用光线追踪来渲染图像.在光线追踪中最重要的操作之一就是找到光线与物体的交点.一旦找到光线与物体的交点,就可以执行着色并返回像素颜色. 在这次作业中,我们要实现两个 ...

  3. 【UE4】GAMES101 图形学作业3:Blinn-Phong 模型与着色

    总览 在这次编程任务中,我们会进一步模拟现代图形技术.我们在代码中添加了Object Loader(用于加载三维模型), Vertex Shader 与Fragment Shader,并且支持了纹理映 ...

  4. 【UE4】GAMES101 图形学作业1:mvp 模型、视图、投影变换

    总览 到目前为止,我们已经学习了如何使用矩阵变换来排列二维或三维空间中的对象.所以现在是时候通过实现一些简单的变换矩阵来获得一些实际经验了.在接下来的三次作业中,我们将要求你去模拟一个基于CPU 的光 ...

  5. 【UE4】GAMES101 图形学作业0:矩阵初识

    作业描述 给定一个点P=(2,1), 将该点绕原点先逆时针旋转45◦,再平移(1,2), 计算出变换后点的坐标(要求用齐次坐标进行计算). UE4 知识点 主要矩阵 FMatrix FBasisVec ...

  6. 计算机图形学:贝塞尔曲线(Bezier Curve)

    计算机图形学:贝塞尔曲线(Bezier Curve) 贝塞尔能由贝塞尔样条组合而成,也可产生更高维的贝塞尔曲面.

  7. Unity3d游戏中自定义贝塞尔曲线编辑器[转]

    关于贝塞尔曲线曲线我们再前面的文章提到过<Unity 教程之-在Unity3d中使用贝塞尔曲线>,那么本篇文章我们来深入学习下,并自定义实现贝塞尔曲线编辑器,贝塞尔曲线是最基本的曲线,一般 ...

  8. Android 自定义View高级特效,神奇的贝塞尔曲线

    效果图 效果图中我们实现了一个简单的随手指滑动的二阶贝塞尔曲线,还有一个复杂点的,穿越所有已知点的贝塞尔曲线.学会使用贝塞尔曲线后可以实现例如QQ红点滑动删除啦,360动态球啦,bulabulabul ...

  9. 贝塞尔曲线:原理、自定义贝塞尔曲线View、使用!!!

    一.原理 转自:http://www.2cto.com/kf/201401/275838.html Android动画学习Demo(3) 沿着贝塞尔曲线移动的Property Animation Pr ...

随机推荐

  1. Tars | 第3篇 Tars中期汇报测试文档(Java语言实现Subset路由规则)

    目录 前言 1. 任务介绍 2. 测试模拟方案 2.0 *前置工作 2.1 添加路由规则 2.2 添加存活节点 2.3 [输出]遍历输出当前存活节点 2.4 [核心]对存活节点按subset规则过滤 ...

  2. WEB漏洞——XSS

    跨站脚本( Cross-site Scripting,简称为XSS或跨站脚本或跨站脚本攻击)是一种针对网站应用程序的安全漏洞攻击技术,是代码注入的一种. XSS攻击可以分为三种:反射型.存储型和DOM ...

  3. java 使用匿名内部类实现多线程的创建

    匿名内部类的作用:简化代码 把子类继承父类,重写父类的方法,创建子类对象合一步完成 把实现类实现类接口,重写接口中的方法,创建实现类对象合成一步完成 匿名内部类的最终产物:子类/实现类对象,而这个类没 ...

  4. [第十五篇]——Swarm 集群管理之Spring Cloud直播商城 b2b2c电子商务技术总结

    Swarm 集群管理 简介 Docker Swarm 是 Docker 的集群管理工具.它将 Docker 主机池转变为单个虚拟 Docker 主机. Docker Swarm 提供了标准的 Dock ...

  5. Docker安装GitLab与Runner(网关),常规设置,自动化用到k8s+token

    [转]图文详解k8s自动化持续集成之GitLab CI/CD Windows里面使用Debian命令行工具完成 和Docker网络相关的命令 查看某一个容器的网络 docker inspect 容器I ...

  6. 干货!基于SpringBoot的RabbitMQ多种模式队列实战

    目录 环境准备 安装RabbitMQ 依赖 连接配置 五种队列模式实现 1 点对点的队列 2 工作队列模式Work Queue 3 路由模式Routing 4 发布/订阅模式Publish/Subsc ...

  7. Linux从头学13:想彻底搞懂“系统调用”的底层原理?建议您别错过这篇【调用门】

    作 者:道哥,10+年嵌入式开发老兵,专注于:C/C++.嵌入式.Linux. 关注下方公众号,回复[书籍],获取 Linux.嵌入式领域经典书籍:回复[PDF],获取所有原创文章( PDF 格式). ...

  8. PHP脚本设置及获取进程名

    今天来学习的是两个非常简单的函数,一个可以用来设置我们执行脚本时运行的进程名.而另一个就是简单的获取当前运行的进程名.这两个函数对于大量的脚本运行代码有很大的作用,比如我们需要 kill 掉某个进程时 ...

  9. Linux系列(42) - 防火墙相关命令

    # 开启 service firewalld start # 重启 service firewalld restart # 关闭 service firewalld stop # 查看防火墙规则 fi ...

  10. Flutter 对状态管理的认知与思考

    前言 由 编程技术交流圣地[-Flutter群-] 发起的 状态管理研究小组,将就 状态管理 相关话题进行为期 两个月 的讨论. 目前只有内定的 5 个人参与讨论,如果你对 状态管理 有什么独特的见解 ...