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.mzph.cn/news/587714.shtml

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

相关文章

亚信安慧AntDB数据库两项目分别入选2023“星河”标杆、优秀案例

近日&#xff0c;由中国信息通信研究院、中国通信标准化协会大数据技术标准推进委员会&#xff08;CCSA TC601&#xff09;共同组织的第七届大数据“星河&#xff08;Galaxy&#xff09;”案例评选结果公示&#xff0c;亚信安慧AntDB数据库两项目入选&#xff0c;其中“基于Ant…

国图公考:研究生可以考选调生吗?

研究生可以报考选调生吗?当然是可以的&#xff0c;但是同样需要满足一定的条件才可以。 除本科生外&#xff0c;具有硕士、博士学位的考生均可申请考试。但是&#xff0c;除了满足应届毕业生的身份&#xff0c;还需要满足年龄限制。一般来说&#xff0c;本科生不超过25岁&…

Kubernetes集群部署Rook Ceph实现文件存储,对象存储,块存储

Kubernetes集群部署Rook Ceph部署Ceph集群 1. Rook Ceph介绍 Rook Ceph是Rook项目中的一个存储方案&#xff0c;专门针对Ceph存储系统进行了优化和封装。Ceph是一个高度可扩展的分布式存储系统&#xff0c;提供了对象存储、块存储和文件系统的功能&#xff0c;广泛应用于提供…

FTP简介FTP服务器的搭建【虚拟机版】以及计算机端口的介绍

目录 一. FTP简介 二. FTP服务器的搭建【虚拟机Windows2012版】 1. 启用防火墙 2. 打开服务器管理器➡工具➡计算机管理 3. 选择本地用户与组➡新建组 4. 给组命名&#xff0c;输入描述&#xff0c;点击创建 5. 新建用户&#xff0c;设置用户名称&#xff0c;添加描述&a…

传感器原理与应用复习--光电式与半导体式传感器

