欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

GAMES101-现代计算机图形学学习笔记(作业02)

程序员文章站 2022-07-14 10:01:22
...

GAMES101-现代计算机图形学学习笔记(作业02)

原课程视频链接以及官网
b站视频链接: link.
课程官网链接: link.

作业

作业描述

在上次作业中,虽然我们在屏幕上画出一个线框三角形,但这看起来并不是 那么的有趣。所以这一次我们继续推进一步——在屏幕上画出一个实心三角形, 换言之,栅格化一个三角形。上一次作业中,在视口变化之后,我们调用了函数 rasterize_wireframe(const Triangle& t)。但这一次,你需要自己填写并调用函数 rasterize_triangle(const Triangle& t)。

提高篇:用 super-sampling 处理 Anti-aliasing : 你可能会注意到,当我们放大图像时,图像边缘会有锯齿感。我们可以用 super-sampling 来解决这个问题,即对每个像素进行 2 * 2 采样,并比较前后的结果 (这里 并不需要考虑像素与像素间的样本复用)。需要注意的点有,对于像素内的每一个样本都需要维护它自己的深度值,即每一个像素都需要维护一个 sample list。最后,如果你实现正确的话,你得到的三角形不应该有不正常的黑边。

需要补充的函数

rasterize_triangle(): 执行三角形栅格化算法
static bool insideTriangle(): 测试点是否在三角形内。你可以修改此函 数的定义,这意味着,你可以按照自己的方式更新返回类型或函数参数。

思路

①三角形栅格算法

作业要求已经给出了*rasterize_triangle()*函数的工作流程:

  1. 创建三角形的 2 维 bounding box。
  2. 遍历此 bounding box 内的所有像素(使用其整数索引)。然后,使用像素中心的屏幕空间坐标来检查中心点是否在三角形内。
  3. 如果在内部,则将其位置处的插值深度值 (interpolated depth value) 与深度 缓冲区 (depth buffer) 中的相应值进行比较。
  4. 如果当前点更靠近相机,请设置像素颜色并更新深度缓冲区 (depth buffer)。

老师这里要求实现的是Barycentric Algorithm(三角形栅格化),下面几个点可能需要注意一下:
①寻找三角形的bounding box:根据三角形的三个坐标,找出最大x,最小x,最大y,最小y坐标即可,注意最小x,y坐标需要取比原数小,最大x,y坐标需要取比原数大
②点是否在三角形内:只需要连接点和三角形个点,形成一系列向量,然后判断它们的叉乘结果的z值是否统一即可
③MSAA 4X 深度:把一个点看成一个格子,判断里面4个小点是否落在三角形内,然后找到其中插值的最小z值,和帧缓冲中的z值进行比较替换即可。
③MSAA 4X 颜色
判断有4个小点中有几个小点落入三角形,然后按比例对颜色进行采样即可。

光栅化函数:

//Screen space rasterization
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
    auto v = t.toVector4();

	// 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 = (int)std::floor(min_x);
	max_x = (int)std::ceil(max_x);
	min_y = (int)std::floor(min_y);
	max_y = (int)std::ceil(max_y);

	bool MSAA = false;
	//MSAA 4X
	if (MSAA) {
		// 格子里的细分四个小点坐标
		std::vector<Eigen::Vector2f> pos
		{
			{0.25,0.25},
			{0.75,0.25},
			{0.25,0.75},
			{0.75,0.75},
		};
		for (int x = min_x; x <= max_x; x++) {
			for (int y = min_y; y <= max_y; y++) {
				// 记录最小深度
				float minDepth = FLT_MAX;
				// 四个小点中落入三角形中的点的个数
				int count = 0;
				// 对四个小点坐标进行判断 
				for (int i = 0; i < 4; i++) {
					// 小点是否在三角形内
					if (insideTriangle((float)x + pos[i][0], (float)y + pos[i][1], t.v)) {
						// 如果在,对深度z进行插值
						auto tup = computeBarycentric2D((float)x + pos[i][0], (float)y + pos[i][1], t.v);
						float alpha;
						float beta;
						float gamma;
						std::tie(alpha, beta, gamma) = tup;
						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 != 0) {
					if (depth_buf[get_index(x, y)] > minDepth) {
						Vector3f color = t.getColor() * count / 4.0;
						Vector3f point(3);
						point << (float)x, (float)y, minDepth;
						// 替换深度
						depth_buf[get_index(x, y)] = minDepth;
						// 修改颜色
						set_pixel(point, color);
					}
				}
			}
		}
	}
	else {
		for (int x = min_x; x <= max_x; x++) {
			for (int y = min_y; y <= max_y; y++) {
				if (insideTriangle((float)x + 0.5, (float)y + 0.5, t.v)) {
					auto tup = computeBarycentric2D((float)x + 0.5, (float)y + 0.5, t.v);
					float alpha;
					float beta;
					float gamma;
					std::tie(alpha, beta, gamma) = tup;
					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(x, y)] > z_interpolated) {
						Vector3f color = t.getColor();
						Vector3f point(3);
						point << (float)x, (float)y, z_interpolated;
						depth_buf[get_index(x, y)] = z_interpolated;
						set_pixel(point, color);
					}
				}
			}
		}
	}
}

判断点是否位于三角形内函数

static bool insideTriangle(double x, double 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]
	Eigen::Vector2f p;
	p << x, y;

	Eigen::Vector2f AB = _v[1].head(2) - _v[0].head(2);
	Eigen::Vector2f BC = _v[2].head(2) - _v[1].head(2);
	Eigen::Vector2f CA = _v[0].head(2) - _v[2].head(2);

	Eigen::Vector2f AP = p - _v[0].head(2);
	Eigen::Vector2f BP = p - _v[1].head(2);
	Eigen::Vector2f CP = p - _v[2].head(2);
	
	// 判断每个z坐标是否统一
	return AB[0] * AP[1] - AB[1] * AP[0] > 0 
		&& BC[0] * BP[1] - BC[1] * BP[0] > 0
		&& CA[0] * CP[1] - CA[1] * CP[0] > 0;
}

结果

以下是结果:
GAMES101-现代计算机图形学学习笔记(作业02)
以下依次是没有做MSAA 4X的结果和做了MSAA 4X的结果:
GAMES101-现代计算机图形学学习笔记(作业02)
GAMES101-现代计算机图形学学习笔记(作业02)

这个光栅化算法其实是比较慢的,一个是自己实现的问题,第二个是从bounding box来看,会扫描很多多余的像素。这里贴几个链接,里面提到了其他的光栅化算法,以后有机会可以改成其他光栅化的算法来实现:
链接: http://www.sunshine2k.de/coding/java/TriangleRasterization/TriangleRasterization.html#algo1.
https://www.thecrazyprogrammer.com/2017/01/bresenhams-line-drawing-algorithm-c-c.html.
https://blog.csdn.net/cppyin/article/details/6232453.

相关标签: 图形学