GAMES101:作业4记录

文章目录

  • 总览
  • 算法
  • 编写代码:
    • recursive_bezier()的实现
    • Bezier()函数的实现
    • 提高部分:反走样

总览

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 曲线。

编写代码:

我们先把源代码的naive_bezier画Bezier曲线的程序跑通

mkdir build
cd build
cmake ..
make
./BezierCurve

在这里插入图片描述
然后我们注释掉main函数里的naive_bezier(control_points, window);,实现自己的Bezier曲线绘制。

recursive_bezier()的实现

我们在recursive_bezier()函数中获得根据t获得贝塞尔曲线的点,这里不同于直接使用多项式(naive_bezier使用的是多项式的方法),使用递归算法,递归的返回条件是最终递归数组的长度为1(下图对应的是 b 0 3 \mathbf{b}_0^3 b03),这时候返回结果。如果没有达到返回条件,就进入下一次递归,传进递归计算后的数组(数组的长度减1):
在这里插入图片描述

cv::Point2f recursive_bezier(const std::vector<cv::Point2f> &control_points, float t) 
{// TODO: Implement de Casteljau's algorithmint len = control_points.size();if (len == 1)return control_points[0];std::vector<cv::Point2f> lerp_control_points(len - 1, cv::Point2f(0.0, 0.0));for (int i = 0; i < len - 1; ++i){lerp_control_points[i] = t * control_points[i] + (1 - t) * control_points[i + 1];}return recursive_bezier(lerp_control_points, t);}

Bezier()函数的实现

我们在bezier()函数里从0到1遍历所有的t,然后使用前面写的recursive_bezier()获得Bezier曲线点的坐标,然后在图上该点的位置涂上颜色即可。

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){auto point = recursive_bezier(control_points, t) ;window.at<cv::Vec3b>(point.y, point.x)[1] = 255; //显示是绿色       }
}

绿色的曲线通过同样的make命令可以得到:

在这里插入图片描述

naive_bezier(control_points, window);取消注释可以看到黄色的Bezier曲线

在这里插入图片描述

但可以看到锯齿比较明显

在这里插入图片描述

提高部分:反走样

提高部分要求使用反走样,题目提示说:

对于一个曲线上的点,不只把它对应于一个像素,你需要根据到像素中心的距离来考虑与它相邻的像素的颜色。

也就是说离像素越近颜色越深,离像素越远颜色越浅。

这里参考的是作业四得到这样的结果是否满足要求?里xuyonglai的思路,我们首先要找到和Bezier曲线点(point.x,point,y)最临近的四个像素,曲线点所在的像素很好找,其他三个像素该怎么找呢?这里类似采用四舍五入的方法,判断(point.x,point,y)靠近它所在像素的哪一侧,以此来确定其他三个像素的方向,然后我们就可以确定四个像素的坐标了,其中代码中的p0是最临近的像素,其他的p1,p2,p3依次是其他三个像素。然后定义一个pvec存放这三个临近的像素,依次给这其他三个近邻像素着色,这里取了最大值是因为如果这次计算的像素的颜色是偏暗的绿色,但是这个像素上次有重复计算(靠近曲线绿色的比重更大),替换为暗色可能会让反走样的效果变差。这种方法也有一定的缺点,就是只能对黑色的背景(RGB三个分量都是0)起作用,但是其他背景颜色直接取max最大值就不太行了。

在这里插入图片描述

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){auto point = recursive_bezier(control_points, t) ;window.at<cv::Vec3b>(point.y, point.x)[1] = 255; //显示是绿色float xDelta = point.x - std:: floor(point.x);float yDelta = point.y - std:: floor(point.y);int xDir = xDelta < 0.5f ? -1 : 1;int yDir = yDelta < 0.5f ? -1 : 1;cv::Point2f p0 = cv::Point2f(std::floor(point.x) + 0.5f, std::floor(point.y) + 0.5f);cv::Point2f p1 = cv::Point2f(std::floor(point.x + xDir * 1.0f) + 0.5f, std::floor(point.y) + 0.5f);cv::Point2f p2 = cv::Point2f(std::floor(point.x) + 0.5f, std::floor(point.y + yDir * 1.0f) + 0.5f);cv::Point2f p3 = cv::Point2f(std::floor(point.x + xDir * 1.0f) + 0.5f, std::floor(point.y + yDir * 1.0) + 0.5f);std::vector<cv::Point2f> pvec;pvec.push_back(p1);pvec.push_back(p2);pvec.push_back(p3);float d1 = std::sqrt(std::pow(p0.x - point.x, 2) + std::pow(p0.y - point.y, 2));for (auto& p: pvec){float dp = std::sqrt(std::pow(p.x - point.x, 2) + std::pow(p.y - point.y, 2));float weight = d1 / dp;float colorG = window.at<cv::Vec3b>(p.y, p.x)[1];colorG = std::fmax(colorG, weight * 255.0);window.at<cv::Vec3b>(p.y, p.x)[1] = (int)colorG;}        }
}

在这里插入图片描述
可以看到反走样有了较好的效果。

