前言
本人是菜B,这节课的作业琢磨了很久,力求将每个知识点搞懂,特写下博客记录,既是总结方法论,也希望能帮助到大家
前置知识
1.需要先完成作业1并且理解三角形光栅化课堂的内容,包括光栅化的步骤,MSAA超采样
2.建议熟悉框架再来动手,否则很难发现问题解决问题
作业目标
如何光栅化三角形?
老师的pdf已经说的很明确了,就不多说了
三角形光栅化代码
void rasterizer::rasterize_triangle(const Triangle& t) {
auto v = t.toVector4();
int minx, maxx, miny, maxy; //定义一个三角形的覆盖面boundingBox
minx = min(v[0].x(), min(v[1].x(), v[2].x()));
maxx = max(v[0].x(), max(v[1].x(), v[2].x()));
miny = min(v[0].y(), min(v[1].y(), v[2].y()));
maxy = max(v[0].y(), max(v[1].y(), v[2].y()));
//循环判断每个点是否在三角形内
for (int x = minx; x <= maxx; ++x) {
for (int y = miny; y <= maxy; ++y) {
if (insideTriangle(x + 0.5, y + 0.5, t.v)) {
//求深度插值
float alpha, beta, gamma;
tie(alpha, beta, gamma) = computeBarycentric2D(x+0.5, y+0.5, t.v); //取tuple值用tie流式比较简单
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;
//光栅化
auto ind = get_index(x, y);
if (z_interpolated < depth_buf[ind]) {
depth_buf[ind] = z_interpolated;
Vector3f point = Vector3f((float)x,(float)y, z_interpolated);
Vector3f color = t.getColor();
set_pixel(point, color);
}
}
}
}
// If so, use the following code to get the interpolated z value.
// TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.
}
如何判断采样点是否在三角形内?
有很多方法,这里采用叉乘同向判断,具体可以看这个博客Games101-作业问题整理
代码
static bool insideTriangle(float x, float y, const Vector3f* _v)
{
Vector3f Q = Vector3f(x, y, 1);
Vector3f v0v1 = _v[1] - _v[0];
Vector3f v0Q = Q - _v[0];
float z0 = v0v1.cross(v0Q).z();
Vector3f v1v2 = _v[2] - _v[1];
Vector3f v1Q = Q - _v[1];
float z1 = v1v2.cross(v1Q).z();
Vector3f v2v0 = _v[0] - _v[2];
Vector3f v2Q = Q - _v[2];
float z2 = v2v0.cross(v2Q).z();
if (z0 > 0) {
return z1 > 0 && z2 > 0;
}
return z0<0 && z1<0 && z2<0; //向量叉积的正负在z轴显示,而且同号才表示在三角形内
}
PS:之前检查错误没发现这里有错,导致我梳理了好久才发现错误,记得叉乘一定是同号才能认为在三角形内
提高题
作业要求中使用的是SSAA,这里使用MSAA,我的笔记已经写的很清楚怎么算了,与一般采样相比,MSAA无非就是多了3个采样点,对每个采样点检测是否在三角形内,最后着色的时候注意覆盖三角形的边界着色
但是需要注意一个容易漏的点,对于4个采样点,每个采样点都需要维护它自己的深度值,这其实就涉及到对框架的改动
//rasterizer.hcpp代码
std::vector<Eigen::Vector3f> frame_buf;
std::vector<Eigen::Vector3f> super_frame_buf;
std::vector<float> depth_buf; //其实只要跟踪这个变量的引用照葫芦画瓢就行
std::vector<float> super_depth_buf;
//rasterizer.cpp代码
//主要作用是清理缓存
void rasterizer::clear(Buffers buff)
{
if ((buff & Buffers::Color) == Buffers::Color)
{
fill(frame_buf.begin(), frame_buf.end(), Vector3f{ 0, 0, 0 });
fill(super_frame_buf.begin(), super_frame_buf.end(), Vector3f{ 0, 0, 0 });
}
if ((buff & Buffers::Depth) == Buffers::Depth)
{
fill(depth_buf.begin(), depth_buf.end(), numeric_limits<float>::infinity());
fill(super_depth_buf.begin(), super_depth_buf.end(), numeric_limits<float>::infinity());
}
}
//主要作用是定义4倍大小的分辨率
rasterizer::rasterizer(int w, int h) : width(w), height(h)
{
frame_buf.resize(w * h);
depth_buf.resize(w * h);
super_frame_buf.resize(4 * w * h);
super_depth_buf.resize(4 * w * h);
}
MSAA的处理已经很明显了,就是分辨率也要改变;这里其实还需要注意作业1中的MVP变换,如果在MVP变换中f改为了-f,那么会出现覆盖问题,所以f要改回来
int rst::rasterizer::get_super_index(int x, int y) //2x2MSAA采样索引
{
return (height * 2 - 1 - y) * width * 2 + x;
}
vector<Vector2f> super_sample_point
{
{0.25,0.25},
{0.75,0.25},
{0.25,0.75},
{0.75,0.75},
};
//Screen space rasterization
void rasterizer::rasterize_triangle(const Triangle& t) {
auto v = t.toVector4();
int minx, maxx, miny, maxy; //定义一个三角形的覆盖面boundingBox
minx = min(v[0].x(), min(v[1].x(), v[2].x()));
maxx = max(v[0].x(), max(v[1].x(), v[2].x()));
miny = min(v[0].y(), min(v[1].y(), v[2].y()));
maxy = max(v[0].y(), max(v[1].y(), v[2].y()));
//循环判断每个点是否在三角形内
for (int x = minx; x <= maxx; ++x) {
for (int y = miny; y <= maxy; ++y) {
bool flag = false; //是否通过深度测试
for (int k = 0; k < 4; k++) {
if (insideTriangle(x + super_sample_point[k][0], y + super_sample_point[k][1], t.v)) {
//求深度插值
float alpha, beta, gamma;
tie(alpha, beta, gamma) = computeBarycentric2D(x + 0.5, y + 0.5, t.v); //取tuple值用tie流式比较简单
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;
//光栅化
auto ind = get_super_index(x * 2 + k % 2, y * 2 + k / 2);
if (z_interpolated < super_depth_buf[ind]) {
flag = true;
super_depth_buf[ind] = z_interpolated; //深度缓存
super_frame_buf[ind] = t.getColor(); //颜色缓存,解决子像素点重叠黑边问题
}
}
}
if (flag) {
int a = get_super_index(x * 2, y * 2); //MSAA分辨率下的4个超采样点
int b = get_super_index(x * 2 + 1, y * 2);
int c = get_super_index(x * 2, y * 2 + 1);
int d = get_super_index(x * 2 + 1, y * 2 + 1);
Vector3f point = Vector3f((float)x, (float)y, 0);
Vector3f color = (super_frame_buf[a] + super_frame_buf[b] + super_frame_buf[c] + super_frame_buf[d]) / 4;
set_pixel(point, color);
}
}
}
// If so, use the following code to get the interpolated z value.
// TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.
}