GAMES101:作业4记录

news2025/1/22 14:40:51

文章目录

  • 总览
  • 算法
  • 编写代码:
    • 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.coloradmin.cn/o/1347552.html

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

相关文章

拒绝采样(算法)总结

先说说什么是拒绝采样算法&#xff1a;就类似于数学上的求阴影面积的方法&#xff0c;直接求求不出来&#xff0c;就用大面积 - 小面积 阴影面积的办法。 所谓拒绝 和 采样 &#xff1a;就像是撒豆子计个数&#xff0c;计算概率问题一样&#xff0c;大桶里面套小桶&#xff0c…

从0到1入门C++编程——01 C++基础知识

文章目录 一、工具安装二、新建项目三、设置字体、注释、行号四、C基础知识1.数据类型2.输入输出3.运算符4.选择、循环结构5.跳转语句6.数组7.函数8.指针9.结构体 一、工具安装 学习C使用到的工具是Visual Studio&#xff0c;Visual Studio 2010旗舰版下载链接&#xff1a;点此…

uni-app模版(扩展插件)

锋哥原创的uni-app视频教程&#xff1a; 2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中..._哔哩哔哩_bilibili2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中...共计23条视频&#xff0c;包括&#xff1a;第1讲 uni…

2023年终总结|回顾学习Tensorflow、Keras的历程

2023年4月&#xff0c;初探TensorFlow2.0&#xff0c;对比了1.0版本的差异。接着&#xff0c;学习了TensorFlow2.0的常量矩阵、四则运算以及常用函数。学习了数据切割、张量梯度计算、遍历元素、类别索引转换等技巧&#xff0c;并掌握了CNN输出特征图形状的计算方法。 在数据处…

【回溯】图的m着色问题Python实现

文章目录 [toc]问题描述图的 m m m可着色判定问题图的 m m m可着色优化问题四色猜想 回溯法时间复杂性Python实现 个人主页&#xff1a;丷从心 系列专栏&#xff1a;回溯法 问题描述 图的 m m m可着色判定问题 给定无向连通图 G G G和 m m m种不同的颜色&#xff0c;用这些颜…

SpringBoot实用篇

SpringBoot实用篇 1、热部署 什么是热部署&#xff1f; 所谓热部署&#xff0c;就是在应用正在运行的时候升级软件&#xff0c;却不需要重新启动应用。对于Java应用程序来说&#xff0c;热部署就是在运行时更新Java类文件。 热部署有什么用&#xff1f; 节约时间&#xff0c;热…

【python高级用法】迭代器、生成器、装饰器、闭包

迭代器 可迭代对象&#xff1a;可以使用for循环来遍历的&#xff0c;可以使用isinstance()来测试。 迭代器&#xff1a;同时实现了__iter__()方法和__next__()方法&#xff0c;可以使用isinstance()方法来测试是否是迭代器对象 from collections.abc import Iterable, Iterat…

Select缺点及代码示例

一、Select缺点 二、服务器端 #include <stdio.h> #include <arpa/inet.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <sys/select.h>int main() {// 创建socketint lfd socket(PF_INET, SOCK_STREAM, 0)…

java进阶(三)

IO与网络编程 3 输入输出流IO3.1 基础定义3.2 IO框架3.3 读取字节输入流-InputStream3.3.1 InputStream.read3.3.2 FileInputStream类说明 3.4读取字符输入流Reader3.4.1 Reader.read3.4.2 FileReader类说明 3.5 字节输出流OutputStream3.5.1 OutputStream.write3.5.2 FileOutp…

Docker容器基础知识点总结

一 、Docker架构 dockers加速镜像&#xff1a; sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-EOF {"registry-mirrors": ["https://z90yxq2m.mirror.aliyuncs.com"] } EOF sudo systemctl daemon-reload sudo systemctl restar…

thinkphp6.0升级到8.0