其他反走样的方法也可以看这篇文章:Games 101 | 作业4 + Bezier Curve + 反走样 + 双线性插值,距离和颜色的值(RGB分量的值越接近1越饱和)成反比,所以用最大距离减去像素中心和曲线点的距离也可以构造反比的函数,权重需要在0到1之间,还是一样我们使用所有的最大距离减像素中心和曲线点的距离的值的和作为分母,使用大距离减去特定像素中心和曲线点的距离作为分子计算特定像素的权重,这里就不写代码了,思路是差不多的。

其他参考:

The Beauty of Bresenham’s Algorithm【介绍了Bresenham’s 算法来实现反走样】
A Rasterizing Algorithm for Drawing Curves【上面网站的pdf的说明】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/311559.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第九课:机器学习与人工智能、计算机视觉、自然语言处理 NLP及机器人

第九课&#xff1a;机器学习与人工智能、计算机视觉、自然语言处理 NLP及机器人 第三十四章&#xff1a;机器学习与人工智能1、分类 Classification2、做分类的算法 分类器 Classifier3、用于分类的值是特征 Feature4、特征值种类叫做标记数据 Labeled data5、决策边界 Decisio…

SpringBoot 医药咨询系统

概述 智慧医药系统&#xff08;smart-medicine&#xff09;是一个基于 SpringBoot 开发的Web 项目。整体页面简约大气&#xff0c;增加了AI医生问诊功能&#xff0c;功能设计的较为简单。 开源地址 https://gitcode.net/NVG_Haru/Java_04 界面预览 功能介绍 游客功能介绍 …

AIGC重塑基础设施,高密数据中心为何众望所归?

凯文凯利在《必然》中认为&#xff0c;科技在本质上有所偏好&#xff0c;使得它朝往某种特定方向。 毫无疑问&#xff0c;进入到数字经济时代&#xff0c;人工智能技术飞速发展与加速应用之际&#xff0c;这个特定方向逐渐明朗&#xff1a;即算力科技&#xff0c;算力已经成为…

Linux shell编程学习笔记38:history命令

目录 0 前言 1 history命令的功能、格式和退出状态1.1 history命令的功能1.2 history命令的格式1.3退出状态2 命令应用实例2.1 history&#xff1a;显示命令历史列表2.2 history -a&#xff1a;将当前会话的命令行历史追加到历史文件~/.bash_history中2.3 history -c&#xf…

训狗技术从初级到高级,专业有效的训狗训犬教程

一、教程描述 现在大部分人家里都会养些宠物&#xff0c;比如狗狗&#xff0c;虽然狗狗的一些行为习惯跟遗传有关&#xff0c;但是主人后天的影响也会给狗狗带来改变&#xff0c;本套教程教你纠正狗狗的不良行为&#xff0c;可以让你与狗愉快地玩耍。本套训狗教程&#xff0c;…

程序员必备IDEA插件,什么是是IDE?

IDEA是一款功能强大的集成开发环境&#xff08;IDE&#xff09;插件&#xff0c;它可以帮助开发人员更加高效地编写、调试和部署软件应用程序。 我们在编写完接口代码后需要进行接口调试等操作&#xff0c;一般需要打开额外的调试工具。今天就给大家介绍一款IDEA插件&#xff…

2023年成都市中等职业学校学生技能大赛“网络搭建及应用”赛项竞赛样卷

2023年成都市中等职业学校学生技能大赛 “网络搭建及应用”赛项竞赛样卷 &#xff08;总分1000分&#xff09; 目录 2023年成都市中等职业学校学生技能大赛 “网络搭建及应用”赛项竞赛样卷 网络建设与调试项目&#xff08;500分&#xff09; 服务器搭建与运维项目&#xff08;…

Rust学习笔记002: 猜字游戏

version0.1 // 导入标准库中的 io 模块&#xff0c;它包含了输入输出相关的功能 use std::io;// 程序的入口点 fn main() {println!("Guess the number!");println!("Please input your guess.");// 创建一个可变的字符串变量 guess&#xff0c;用于存储用…

【2023年终总结】谈谈一个新人眼里的阿里方法论

写在开头 2023年转眼就过去了&#xff0c;今年我从一名大学生转变某阿里系大厂的“搬砖打工人”&#xff0c;这一转变真的是给我“涉世未深的纯洁心灵”带来了大大的“震撼”。 角色的转变是需要时间进行“内部消化”的。无论是对于个人的价值认知或者是行为方式来说&#xf…

液晶时钟设计

#include<reg51.h> //包含单片机寄存器的头文件 #include<stdlib.h> //包含随机函数rand()的定义文件 #include<intrins.h> //包含_nop_()函数定义的头文件 sbit RSP2^0; //寄存器选择位&#xff0c;将RS位定义为P2.0引脚 sbit RWP2^1; //读写选…

ros2 run传递参数的格式

ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -r /cmd_vel:/model/vehicle_blue/cmd_vel #这个只能用于重命名节点名称可以用以下语法直接从命令行中设置参数&#xff1a; 这里很清楚看出来ros2 run name exec --ros-args -p param:value中的parma指的是…

“C语言与人生:手把手教你玩转C语言数组,从此编程无难题“

各位少年&#xff0c;我是博主那一脸阳光&#xff0c;由我来给大家介绍C语言的数组的详解。 在C语言中&#xff0c;数组是一种极其重要的数据结构&#xff0c;它允许我们存储和管理相同类型的一系列相关数据。通过理解并熟练掌握数组的使用&#xff0c;开发者能够高效地处理大量…