GAMES101 作业2

作业栅格化一个三角形。进阶是super-sampling

insideTriangle

// TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v[2]
/**
 * @brief 判断一个像素点在不在三角形内 
 * @note   
 * @param  x: 像素横座标
 * @param  y: 像素纵座标
 * @param  _v: 三角形顶点
 * @retval true:在三角形内;false:不在
 *
*/
static bool insideTriangle(int x, int y, const Vector3f* _v)
{   

    // 叉乘同号 那么这个点在三角形内部
    Eigen::Vector3f P(x,y,0);
    std::array<Eigen::Vector3f, 3> res;
    for(int i=0;i<3;i++){
        Eigen::Vector3f A(_v[i](0),_v[i](1),0);
        Eigen::Vector3f B(_v[(i+1)%3](0),_v[(i+1)%3](1),0);

        Eigen::Vector3f AB=B-A;
        Eigen::Vector3f BP=P-B;
        
        res.at(i)=AB.cross(BP);
    }
    bool flag=true;
    for(int i=0;i<3;i++){
        Eigen::Vector3f res1=res.at(i);
        Eigen::Vector3f res2=res.at((i+1)%3);

        if(res1.dot(res2)<0){
            flag=false;
            break;
        }
    }
    return flag;
}

rasterize_triangle

注意这里是使用的投影矩阵没有发现翻转z的迹象,还是右手系,z越大越靠近摄像机

//Screen space rasterization
// TODO : Find out the bounding box of current triangle.
// iterate through the pixel and find if the current pixel is inside the triangle
// TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.
/**
 * @brief  将已经在正交投影下的三角形光栅化
 * @note   
 * @param  t: 三角形
 * @retval None
 *
*/
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
    auto v = t.toVector4();
    
    // 三角形包围盒
    float x_max=v.at(0)(0); 
    float x_min=v.at(0)(0);
    float y_max=v.at(0)(1); 
    float y_min=v.at(0)(1);
    for(auto vv:v){
        if(vv(0)>x_max){
            x_max=vv(0);
        }
        if(vv(0)<x_min){
            x_min=vv(0);
        }
        if(vv(1)>y_max){
            y_max=vv(1);
        }
        if(vv(1)<y_min){
            y_min=vv(1);
        }
    }

    for(int x=x_min;x<=x_max;x++){
        for(int y=y_min;y<=y_max;y++){
            // 每一个像素中心是不是在三角形中
            if(insideTriangle(x+0.5, y+0.5 , t.v)){
                // If so, use the following code to get the interpolated z value.
                auto[alpha, beta, gamma] = computeBarycentric2D(x+0.5, y+0.5, 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;

                int depth_index=get_index(x+0.5,y+0.5);
                if(depth_buf[depth_index]==std::numeric_limits<float>::infinity()||z_interpolated > depth_buf[depth_index]){
                    // 这里并没有找到有关翻转z轴的代码,认为没有翻转,因此越大的数值应该越在前面
                    // 渲染
                    Eigen::Vector3f point(x+0.5,y+0.5,z_interpolated);
                    set_pixel(point,t.getColor());

                    // 更新zBuffer
                    depth_buf[depth_index]=z_interpolated;
                }
            }
        }
    }
    
}

进阶

