games101 作业2
题目
光栅化一个三角形
1. 创建三角形的 2 维 bounding box。
2. 遍历此 bounding box 内的所有像素(使用其整数索引)。然后,使用像素中心的屏幕空间坐标来检查中心点是否在三角形内。
3. 如果在内部,则将其位置处的插值深度值 (interpolated depth value) 与深度缓冲区 (depth buffer) 中的相应值进行比较。
4. 如果当前点更靠近相机,请设置像素颜色并更新深度缓冲区 (depth buffer)。
题解
本次作业需要实现代码框架中的两个接口:
void rst::rasterizer::rasterize_triangle(const Triangle& t);
static bool insideTriangle(int x, int y, const Vector3f* _v);
1. 在2D空间中,计算一个三角形的轴对称boundbox
只需要计算出三角形的三个顶点坐标中,x最大最小值,y最大最小值。即 ( x m i n , y m i n ) , ( x m a x , y m a x ) (x_{min},y_{min}),(x_{max},y_{max}) (xmin,ymin),(xmax,ymax)
使用<math.h>库实现如下:
int xMin, yMin, xMax, yMax;xMin = std::floor(std::min(std::min(v[0].x(),v[1].x()),v[2].x()));yMin = std::floor(std::min(std::min(v[0].y(), v[1].y()), v[2].y()));xMax = std::ceil(std::max(std::max(v[0].x(), v[1].x()), v[2].x()));yMax = std::ceil(std::max(std::max(v[0].y(), v[1].y()), v[2].y()));
注意:顶点坐标都是浮点数,但是我们计算出的包围盒必须是整型。左上角下取整,右下角上去整。
2. 判断像素的中心点是否在三角形内部
其实方法有很多种,具体可以参考这个博客。
最常用最高效的有两种:重心坐标法和向量叉积。
本次作业选用向量叉积法:
代码如下
static bool insideTriangle(int x, int y, const Vector3f* _v)
{ // TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v[2]auto v0_v1 = _v[1] - _v[0];auto v1_v2 = _v[2] - _v[1];auto v2_v0 = _v[0] - _v[2];auto v0_P = Vector3f(x, y, _v[0].z()) - _v[0];auto v1_P = Vector3f(x, y, _v[1].z()) - _v[1];auto v2_P = Vector3f(x, y, _v[2].z()) - _v[2];auto v0pCross = v0_v1.cross(v0_P);auto v1pCross = v1_v2.cross(v1_P);auto v2pCross = v2_v0.cross(v2_P);if (v0pCross.dot(v1pCross) >= 0 && v0pCross.dot(v2pCross) >= 0)return true;return false;
}
因为我们判断的是一个像素的中心点是否在三角形内部,所以需要给x,y 分别加0.5,即insideTriangle(x+0.5,y+0.5,t.v)
注意:Vector3f Triangle::v[3] 中存放的就是三角形的三个顶点。
3.根据插值得到的深度值和深度缓冲的深度值比较。
插值运算使用代码框架,所以这块比较简单。
代码如下
for (int i = xMin; i <= xMax; i++){for (int j = yMin; j <= yMax; j++){if (insideTriangle(i+0.5f, j+0.5f,t.v)){auto[alpha, beta, gamma] = computeBarycentric2D(i, j, t.v);float w_reciprocal = 1.0/(alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();z_interpolated *= w_reciprocal;int index = get_index(i, j);if (depth_buf[index] > z_interpolated){depth_buf[index] = z_interpolated; // 更新深度缓冲区set_pixel(Vector3f(i,j,z_interpolated),t.getColor());}}}}
注意:如果当前z值小于深度缓冲区的深度值,一定要更新深度缓冲区。
结果

代码:
static bool insideTriangle(int x, int y, const Vector3f* _v)
{ // TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v[2]auto v0_v1 = _v[1] - _v[0];auto v1_v2 = _v[2] - _v[1];auto v2_v0 = _v[0] - _v[2];auto v0_P = Vector3f(x, y, _v[0].z()) - _v[0];auto v1_P = Vector3f(x, y, _v[1].z()) - _v[1];auto v2_P = Vector3f(x, y, _v[2].z()) - _v[2];auto v0pCross = v0_v1.cross(v0_P);auto v1pCross = v1_v2.cross(v1_P);auto v2pCross = v2_v0.cross(v2_P);if (v0pCross.dot(v1pCross) >= 0 && v0pCross.dot(v2pCross) >= 0)return true;return false;
}void rst::rasterizer::rasterize_triangle(const Triangle& t) {auto v = t.toVector4();int xMin, yMin, xMax, yMax;xMin = std::floor(std::min(std::min(v[0].x(),v[1].x()),v[2].x()));yMin = std::floor(std::min(std::min(v[0].y(), v[1].y()), v[2].y()));xMax = std::ceil(std::max(std::max(v[0].x(), v[1].x()), v[2].x()));yMax = std::ceil(std::max(std::max(v[0].y(), v[1].y()), v[2].y()));for (int i = xMin; i <= xMax; i++){for (int j = yMin; j <= yMax; j++){if (insideTriangle(i+0.5f, j+0.5f,t.v)){auto[alpha, beta, gamma] = computeBarycentric2D(i, j, t.v);float w_reciprocal = 1.0/(alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();z_interpolated *= w_reciprocal;int index = get_index(i, j);if (depth_buf[index] > z_interpolated){depth_buf[index] = z_interpolated;set_pixel(Vector3f(i,j, z_interpolated),t.getColor());}}}}
}
参考文献
判断点是否在三角形内
相关文章:
games101 作业2
题目 光栅化一个三角形 1. 创建三角形的 2 维 bounding box。 2. 遍历此 bounding box 内的所有像素(使用其整数索引)。然后,使用像素中心的屏幕空间坐标来检查中心点是否在三角形内。 3. 如果在内部,则将其位置处的插值深度值 (…...
二叉树链式存储结构
目录 1.二叉树链式存储结构 2.二叉树的遍历 2.1 前、中、后序遍历 2.2 层序遍历 3.二叉树的其他递归问题 3.1 二叉树的结点个数 3.2 二叉树的叶子结点个数 3.3 二叉树第k层结点个数 3.4 二叉树的深度 3.5 二叉树查找 3.6 二叉树销毁 4.二叉树的基础OJ题 4.1 单值…...
Claude 使用指南 | 可与GPT-4媲美的语言模型
本文全程干货,让你轻松使用上claude,这也是目前体验cluade的唯一途径!废话不多说,直接上教程,cluade的能力不逊于GPT4,号称是ChatGPT4.0最强竞品。相对Chatgpt来说,Claude不仅是完全免费的&…...
【汇编】微处理器
【汇编】微处理器 文章目录 【汇编】微处理器1、微处理器概念1.1 关键词1.2 分类 2、微处理器结构2.1 寄存器2.2 寄存器&汇编助记符2.3 寄存器组成结构 3、地址空间3.1 存储空间3.1.1 虚拟空间(编程空间)3.1.2 线性空间 3.2 I/O空间 4、工作模式4.1 …...
按键点亮led灯
原理图: K0这个按键按下时,开发板D1这个灯亮,松开,灯灭 代码如下: #include "stm32f4xx.h" void LED_Init(void) {//1.定义一个GPIO外设的结构体变量 GPIO_InitTypeDef GPIO_InitStructure;//RCC_AHB1PeriphClockCmd(RCC_AHB1Pe…...
Java常见面试题
目录 1、mysql并发事务会带来哪些问题,如何解决?2、请详细描述Redis持久化机制?3、简述Redis缓存雪崩和缓存穿透的问题和解决方案?4、RabbitMQ消息丢失及对应解决方案5、什么叫线程安全?举例说明6、举例说明常用的加密…...
笔记1.5:计算机网络体系结构
从功能上描述计算机网络结构 分层结构 每层遵循某个网络协议完成本层功能 基本概念 实体:表示任何可发送或接收信息的硬件或软件进程。 协议是控制两个对等实体进行通信的规则的集合,协议是水平的。 任一层实体需要使用下层服务,遵循本层…...
【Python】Python 连接字符串应优先使用 join 而不是 +
Python 连接字符串应优先使用 join 而不是 简介 字符串处理在大多数编程程序语言中都不可避免,字符串的连接也是在编程过程中经常需要面对的问题。 Python中的字符串与其他一些程序语言如C、Java有一些不同,它为不 可变对象。 一旦创建便不能改变&…...
uniapp 小程序 父组件调用子组件方法
答案:配合小程序API > this.selectComponent(""),来选择组件,再使用$vm选择组件实例,再调用方法,或者data 1 设置组件的id,如果你的多端,请跟据情况设置ref,class,id,以便通过小…...
Vue-01:MVVM数据双向绑定与Vue的生命周期
一、Vue介绍 1.1 什么是Vue ? Vue是一个渐进式的JavaScript框架,用于构建用户界面。"渐进式"意味着Vue的设计理念是逐步增强应用的功能和复杂性,而不是一次性地引入所有功能。这使得开发者可以根据项目需求选择性地使用Vue的不同特…...
数据通信网络之OSPFv3基础
文章及资源归档至【AIShareLab】,回复 通信系统与网络 可获取。 文章目录 一、目的二、拓扑三、需求四、步骤 一、目的 掌握路由器的IPv6 基础配置。掌握OSPFv3(单区域)的基础配置。 二、拓扑 如图1 所示,三台路由器R1、R2 和R…...
FPGA-结合协议时序实现UART收发器(五):串口顶层模块UART_TOP、例化PLL、UART_FIFO、uart_drive
FPGA-结合协议时序实现UART收发器(五):串口顶层模块UART_TOP、例化PLL、UART_FIFO、uart_drive 串口顶层模块UART_TOP、例化PLL、UART_FIFO、uart_drive,功能实现。 文章目录 FPGA-结合协议时序实现UART收发器(五&…...
我学编程全靠B站了,真香-国外篇(第三期)
你好,我是Martin。 今天来点猛料,给大家推荐点我的压箱收藏-国外知名大学的公开课。 我推荐的不多,本着少就是多的原则,只给大家推荐我看过最好的五门视频,主要是来自两所国外高校:MIT美国麻省理工、CMU卡…...
c++ 变量常量指针练习题
Q1:在win32 x86模式下,int *p; int **pp; double *q; 请说明p、pp、q各占几个字节的内存单元。 p 占 4 个字节 pp 占 4 个字节 q 占 4 个字节 Q2常量1、1.0、“1”的数据类型是什么? 1 是 整形 int 1.0 是 浮点型 double “1” 是 const char * Q3 语句&…...
Linux底层基础知识
一.汇编,C语言,C,JAVA之间的关系 汇编,C语言,C可以通过不同的编译器,编译成机器码。而java只能由Java虚拟机识别。Java虚拟机可以看成一个操作系统,Java虚拟机是由汇编,C,…...
JUC并发编程--------线程安全篇
目录 什么是线程安全性问题? 如何实现线程安全? 1、线程封闭 2、无状态的类 3、让类不可变 4、加锁和CAS 并发环境下的线程安全问题有哪些? 1、死锁 2、活锁 3、线程饥饿 什么是线程安全性问题? 我们可以这么理解&#…...
机器视觉之Basler工业相机使用和配置方法(C++)
basler工业相机做双目视觉用,出现很多问题记录一下: 首先是多看手册:https://zh.docs.baslerweb.com/software 手册内有所有的源码和参考示例,实际上在使用过程中,大部分都是这些源码,具体项目选择对应的…...
Centos nginx配置文档
1、安装nginx: yum install nginx 2、Nginx常用命令 查看版本:nginx -v 启动:nginx -c /etc/nginx/nginx.conf 重新加载配置:nginx -s reload 停止:nginx -s stop 3、Nginx反向代理配置 nginx配置详解 1、Nginx配置图 详情可以查看:http://nginx.org/ru/docs/example…...
2023/9/14 -- C++/QT
作业: 仿照Vector实现MyVector,最主要实现二倍扩容 #include <iostream>using namespace std;template <typename T> class MyVector { private:T *data;size_t size;size_t V_capacity; public://无参构造MyVector():data(nullptr),size(…...
golang在goland编译时获取环境变量失效
在golang中, 我们通常使用os包来获取环境变量,如: os.Getenv() os.LookupEnv() 等。 但如果我们使用goland编译器,在编译是,这时操作环境变量,会发现os包读取到的环境变量值不变: 新增后&am…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
用鸿蒙HarmonyOS5实现中国象棋小游戏的过程
下面是一个基于鸿蒙OS (HarmonyOS) 的中国象棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chinesechess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├──…...
高分辨率图像合成归一化流扩展
大家读完觉得有帮助记得关注和点赞!!! 1 摘要 我们提出了STARFlow,一种基于归一化流的可扩展生成模型,它在高分辨率图像合成方面取得了强大的性能。STARFlow的主要构建块是Transformer自回归流(TARFlow&am…...
Copilot for Xcode (iOS的 AI辅助编程)
Copilot for Xcode 简介Copilot下载与安装 体验环境要求下载最新的安装包安装登录系统权限设置 AI辅助编程生成注释代码补全简单需求代码生成辅助编程行间代码生成注释联想 代码生成 总结 简介 尝试使用了Copilot,它能根据上下文补全代码,快速生成常用…...