文章目录 上一篇光电传感器光电器件 光纤传感器光纤传感器的工作原理及组成 半导体传感器下一篇 上一篇 传感器原理与应用复习–磁电式与霍尔传感器 光电传感器 光电器件 每个光子的能量为 E h v E hv Ehv h为普朗克常数 6.626 ∗ 1 0 − 34 ( J / s ) 6.626 * 10^{-…

【ARMv8M Cortex-M33 系列 2.3 -- SEGGER JFlash 烧写命令介绍】

请阅读【嵌入式开发学习必备专栏 之Cortex-M33 专栏】 文章目录 SEGGER JFlash 烧写命令介绍JFlash 配置文件 固件烧写地址介绍确定烧写地址 SEGGER JFlash 烧写命令介绍 本文以介绍烧写 Renesas RA4M2 为例&#xff0c;对 JFlash 进行简单介绍。它是 ARM Cortex-M33 微控制器…

华为ensp网络设计期末测试题-复盘

网络拓扑图 地址分配表 vlan端口分配表 需求 The device is running!<Huawei>sys Enter system view, return user view with CtrlZ. [Huawei]un in en Info: Information center is disabled. [Huawei]sys S1 [S1]vlan 99 [S1-vlan99]vlan 100 [S1-vlan100]des IT [S1-…

2023 全球 AI 大事件盘点

本文来自微信公众号硅星人

跨境电商的语言障碍:翻译工具的必要性

随着全球化的加速和电子商务的普及&#xff0c;跨境电商逐渐成为企业拓展市场的重要渠道。然而&#xff0c;跨境电商在带来无限商机的同时&#xff0c;也面临着语言障碍的挑战。由于不同国家和地区的语言和文化差异&#xff0c;跨境电商在产品描述、用户沟通、广告宣传等方面需…

年度总结|存储随笔2023年度最受欢迎文章榜单TOP15-part1

原创 古猫先生 存储随笔 2023-12-31 08:31 发表于上海 回首2023 2-8月份有近半年时间基本处于断更状态 好在8月份后小编没有松懈 &#xff08;虽然2023年度总结&#xff0c;更像是近4个月总结&#xff09; 本年度顺利加V啦&#xff01; 感谢各位粉丝朋友的一路支持与陪伴 …

2024主流的免费电脑数据恢复软件EasyRecovery

EasyRecovery绿色版是一款非常专业的电脑数据恢复工具。它可以全面恢复删除丢失数据&#xff0c;能对电脑误删文件恢复、格式化硬盘数据恢复、手机U盘数据恢复等&#xff0c;能恢复包括文档、表格、图片、音视频等各种文件&#xff0c;此版本经过处理&#xff0c;可永久免费使用…

AcWing算法提高课-2.2.3抓住那头牛

算法提高课整理 CSDN个人主页&#xff1a;更好的阅读体验 本文同步发表于 CSDN | 洛谷 | AcWing | 个人博客 原题链接 题目描述 农夫知道一头牛的位置&#xff0c;想要抓住它。 农夫和牛都位于数轴上&#xff0c;农夫起始位于点 n n n&#xff0c;牛位于点 k k k。 农夫…

golang第五卷---包以及常用内置包归纳

包以及常用内置包归纳 包的概念math包time包sync包 Go 语言官方的包文档网站&#xff1a;包文档 包的概念 Go语言是使用包来组织源代码的&#xff0c;包&#xff08;package&#xff09;是多个 Go 源码的集合&#xff0c;是一种高级的代码复用方案。 任何源代码文件必须属于某…

听GPT 讲Rust源代码--src/tools(37)

File: rust/src/tools/clippy/clippy_lints/src/explicit_write.rs 在Rust源代码中&#xff0c;explicit_write.rs这个文件是Clippy的一个lint插件&#xff0c;其作用是检查代码中的write!、writeln!宏使用时的不当或繁琐的情况&#xff0c;并给出相关的警告或建议。 具体来说&…

可视化设计器介绍

1 、可视化设计器分为&#xff1a; 目录树&#xff1a;包含“页面、逻辑、数据、流程”四种可视化设计模块的切换&#xff0c;及其下设计内容管理与树形展示应用信息与操作区域&#xff1a;支持应用的“预览、发布、自定义主题样式”等操作标签页&#xff1a;支持各个编辑页面…

java方法(method)概述,计算机中的执行原理及案例

引言&#xff1a; 学习了方法&#xff08;method&#xff09;&#xff0c;整理下心得 概述&#xff1a; 1&#xff0c;什么是方法&#xff08;method&#xff09;: 方法是一种语法结构&#xff0c;它可以把一段代码封装成一个功能&#xff0c;以便重复利用 2&#xff0c;使用…

Vue实现导出Excel表格,提示“文件已损坏,无法打开”的解决方法

一、vue实现导出excel 1、前端实现 xlsx是一个用于读取、解析和写入Excel文件的JavaScript库。它提供了一系列的API来处理Excel文件。使用该库&#xff0c;你可以将数据转换为Excel文件并下载到本地。这种方法适用于在前端直接生成Excel文件的场景。 安装xlsx依赖 npm inst…

【计算机网络实验】educoder实验八 IPV6网络及其路由 头歌

第一关 IPV6网络基础 //千万不要破坏文档原有结构与内容&#xff01;&#xff01;&#xff01; //以下均为判断题&#xff0c;F&#xff1a;表示错误&#xff0c;T&#xff1a;表示正确 //答案必须写在相应行末尾括号内&#xff0c;F与T二选一&#xff0c;大写 // 1、ipv6协议…

洛谷:集合与差分

1.学籍管理(map&#xff09; #include<iostream> #include<map> #include<string> using namespace std; map<string,int>a; int n; string name; int op,score; int main() {cin>>n;for(int i1;i<n;i){cin>>op;if(op!4)cin>>na…

Redis内存使用率高,内存不足问题排查和解决

问题现象 表面现象是系统登录突然失效&#xff0c;排查原因发现&#xff0c;使用redis查询用户信息异常&#xff0c;从而定位到redis问题 if (PassWord.equals(dbPassWord)) {map.put("rtn", 1);map.put("value", validUser);session.setAttribute("…