写完整个思路之后,看了一些大佬的总结。补充一下相关知识:super-sampling 处理 Anti-aliasing也就是SSAA。是把每个像素的分块都记录下来(这就要维护新的sample list)然后最后渲染之前再平均给buffer里面。与之类似有一个简便计算的MSAA。在当前情况下,SSAA和MSAA区别不是很大,因为SSAA是(写不下去,先马住,有精力再写:参考教程https://www.zhihu.com/question/496212252和https://zhuanlan.zhihu.com/p/484890144)

super-sampling 处理 Anti-aliasing(SSAA)

具体思路是先将每一个像素分成2×2块,然后遍历存入一个depth_buf_supersample和frame_buf_supersample的buffer,记录每一个分块的颜色和深度。然后再将分块的值平均给depth_buf和frame_buffer。

代码实现

定义SUPER_SAMPLING控制超采样:

#ifndef DEFINITION_H
#define DEFINITION_H

#define SUPER_SAMPLING


#endif //DEFINITION_H

要判断点的分块是否在三角形内,要做浮点数上的判断(int直接变整数了):


// TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v[2]
/**
 * @brief 判断一个像素点在不在三角形内 
 * @note   
 * @param  x: 像素横座标
 * @param  y: 像素纵座标
 * @param  _v: 三角形顶点
 * @retval true:在三角形内;false:不在
 *
*/
static bool insideTriangle_float(float x, float y, const Vector3f* _v)
{   

    // 叉乘同号 那么这个点在三角形内部
    Eigen::Vector3f P(x,y,0);
    std::array<Eigen::Vector3f, 3> res;
    for(int i=0;i<3;i++){
        Eigen::Vector3f A(_v[i](0),_v[i](1),0);
        Eigen::Vector3f B(_v[(i+1)%3](0),_v[(i+1)%3](1),0);

        Eigen::Vector3f AB=B-A;
        Eigen::Vector3f BP=P-B;
        
        res.at(i)=AB.cross(BP);
    }
    bool flag=true;
    for(int i=0;i<3;i++){
        Eigen::Vector3f res1=res.at(i);
        Eigen::Vector3f res2=res.at((i+1)%3);

        if(res1.dot(res2)<0){
            flag=false;
            break;
        }
    }
    return flag;
}

在rasterizer.hpp中增加数据结构和控制supersample的参数

#ifdef SUPER_SAMPLING
        std::vector<std::vector<float>> depth_buf_supersample;
        std::vector<std::vector<Eigen::Vector3f>> frame_buf_supersample;
        int x_size=3,y_size=3;// 将一个像素分为x_size*y_size块
#endif

总的代码实现如下:

//Screen space rasterization
// TODO : Find out the bounding box of current triangle.
// iterate through the pixel and find if the current pixel is inside the triangle
// TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.
/**
 * @brief  将已经在正交投影下的三角形光栅化
 * @note   
 * @param  t: 三角形
 * @retval None
 *
*/
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
    auto v = t.toVector4();
    
    // 三角形包围盒
    float x_max=v.at(0)(0); 
    float x_min=v.at(0)(0);
    float y_max=v.at(0)(1); 
    float y_min=v.at(0)(1);
    for(auto vv:v){
        if(vv(0)>x_max){
            x_max=vv(0);
        }
        if(vv(0)<x_min){
            x_min=vv(0);
        }
        if(vv(1)>y_max){
            y_max=vv(1);
        }
        if(vv(1)<y_min){
            y_min=vv(1);
        }
    }
#ifdef SUPER_SAMPLING
    for(int x=x_min;x<=x_max;x++){
        for(int y=y_min;y<=y_max;y++){
            // 对每一个像素进行supersimple
            //在三角形内部的分块 填充 depth_buf_supersample 和 frame_buf_supersample 
            bool isUpdate=false;
            for(int i=0;i<x_size;i++){
                for(int j=0;j<y_size;j++){
                    float x_index=x+0.5/x_size+i*1.0/x_size;
                    float y_index=y+0.5/y_size+j*1.0/y_size;
                    if(insideTriangle_float(x_index,y_index,t.v)){
                        isUpdate=true;
                        // 计算z
                        auto[alpha, beta, gamma] = computeBarycentric2D(x_index, y_index, 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;

                        int depth_index=get_index(x+0.5, y+0.5);
                        int supersampling_index=i*x_size+j;
                        if(depth_buf_supersample[depth_index][supersampling_index]==std::numeric_limits<float>::infinity()||z_interpolated > depth_buf_supersample[depth_index][supersampling_index]){
                            // 这里并没有找到有关翻转z轴的代码,认为没有翻转,因此越大的数值应该越在前面
                            // 渲染
                            Eigen::Vector3f point(x,y,z_interpolated);
                            auto ind = (height-1-point.y())*width + point.x();
                            frame_buf_supersample[ind][supersampling_index] = t.getColor();

                            // 更新zBuffer
                            depth_buf_supersample[depth_index][supersampling_index]=z_interpolated;
                        }
                    }
                }
            }
            
            if(isUpdate){// 这个像素有在三角形内部的部分 更新 frame_buf 和 depth_buf
                // 更新 depth_buf
                int depth_index=get_index(x+0.5,y+0.5);
                float z_average=0; // 因为有更新 一定有深度
                for(float zzz:depth_buf_supersample[depth_index]){
                    if(zzz!=std::numeric_limits<float>::infinity())
                        z_average+=zzz;
                }
                z_average/=(x_size*y_size);
                depth_buf[depth_index]=z_average;

                // 更新frame_buf
                auto ind = (height-1-y)*width + x;
                Eigen::Vector3f color_average(0,0,0);
                for(Vector3f ccc:frame_buf_supersample[ind])// 写到这里我不禁思考 RGB通道每个通道取平均应该是合理的吧?
                {
                    color_average=color_average+ccc;
                }
                color_average=color_average/(x_size*y_size);
                frame_buf[ind]=color_average;

            }
            
        }
    }
#else
    for(int x=x_min;x<=x_max;x++){
        for(int y=y_min;y<=y_max;y++){
            // 每一个像素中心是不是在三角形中
            if(insideTriangle(x+0.5, y+0.5 , t.v)){
                // If so, use the following code to get the interpolated z value.
                auto[alpha, beta, gamma] = computeBarycentric2D(x+0.5, y+0.5, 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;

                int depth_index=get_index(x+0.5,y+0.5);
                if(depth_buf[depth_index]==std::numeric_limits<float>::infinity()||z_interpolated > depth_buf[depth_index]){
                    // 这里并没有找到有关翻转z轴的代码,认为没有翻转,因此越大的数值应该越在前面
                    // 渲染
                    Eigen::Vector3f point(x,y,z_interpolated);
                    set_pixel(point,t.getColor());

                    // 更新zBuffer
                    depth_buf[depth_index]=z_interpolated;
                }
            }
        }
    }
#endif
}

depth_buf_supersample和frame_buf_supersample的初始化和更新

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});
    }
    if ((buff & rst::Buffers::Depth) == rst::Buffers::Depth)
    {
        std::fill(depth_buf.begin(), depth_buf.end(), std::numeric_limits<float>::infinity());
    }

