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 algorithm
    int 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.mfbz.cn/a/281591.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Java开发岗面试】八股文—操作系统

声明&#xff1a; 背景&#xff1a;本人为24届双非硕校招生&#xff0c;已经完整经历了一次秋招&#xff0c;拿到了三个offer。本专题旨在分享自己的一些Java开发岗面试经验&#xff08;主要是校招&#xff09;&#xff0c;包括我自己总结的八股文、算法、项目、HR面和面试技巧…

【ARMv8M Cortex-M33 系列 2.1 -- Cortex-M33 使用 .hex /.srec 文件介绍】

请阅读【嵌入式开发学习必备专栏 之Cortex-M33 专栏】 文章目录 HEX 文件介绍英特尔十六进制文件格式记录类型hex 示例Cortex-M 系列hex 文件的使用 hex 文件和srec 文件生成Motorola S-Record (srec) 格式 HEX 文件介绍 .hex 文件通常用于微控制器编程&#xff0c;包括 ARM C…

『番外篇八』SwiftUI 脑洞大开实现“另类”视图跟随方法

概览 在 SwiftUI 的开发中,我们时常需要用指尖丝滑般地操作指定视图:比如,我们需要在拖动视图后让它自动归位,或者拖动一个视图时让另一个视图跟随它移动。 我们随后将会详细讨论上述两个 SwiftUI 中与视图移动相关场景的实现。 在本篇博文中,您将学到如下内容: 概览1.…

【C++】STL 容器 - multiset 容器 ( std::multiset 容器简介 | std::multiset 容器 常用操作 api 简介 )

