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:

放大一下

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