#ifdef SUPER_SAMPLING
    // 如果是动态的话 这里也需要清除
    if ((buff & rst::Buffers::Depth_supersampling) == rst::Buffers::Depth_supersampling)
    {
        std::vector<float> dept;
        dept.resize(x_size*y_size);
        std::fill(dept.begin(), dept.end(), std::numeric_limits<float>::infinity());
        std::fill(depth_buf_supersample.begin(), depth_buf_supersample.end(), dept);// 这里是深拷贝还是浅拷贝????
    }
    if ((buff & rst::Buffers::Color_supersampling) == rst::Buffers::Color_supersampling)
    {
        std::vector<Eigen::Vector3f> col;
        col.resize(x_size*y_size);
        std::fill(col.begin(), col.end(), Eigen::Vector3f{0, 0, 0});
        std::fill(frame_buf_supersample.begin(), frame_buf_supersample.end(), col);// 这里是深拷贝还是浅拷贝????
    }
#endif
}

rst::rasterizer::rasterizer(int w, int h) : width(w), height(h)
{
    frame_buf.resize(w * h);
    depth_buf.resize(w * h);

#ifdef SUPER_SAMPLING
    depth_buf_supersample.resize(w * h);
    for(int i=0;i<w;i++){
        for(int j=0;j<h;j++){
            int index=i*w+j;
            depth_buf_supersample[index].resize(x_size*y_size);
        }
    }
    frame_buf_supersample.resize(w*h);
    for(int i=0;i<w;i++){
        for(int j=0;j<h;j++){
            int index=i*w+j;
            frame_buf_supersample[index].resize(x_size*y_size);
        }
    }
#endif
}

运行结果

1*1:

2*2:

3*3:

放大一下

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章