文章目录 一、mulset 容器1、std::multiset 容器简介2、代码示例 - multiset 容器 二、std::multiset 容器 常用操作 api 简介1、常用 api 简介2、代码示例 - multiset 容器常用操作 一、mulset 容器 1、std::multiset 容器简介 在 C 语言 的 标准模板库 ( STL , Standard Temp…

QString设置小数点精度位数

QString设置小数点精度位数 Chapter1 QString设置小数点精度位数Chapter2 Qt中QString.toDouble有效位数6位问题以及数据小数点有效位数的处理问题一&#xff1a;QString.toDouble有效位只有6位问题二:小数点有效位数的问题 Chapter3 qt QString转Double只显示6位数字的问题(精…

MCS-51单片机的中断源

目录 MCS-51中断源&#xff1a; 中断控制&#xff1a; 1.定时控制寄存器&#xff08;TCON&#xff09; 2.串行口控制寄存器&#xff08;SCON&#xff09; 3.中断允许寄存器(IE) 4.中断优先级控制寄存器(IP) 中断处理: 中断采样&#xff1a; 中断查询&#xff1a; 中断…

计算机操作系统(OS)——P4文件管理

1、初始文件管理 1.1、文件的属性 1&#xff09;文件名&#xff1a;由创建文件的用户决定文件名&#xff0c;主要是为了方便用户找到文件&#xff0c;同一目录下不允许有重名文件。 2&#xff09;标识符&#xff1a;一个系统内的各文件标识符唯一&#xff0c;对用户来说毫无…

【YOLO系列】YOLOv8 -【教AI的陶老师】

文章目录 yolo v8 模型结构图这样搞有什么意义&#xff1f;【获得不同尺寸的输出】c2f 详细结构yolo v8 损失函数与 yolo v5 的区别 yolo v8 模型结构图 详细结构图 这样搞有什么意义&#xff1f;【获得不同尺寸的输出】 c2f 详细结构 yolo v8 损失函数 与 yolo v5 的区别

学习体系结构 - AArch64内存管理

学习体系结构 - AArch64内存管理 Learn the architecture - AArch64 memory management Version 1.2 个人的英语很一般&#xff0c;对拿不准的翻译校准在后面添加了英文原文。 1、 概述 本指南介绍了AArch64中的内存转换&#xff0c;这是内存管理的关键。它解释了如何将虚拟地…

常用设计模式全面总结版(JavaKotlin)

这篇文章主要是针对之前博客的下列文章的总结版本: 《设计模式系列学习笔记》《Kotlin核心编程》笔记:设计模式【Android知识笔记】FrameWork中的设计模式主要为了在学习了 Kotlin 之后,将 Java 的设计模式实现与 Kotin 的实现放在一起做一个对比。 一、创建型模式 单例模…

CentOS 7 实战指南:文件操作命令详解

写在前面 想要快速掌握 CentOS 7 系统下的文件操作技巧吗&#xff1f;不用担心&#xff01;我为你准备了一篇详细的技术文章&#xff0c;涵盖了各种常用的文件操作命令。无论您是初学者还是有一定经验的用户&#xff0c;这篇文章都能帮助您加深对 CentOS 7 文件操作的理解&…

【LeetCode每日一题】1154. 一年中的第几天(直接计算+调用库函数)

2023-12-31 文章目录 [1154. 一年中的第几天](https://leetcode.cn/problems/day-of-the-year/)方法一&#xff1a;直接计算思路&#xff1a; 方法二&#xff1a;调用库函数思路 1154. 一年中的第几天 方法一&#xff1a;直接计算 思路&#xff1a; 1.根据所给的字符串&#…

第二节 linux操作系统安装与配置

一&#xff1a;Vmware虚拟机安装与使用   ①VMware是一个虚拟PC的软件&#xff0c;可以在现有的操作系统上虚拟出一个新的硬件环境&#xff0c;相当于模拟出一台新的PC &#xff0c;以此来实现在一台机器上真正同时运行多个独立的操作系统。   ②VMware主要特点&#xff1a…

[LitCTF 2023]Vim yyds

[LitCTF 2023]Vim yyds wp 题目页面如下&#xff1a; 搜索一番&#xff0c;没有发现任何信息。题目描述中说到了源码泄露&#xff0c;那么先进行目录扫描。 dirsearch 目录扫描 命令&#xff1a; dirsearch -u "http://node4.anna.nssctf.cn:28588/"返回结果&…

PTA——计算火车运行时间

本题要求根据火车的出发时间和达到时间&#xff0c;编写程序计算整个旅途所用的时间。 输入格式&#xff1a; 输入在一行中给出2个4位正整数&#xff0c;其间以空格分隔&#xff0c;分别表示火车的出发时间和到达时间。每个时间的格式为2位小时数&#xff08;00-23&#xff0…

最简单的基于 SDL2 的音频播放器

最简单的基于 SDL2 的音频播放器 最简单的基于 SDL2 的音频播放器正文工程文件下载 参考雷霄骅博士的文章&#xff0c;链接&#xff1a;最简单的基于FFMPEGSDL的音频播放器&#xff1a;拆分-解码器和播放器 最简单的基于 SDL2 的音频播放器 正文 SDL2 音频播放器实现了播放 …

抖音详情API:开发环境搭建与工具选择

随着短视频的流行&#xff0c;抖音已经成为了一个备受欢迎的社交媒体平台。对于开发人员而言&#xff0c;利用抖音详情API开发定制化的抖音应用具有巨大的潜力。本文将为你详细介绍开发抖音应用的开发环境搭建与工具选择&#xff0c;帮助你顺利地开始开发工作。 一、开发环境搭…

Github项目推荐-vocal-separate

项目地址 vocal-separate: 项目简述 这是一个音乐和人声分离的项目&#xff0c;基于python开发。有图形化操作界面&#xff0c;看起来还不错。 项目截图

Centos7安装Docker和Docker-Compose

环境 操作系统&#xff1a;Centos 7.9 root环境 Docker安装 卸载原先的Docker环境 如果你先前的操作系统安装了Docker环境&#xff0c;请卸载 Docker 相关的软件包&#xff0c;没有则忽略这一步。 yum remove docker \docker-client \docker-client-latest \docker-common \doc…

计数排序 CountingSort

计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序&#xff0c;计数排序要求输入的数据必须是有确定范围的整数。 动画演示 : 案例代码 : (此代码有些瑕疵 , 不能处理负数) public static void main(String[] args) {//int[…