【UE4】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 曲线上对应点的坐标。
实现
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 算法说明如下:
- 考虑一个p0, p1, ... pn 为控制点序列的Bézier 曲线。首先,将相邻的点连接起来以形成线段。
- 用t : (1 − t) 的比例细分每个线段,并找到该分割点。
- 得到的分割点作为新的控制点序列,新序列的长度会减少一。
- 如果序列只包含一个点,则返回该点并终止。否则,使用新的控制点序列并转到步骤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:贝塞尔曲线的更多相关文章
- 【UE4】GAMES101 图形学作业2:光栅化和深度缓存
总览 在上次作业中,虽然我们在屏幕上画出一个线框三角形,但这看起来并不是那么的有趣.所以这一次我们继续推进一步--在屏幕上画出一个实心三角形,换言之,栅格化一个三角形.上一次作业中,在视口变化之后,我 ...
- 【UE4】GAMES101 图形学作业5:光线与物体相交(球、三角面)
总览 在这部分的课程中,我们将专注于使用光线追踪来渲染图像.在光线追踪中最重要的操作之一就是找到光线与物体的交点.一旦找到光线与物体的交点,就可以执行着色并返回像素颜色. 在这次作业中,我们要实现两个 ...
- 【UE4】GAMES101 图形学作业3:Blinn-Phong 模型与着色
总览 在这次编程任务中,我们会进一步模拟现代图形技术.我们在代码中添加了Object Loader(用于加载三维模型), Vertex Shader 与Fragment Shader,并且支持了纹理映 ...
- 【UE4】GAMES101 图形学作业1:mvp 模型、视图、投影变换
总览 到目前为止,我们已经学习了如何使用矩阵变换来排列二维或三维空间中的对象.所以现在是时候通过实现一些简单的变换矩阵来获得一些实际经验了.在接下来的三次作业中,我们将要求你去模拟一个基于CPU 的光 ...
- 【UE4】GAMES101 图形学作业0:矩阵初识
作业描述 给定一个点P=(2,1), 将该点绕原点先逆时针旋转45◦,再平移(1,2), 计算出变换后点的坐标(要求用齐次坐标进行计算). UE4 知识点 主要矩阵 FMatrix FBasisVec ...
- 计算机图形学:贝塞尔曲线(Bezier Curve)
计算机图形学:贝塞尔曲线(Bezier Curve) 贝塞尔能由贝塞尔样条组合而成,也可产生更高维的贝塞尔曲面.
- Unity3d游戏中自定义贝塞尔曲线编辑器[转]
关于贝塞尔曲线曲线我们再前面的文章提到过<Unity 教程之-在Unity3d中使用贝塞尔曲线>,那么本篇文章我们来深入学习下,并自定义实现贝塞尔曲线编辑器,贝塞尔曲线是最基本的曲线,一般 ...
- Android 自定义View高级特效,神奇的贝塞尔曲线
效果图 效果图中我们实现了一个简单的随手指滑动的二阶贝塞尔曲线,还有一个复杂点的,穿越所有已知点的贝塞尔曲线.学会使用贝塞尔曲线后可以实现例如QQ红点滑动删除啦,360动态球啦,bulabulabul ...
- 贝塞尔曲线:原理、自定义贝塞尔曲线View、使用!!!
一.原理 转自:http://www.2cto.com/kf/201401/275838.html Android动画学习Demo(3) 沿着贝塞尔曲线移动的Property Animation Pr ...
随机推荐
- WebService学习总结(二)--使用JDK开发WebService
一.WebService的开发方法 使用java的WebService时可以使用一下两种开发手段 使用jdk开发(1.6及以上版本) 使用CXF框架开发(工作中) 二.使用JDK开发WebServic ...
- QT 4.7.3 交叉编译环境搭建
测试平台 宿主机平台:Ubuntu 12.04.4 LTS 目标机:Easy-ARM IMX283 目标机内核:Linux 2.6.35.3 交叉编译器:arm-linux-gcc 4.4.4 tsl ...
- 哦?原来这就是 JVM 垃圾!
大家都知道,JVM 有垃圾回收的机制,垃圾回收的前提是要知道:什么是垃圾!然后再是如何识别垃圾! 什么是垃圾 垃圾,本质上就是没有引用的对象(们),下面来介绍两种垃圾 1. 没有引用指向的对象 下图是 ...
- 【机器学习|数学基础】Mathematics for Machine Learning系列之线性代数(1):二阶与三阶行列式、全排列及其逆序数
@ 目录 前言 二阶与三阶行列式 二阶行列式 三阶行列式 全排列及其逆序数 全排列 逆序数 结语 前言 Hello!小伙伴! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出- 自我介绍 ...
- CSS导航菜单(二级菜单)
index.html <div class="nav"> <ul> <li> <a href="#">Java& ...
- FileWriter文件文件字符输出流写入存储数据
1.FileWriter文件字符输出流-写入-存储数据 其中,流关闭之后再调用会报IOException; 其中,与文件字符输入流-写出-读取数据 和 字节输出流-写入-存储数据 不同的是,要先flu ...
- IDEA weblogic远程调试
weblogic远程调试 这里我们使用vulhub的镜像作为初始构建镜像搭建漏洞环境 1. 搭建docker环境 新建一个目录,创建两个文件 DockerFile FROM vulhub/weblog ...
- 苹果ASA广告投放归因的接入
前段时间,苹果终于在大陆区开放了应用商店的竞价广告.毫无疑问又开启了苹果应用导量的新玩法,各大厂商都紧跟脚步吃螃蟹.本篇讲解苹果广告中的归因部分. 苹果广告其实在海外已运行多年,而因为IDFA的政策变 ...
- 利用k8s yaml配置文件起一个http能够让外部访问
1.首先建一个http的Deployment apiVersion: apps/v1 #版本信息 kind: Deployment #文件类型 metadata: #Deployment资源的元数据信 ...
- CLion远程调试嵌入式开发板程序
CLion远程调试嵌入式开发板程序 目录 CLion远程调试嵌入式开发板程序 1. 目的 2. 前提条件 3. CLion设置 3.1 设置一个Deployment 3.2 上传需要的目录到目标板子 ...