目录 一&#xff1a;升级过程 二&#xff1a;报错处理 最近写的项目需要使用thinkphp8.0&#xff0c;之前的老项目需要从php6.0升级到8.0&#xff0c;特此记录下升级过程。 一&#xff1a;升级过程 查看版本&#xff1a; php think version,我目前的版本是6.1.4 生成thin…

Rust学习笔记001:HELLOW WORLD + Cargo

Rust介绍 Rust&#xff08;中文称为“锈”&#xff09;是一种由Mozilla开发的系统编程语言&#xff0c;它着力于提供安全性、并发性和实用性。Rust的设计目标是消除程序出现的内存安全性问题&#xff0c;如空指针引用、数据竞争等。它通过在编译时进行严格的所有权和借用检查来…

Java实现树结构(为前端实现级联菜单或者是下拉菜单接口)

Java实现树结构&#xff08;为前端实现级联菜单或者是下拉菜单接口&#xff09; 我们常常会遇到这样一个问题&#xff0c;就是前端要实现的样式是一个级联菜单或者是下拉树&#xff0c;如图 这样的数据接口是怎么实现的呢&#xff0c;是什么样子的呢&#xff1f; 我们可以看看 …

【jdk与tomcat配置文件夹共享防火墙设置(入站出站规则)】

目录 一、jdk与tomcat配置 1.1 jdk配置 1.2 tomcat配置 二、文件夹共享 2.1 为什么需要配置文件夹共享功能 2.2 操作步骤 2.2.1 高级共享 2.2.2 普通共享 2.3 区别 三、防火墙设置&#xff08;入站规则&出站规则&#xff09; 3.1 入站规则跟出站规则 3.2 案例…

【低代码平台】10个开源免费Airtable 的替代方案

Airtable是一个易于使用的简单低代码平台&#xff0c;有助于团队协作管理复杂的数据表&#xff0c;并创建定制的工作流程。把它想象成一个类固醇上的云电子表格。 Airtable还简化了数据输入过程&#xff0c;连接和集成第三方服务和应用程序&#xff0c;并提供了许多数据导入/导…

第二部分 离散型随机变量

目录 求分布律里的未知数 例1 例2 根据X的分布律写Y的分布律 例3 根据(X,Y)的分布律写Z的分布律 例4 根据(X,Y)的分布律写边缘分布律 例5 X与Y相互独立时的联合分布律 例6 根据分布律求期望、方差 例7 求分布律里的未知数 例1 已知X的分布律为 X-202P0.40.3k ,试求k 解 0.40…

普中STM32-PZ6806L 使用FlyMcu串口烧录程序

简介 我的串口下载电路坏掉了, 所以研究了下如何通过USB转TTL进行程序的下载, 为后续Bootloader部分做准备;连接 我的板几乎是十年前买的&#xff0c; 所以电路与现有网上的资料有些差异, 所以仅供参考 USB 转 TTL线 与开发板 连接&#xff0c; 如图图中 ①, 需要去掉第一个…

[2024区块链开发入门指引] - 比特币运行原理

一份为小白用户准备的免费区块链基础教程 工欲善其事,必先利其器 Web3开发中&#xff0c;各种工具、教程、社区、语言框架.。。。 种类繁多&#xff0c;是否有一个包罗万象的工具专注与Web3开发和相关资讯能毕其功于一役&#xff1f; 参见另一篇博文&#x1f449; 2024最全面…

RFC6749-OAuth2.0

前言 最近在项目中需要实现SSO(单点登录)功能,以实现一处注册,即可在任何平台之间登录的功能。我们项目中并没有直接对接第三方认证系统而是通过集成keycloak 完成一系类安全协议的对接工作。如果我们在代码级别自己完成各种安全协议的对接是一项十分大的工程。不仅要走统一的…

提取 PE 文件的各种信息

前段时间项目需要实现对 Windows PE 文件版本信息的提取&#xff0c;如文件说明、文件版本、产品名称、版权、原始文件名等信息。获取这些信息在 Windows 下当然有一系列的 API 函数供调用&#xff0c;简单方便。 我们先看一下PE文件结构&#xff0c;PE文件由DOS首部&#xff0…