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

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;
}

结果

以下是结果:
在这里插入图片描述
以下依次是没有做MSAA 4X的结果和做了MSAA 4X的结果:
在这里插入图片描述
在这里插入图片描述

这个光栅化算法其实是比较慢的,一个是自己实现的问题,第二个是从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.

原创文章 9 获赞 3 访问量 509

猜你喜欢

转载自blog.csdn.net/qq_36242312/article/details/105758619
今日推荐