Z-Buffer(用来存储深度):储存每个样本(像素)的当前最小值,需要一个额外的缓冲区来存放深度值。为了简单起见,我们假设Z总是正的,较小的z—>近,较大的z—>远
Z-Buffer算法()
{
帧缓存全置为背景色
深度缓存全置为最小z值
for
(每一个多边形)
{
扫描转换该多边形
for
(该多边形所覆盖的每个象素(x,y) )
{
计算该多边形在该象素的深度值Z(x,y);
if
(z(x,y)大于z缓存在(x,y)的值)
{
把z(x,y)存入z缓存中(x,y)处
把多边形在(x,y)处的颜色值存入帧缓存的(x,y)处
}
}
}
}
完整代码参考:
void rst::rasterizer::rasterize_triangle(const Triangle& t, const std::array<Eigen::Vector3f, 3>& view_pos)
{
auto v = t.toVector4();
int min_x = std::min({ v[0].x(), v[1].x(), v[2].x() });
int max_x = std::max({ v[0].x(), v[1].x(), v[2].x() });
int min_y = std::min({ v[0].y(), v[1].y(), v[2].y() });
int max_y = std::max({ v[0].y(), v[1].y(), v[2].y() });
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 [alpha, beta, gamma] = computeBarycentric2D(x, y, t.v);
float Z = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
float zp = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
zp *= Z;
if (zp < depth_buf[get_index(x, y)])
{
// TODO: Interpolate the attributes:
// auto interpolated_color
// auto interpolated_normal
// auto interpolated_texcoords
// auto interpolated_shadingcoords
auto interpolated_color = interpolate(alpha, beta, gamma, t.color[0], t.color[1], t.color[2], 1);
auto interpolated_normal = interpolate(alpha, beta, gamma, t.normal[0], t.normal[1], t.normal[2], 1).normalized();
auto interpolated_texcoords = interpolate(alpha, beta, gamma, t.tex_coords[0], t.tex_coords[1], t.tex_coords[2], 1);
auto interpolated_shadingcoords = interpolate(alpha, beta, gamma, view_pos[0], view_pos[1], view_pos[2], 1);
fragment_shader_payload payload( interpolated_color, interpolated_normal.normalized(), interpolated_texcoords, texture ? &*texture : nullptr);
payload.view_pos = interpolated_shadingcoords;
// Use: Instead of passing the triangle's color directly to the frame buffer, pass the color to the shaders first to get the final color;
auto pixel_color = fragment_shader(payload);
depth_buf[get_index(x, y)] = zp;
set_pixel(Eigen::Vector2i(x, y), pixel_color);
}
}
}
}
}
view_pos[ ]是三角形顶点在view space中的坐标,插值是为了还原在camera space中的坐标
细节
1、如何找到包围盒
auto v = t.toVector4();
int min_x = std::min({ v[0].x(), v[1].x(), v[2].x() });
int max_x = std::max({ v[0].x(), v[1].x(), v[2].x() });
int min_y = std::min({ v[0].y(), v[1].y(), v[2].y() });
int max_y = std::max({ v[0].y(), v[1].y(), v[2].y() });
注释: 这里的v为Z-Buffer指针
2、判断该点是否在三角形内
static bool insideTriangle(int x, int y, const Vector4f* _v){
Vector3f v[3];
for(int i=0;i<3;i++)
v[i] = {_v[i].x(),_v[i].y(), 1.0};
Vector3f f0,f1,f2;
f0 = v[1].cross(v[0]);
f1 = v[2].cross(v[1]);
f2 = v[0].cross(v[2]);
Vector3f p(x,y,1.);
if((p.dot(f0)*f0.dot(v[2])>0) && (p.dot(f1)*f1.dot(v[0])>0) && (p.dot(f2)*f2.dot(v[1])>0))
return true;
return false;
}
质心(重心)坐标(Barycentric):
static std::tuple<float, float, float> computeBarycentric2D(float x, float y, const Vector4f* v){
float c1 = (x*(v[1].y() - v[2].y()) + (v[2].x() - v[1].x())*y + v[1].x()*v[2].y() - v[2].x()*v[1].y()) / (v[0].x()*(v[1].y() - v[2].y()) + (v[2].x() - v[1].x())*v[0].y() + v[1].x()*v[2].y() - v[2].x()*v[1].y());
float c2 = (x*(v[2].y() - v[0].y()) + (v[0].x() - v[2].x())*y + v[2].x()*v[0].y() - v[0].x()*v[2].y()) / (v[1].x()*(v[2].y() - v[0].y()) + (v[0].x() - v[2].x())*v[1].y() + v[2].x()*v[0].y() - v[0].x()*v[2].y());
float c3 = (x*(v[0].y() - v[1].y()) + (v[1].x() - v[0].x())*y + v[0].x()*v[1].y() - v[1].x()*v[0].y()) / (v[2].x()*(v[0].y() - v[1].y()) + (v[1].x() - v[0].x())*v[2].y() + v[0].x()*v[1].y() - v[1].x()*v[0].y());
return {c1,c2,c3};
}