GAMES101作业2
作业任务:
填写并调用函数 rasterize_triangle(const Triangle& t)。
即实现光栅化
该函数的内部工作流程如下:
- 创建三角形的 2 维 bounding box。
- 遍历此 bounding box 内的所有像素(使用其整数索引)。然后,使用像素中
心的屏幕空间坐标来检查中心点是否在三角形内。 - 如果在内部,则将其位置处的插值深度值 (interpolated depth value) 与深度
缓冲区 (depth buffer) 中的相应值进行比较。 - 如果当前点更靠近相机,请设置像素颜色并更新深度缓冲区 (depth buffer)。
你需要修改的函数如下:
• rasterize_triangle(): 执行三角形栅格化算法
• static bool insideTriangle(): 测试点是否在三角形内。你可以修改此函
数的定义,这意味着,你可以按照自己的方式更新返回类型或函数参数。
开始实现:
在进行光栅化的时候,我们需要判断某个点是否在triangle内,来决定是否着色。
所以我们第一步实现
static bool insideTriangle()
x:表示观测点的x值
y:表示观测点的y值
_v:是一个vector<Vector3f>,内含三角形的三个点,每个点存有x、y、z值
static bool insideTriangle(double x, double y, const Vector3f* _v)
{
Eigen::Vector2f p; //检测目标
p << x, y;
//.head(2)指这个点的前两个数值,即x,y
Eigen::Vector2f a, b, c; //被检测的三角形三边向量
a = _v[0].head(2) - _v[1].head(2); //a = A - B 即B->A
b = _v[1].head(2) - _v[2].head(2); //b = B - C 即C->B
c = _v[2].head(2) - _v[0].head(2); //c = C - A 即A->C
Eigen::Vector2f AP, BP, CP;
AP = p - _v[0].head(2);
BP = p - _v[1].head(2);
CP = p - _v[2].head(2);
//由于我这里的向量方向都是逆时针的,所以如果点p在内,那么所有边向量叉乘对应的XP都应该为正值,指向z的正半轴。
return AP[0] * c[1] - AP[1] * c[0] > 0 && BP[0] * a[1] - BP[1] * a[0] > 0 && CP[0] * b[1] - CP[1] * b[0] > 0;
}
现在来实现
rasterize_triangle(const Triangle& t)
分析:
第一步 确定bounding box
给定一个三角形,我们先要扫描出这个三角占据了哪些格子(像素单元),即定义bounding box,取得这个三角形的x和y轴上的边界值,使得这个bounding box尽可能的小,但又必须包裹住这个三角形。第二步 扫描bounding box
逐一扫描这个bounding box内的所有单元,以每个单元的中心位置代替此单元,将坐标传入insideTriangle函数中,如果不在,则扫描下一个,如果在,则以三角形三点做插值得到这个中心点的z值,进一步判断,这个z值是否比深度缓存中对应位置的值要小,不小则不处理,小则替换深度缓存中的值为当前值。随后设置这个单元的颜色值。
这里很容易就能想到,如果这个单元尺寸越小,就能更加精确的确定边界。也就是MSAA,多重采样抗锯齿,这个并不是从物理上将单元切割成更小的,而是将一个单元看成多个单元组合,然后对这个更小单元进行采样。每个单元的尺寸越小,成像的边界锯齿效果就越虚,即成像效果越好,但这样会使效率降低,将一个单元切割成2x2的四个小单元,会使原来一个单位的工作量升至四个单位的工作量。
//Screen space rasterization
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
auto v = t.toVector4();//这里是把三角形结构体换成三个顶点如(x1,y1,z1,1)
//还记得嘛?最后的1表示它是一个点,用于齐次坐标
//bounding box
float min_x = std::min(v[0][0], std::min(v[1][0], v[2][0]));
float max_x = std::max(v[0][0], std::max(v[1][0], v[2][0]));
float min_y = std::min(v[0][1], std::min(v[1][1], v[2][1]));
float max_y = std::max(v[0][1], std::max(v[1][1], v[2][1]));
min_x = static_cast<int>(std::floor(min_x));
min_y = static_cast<int>(std::floor(min_y));
max_x = static_cast<int>(std::ceil(max_x));
max_y = static_cast<int>(std::ceil(max_y));
//左边界小数部分全部直接舍,右边界小数部分直接入,确保单元边界坐标都是整数,三角形一定在bounding box内。
bool MSAA = true;//MSAA是否启用
if (MSAA)
{
std::vector<Eigen::Vector2f> pos
{ //对一个像素分割四份 当然你还可以分成4x4 8x8等等甚至你还可以为了某种特殊情况设计成不规则的图形来分割单元
{0.25,0.25}, //左下
{0.75,0.25}, //右下
{0.25,0.75}, //左上
{0.75,0.75} //右上
};
for (int i = min_x; i <= max_x; ++i)
{
for (int j = min_y; j <= max_y; ++j)
{
int count = 0;
float minDepth = FLT_MAX;
for (int MSAA_4 = 0; MSAA_4 < 4; ++MSAA_4)
{
if (insideTriangle(static_cast<float>(i+pos[MSAA_4][0]), static_cast<float>(j+pos[MSAA_4][1]),t.v))
{
auto[alpha, beta, gamma] = computeBarycentric2D(static_cast<float>(i + pos[MSAA_4][0]), static_cast<float>(j + pos[MSAA_4][1]), 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;
minDepth = std::min(minDepth, z_interpolated);
++count;
}
}
if (count)
{
if (depth_buf[get_index(i, j)] > minDepth)
{
depth_buf[get_index(i, j)] = minDepth;//更新深度
Eigen::Vector3f color = t.getColor() * (count / 4.0);//对颜色进行平均,使得边界更平滑,也是一种模糊的手段
Eigen::Vector3f point;
point << static_cast<float>(i), static_cast<float>(j), minDepth;
set_pixel(point, color);//设置颜色
}
}
}
}
}
else
{
for (int i = min_x; i <= max_x; ++i)
{
for (int j = min_y; j <= max_y; ++j)
{
if (insideTriangle(static_cast<float>(i+0.5), static_cast<float>(j+0.5),t.v))
{
auto [alpha, beta, gamma] = computeBarycentric2D(static_cast<float>(i + 0.5), static_cast<float>(j + 0.5), 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;
if (depth_buf[get_index(i, j)] > z_interpolated)
{
depth_buf[get_index(i, j)] = z_interpolated;//更新深度
Eigen::Vector3f color = t.getColor();
Eigen::Vector3f point;
point << static_cast<float>(i), static_cast<float>(j), z_interpolated;
set_pixel(point, color);//设置颜色
}
}
}
}
}
}
根据三角形三点求得观测点的z插值,此时视频还没有讲解,是直接使用的框架中的代码。
MSAA = true

MSAA = false

细节对比


还是比较明显的吧!
GAMES101作业2的更多相关文章
- 【UE4】GAMES101 图形学作业2:光栅化和深度缓存
总览 在上次作业中,虽然我们在屏幕上画出一个线框三角形,但这看起来并不是那么的有趣.所以这一次我们继续推进一步--在屏幕上画出一个实心三角形,换言之,栅格化一个三角形.上一次作业中,在视口变化之后,我 ...
- 【UE4】GAMES101 图形学作业5:光线与物体相交(球、三角面)
总览 在这部分的课程中,我们将专注于使用光线追踪来渲染图像.在光线追踪中最重要的操作之一就是找到光线与物体的交点.一旦找到光线与物体的交点,就可以执行着色并返回像素颜色. 在这次作业中,我们要实现两个 ...
- 【UE4】GAMES101 图形学作业4:贝塞尔曲线
总览 Bézier 曲线是一种用于计算机图形学的参数曲线. 在本次作业中,你需要实现de Casteljau 算法来绘制由4 个控制点表示的Bézier 曲线(当你正确实现该算法时,你可以支持绘制由更 ...
- 【UE4】GAMES101 图形学作业3:Blinn-Phong 模型与着色
总览 在这次编程任务中,我们会进一步模拟现代图形技术.我们在代码中添加了Object Loader(用于加载三维模型), Vertex Shader 与Fragment Shader,并且支持了纹理映 ...
- 【UE4】GAMES101 图形学作业1:mvp 模型、视图、投影变换
总览 到目前为止,我们已经学习了如何使用矩阵变换来排列二维或三维空间中的对象.所以现在是时候通过实现一些简单的变换矩阵来获得一些实际经验了.在接下来的三次作业中,我们将要求你去模拟一个基于CPU 的光 ...
- 【UE4】GAMES101 图形学作业0:矩阵初识
作业描述 给定一个点P=(2,1), 将该点绕原点先逆时针旋转45◦,再平移(1,2), 计算出变换后点的坐标(要求用齐次坐标进行计算). UE4 知识点 主要矩阵 FMatrix FBasisVec ...
- python10作业思路及源码:类Fabric主机管理程序开发(仅供参考)
类Fabric主机管理程序开发 一,作业要求 1, 运行程序列出主机组或者主机列表(已完成) 2,选择指定主机或主机组(已完成) 3,选择主机或主机组传送文件(上传/下载)(已完成) 4,充分使用多线 ...
- SQLServer2005创建定时作业任务
SQLServer定时作业任务:即数据库自动按照定时执行的作业任务,具有周期性不需要人工干预的特点 创建步骤:(使用最高权限的账户登录--sa) 一.启动SQL Server代理(SQL Server ...
- 使用T-SQL找出执行时间过长的作业
有些时候,有些作业遇到问题执行时间过长,因此我写了一个脚本可以根据历史记录,找出执行时间过长的作业,在监控中就可以及时发现这些作业并尽早解决,代码如下: SELECT sj.name , ...
随机推荐
- react slot component with args
react slot component with args how to pass args to react props child component https://codesandbox.i ...
- js jsonParse
mdn const rx_one = /^[\],:{}\s]*$/; const rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; // 匹配 ...
- JS实现点击加载更多效果
适用场景:后端直接把所有的文章都给你调出来了,但是领导又让做点击加载更多效果...(宝宝心里苦啊) 点击加载更多效果: 第一个和第二个参数分别是btn和ul的DOM(必填) ...
- 「NGK每日快讯」12.14日NGK公链第41期官方快讯!
- 你真的知道typeof null的结果为什么是‘object‘吗?
到目前为止,ECMAScript 标准中定义了8种数据类型,它们分别是Undefined.Null.Number.Boolean.String.Symbol.BigInt.Object. 为了判断变量 ...
- oracle impdp ORA-02304 invalid object identifier literal
reference: https://webgeest.blogspot.com/2015/07/ora-39083-ora-02304-on-impdp-datapump.html 解决方法 ...
- 解决springboot项目打成jar包部署到linux服务器后上传图片无法访问的问题
前言:目前大三,自己也在学习和摸索的阶段.在和学校的同学一起做前后端分离项目的时候,我们发现将后端打包成jar,然后部署到服务器中通过java -jar xxx.jar运行项目以后,项目中存在文件上传 ...
- PAT-1133(Splitting A Linked List)vector的应用+链表+思维
Splitting A Linked List PAT-1133 本题一开始我是完全按照构建链表的数据结构来模拟的,后来发现可以完全使用两个vector来解决 一个重要的性质就是位置是相对不变的. # ...
- POJ-1182(经典带权并查集)
食物链 POJ-1182 一个很好的分析博客:https://blog.csdn.net/niushuai666/article/details/6981689 三种关系:两者同类,吃父节点,被父节点 ...
- Fedora一键安装NVIDIA显卡驱动Fedora28+
这是一篇以前写的文章,写在CSDN的,现在不想使用CSDN了,就把笔记写在了博客源,后续考虑建立自己的博客,每一个CRUD程序员都想建立自己的博客吧,我猜是的 进入正题 rpm fusion源包含Nv ...