GAMES101 作业 02:Triangles and Z-buffering
作业 02 的实现代码:Assignment02
1. insideTriangle
static bool insideTriangle(float x, float 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]
Vector3f p(x, y, 1.0);
Vector3f pa = _v[0] - p;
Vector3f pb = _v[1] - p;
Vector3f pc = _v[2] - p;
Vector3f ab = _v[1] - _v[0];
Vector3f bc = _v[2] - _v[1];
Vector3f ca = _v[0] - _v[2];
float paCab = pa.cross(ab)[2];
float pbCbc = pb.cross(bc)[2];
float pcCca = pc.cross(ca)[2];
if (paCab > 0 && pbCbc > 0 && pcCca > 0) {
return true;
}
if (paCab < 0 && pbCbc < 0 && pcCca < 0) {
return true;
}
return false;
}
2. rasterize_triangle
//Screen space rasterization
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
auto v = t.toVector4();
// TODO : Find out the bounding box of current triangle.
auto [minX, maxX, minY, maxY] = calBoundingBox2D(v);
// iterate through the pixel and find if the current pixel is inside the triangle
for (int y = minY; y <= maxY; ++y) {
for (int x = minX; x <= maxX; ++x) {
int index = get_index(x, y);
float minDepth = std::numeric_limits<float>::infinity();
Vector3f pixelColor = {0.0, 0.0, 0.0};
bool isInside = false;
for (int di = 0; di < MSAA_N; ++di) {
float tx = float(x) + DX[di];
float ty = float(y) + DY[di];
if (insideTriangle(tx, ty, t.v)) {
// If so, use the following code to get the interpolated z value.
isInside = true;
auto[alpha, beta, gamma] = computeBarycentric2D(tx, ty, 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_sample_buf[index][di] > z_interpolated) {
depth_sample_buf[index][di] = z_interpolated;
frame_sample_buf[index][di] = t.getColor();
}
}
minDepth = std::min(minDepth, depth_sample_buf[index][di]);
pixelColor += (frame_sample_buf[index][di] / float(MSAA_N));
}
// TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.
if (isInside) {
set_pixel(Vector3f(float(x), float(y), minDepth), pixelColor);
}
}
}
}
下面仔细讲解一下。
2.1. calBoundingBox2D
static std::tuple<int, int, int, int> calBoundingBox2D(const std::array<Vector4f, 3> &v) {
int minX = int(floor(std::min(std::min(v[0][0], v[1][0]), v[2][0])));
int maxX = int(ceil(std::max(std::max(v[0][0], v[1][0]), v[2][0])));
int minY = int(floor(std::min(std::min(v[0][1], v[1][1]), v[2][1])));
int maxY = int(ceil(std::max(std::max(v[0][1], v[1][1]), v[2][1])));
return {minX, maxX, minY, maxY};
}
2.2. MSAA
2.2.1. MSAA 的一些常量
static const int MSAA_N = 2 * 2;
static const std::array<float, MSAA_N> DX, DY;
const std::array<float, rst::rasterizer::MSAA_N>
rst::rasterizer::DX = {0.25, 0.75, 0.25, 0.75};
const std::array<float, rst::rasterizer::MSAA_N>
rst::rasterizer::DY = {0.25, 0.25, 0.75, 0.75};
2.2.2. sampleList
对于每个像素的每个样本都需要管理颜色和深度,原来的 depth_buf 就没有用了。
std::vector<Eigen::Vector3f> frame_buf;
std::vector<std::array<Eigen::Vector3f, MSAA_N>> frame_sample_buf;
//std::vector<float> depth_buf;
std::vector<std::array<float, MSAA_N>> depth_sample_buf;
rst::rasterizer::rasterizer(int w, int h) : width(w), height(h)
{
frame_buf.resize(w * h);
frame_sample_buf.resize(w * h);
//depth_buf.resize(w * h);
depth_sample_buf.resize(w * h);
}
void rst::rasterizer::clear(rst::Buffers buff)
{
if ((buff & rst::Buffers::Color) == rst::Buffers::Color)
{
std::fill(frame_buf.begin(), frame_buf.end(), Eigen::Vector3f{0, 0, 0});
for (auto frameSampleList : frame_sample_buf) {
std::fill(frameSampleList.begin(), frameSampleList.end(), Eigen::Vector3f{0, 0, 0});
}
}
if ((buff & rst::Buffers::Depth) == rst::Buffers::Depth)
{
//std::fill(depth_buf.begin(), depth_buf.end(), std::numeric_limits<float>::infinity());
for (auto depthSampleList : depth_sample_buf) {
std::fill(depthSampleList.begin(), depthSampleList.end(), std::numeric_limits<float>::infinity());
}
}
}
2.2.3. MSAA 的实现
遍历每一个像素,然后遍历每个像素的每个样本;
假如样本在三角形之中,那么检查这个样本的 z 值;
假如 z 值比当前管理的 z 值小,即比原来的物品离视角更近,
那么保存样本的深度与颜色;
最后,取样本间最小的深度为此像素的深度,
样本颜色均值为此像素的颜色。
for (int y = minY; y <= maxY; ++y) {
for (int x = minX; x <= maxX; ++x) {
int index = get_index(x, y);
float minDepth = std::numeric_limits<float>::infinity();
Vector3f pixelColor = {0.0, 0.0, 0.0};
bool isInside = false;
for (int di = 0; di < MSAA_N; ++di) {
float tx = float(x) + DX[di];
float ty = float(y) + DY[di];
if (insideTriangle(tx, ty, t.v)) {
// If so, use the following code to get the interpolated z value.
isInside = true;
auto[alpha, beta, gamma] = computeBarycentric2D(tx, ty, 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_sample_buf[index][di] > z_interpolated) {
depth_sample_buf[index][di] = z_interpolated;
frame_sample_buf[index][di] = t.getColor();
}
}
minDepth = std::min(minDepth, depth_sample_buf[index][di]);
pixelColor += (frame_sample_buf[index][di] / float(MSAA_N));
}
// TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.
if (isInside) {
set_pixel(Vector3f(float(x), float(y), minDepth), pixelColor);
}