Games101 Asignment01

- 编写代码构建模型视图变换矩阵

Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
    Eigen::Matrix4f model = Eigen::Matrix4f::Identity();

    // TODO: Implement this function
    // Create the model matrix for rotating the triangle around the Z axis.
    // Then return it.

    float sin_rotation_angle = sin(rotation_angle / 180.0 * acos(-1));
    float cos_rotation_angle = cos(rotation_angle / 180.0 * acos(-1));
    model <<
    cos_rotation_angle, -sin_rotation_angle, 0, 0,
    sin_rotation_angle, cos_rotation_angle, 0, 0,
    0, 0, 1, 0,
    0, 0, 0, 1;

    return model;
}

Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,float zNear, float zFar)
{
    // Students will implement this function

    Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();

    // TODO: Implement this function
    // Create the projection matrix for the given parameters.
    // Then return it.

    // Matrix for M_persp -> M_troho
    Eigen::Matrix4f M_persp_troth = Eigen::Matrix4f::Identity();
    M_persp_troth << zNear, 0, 0, 0,
    0, zNear, 0, 0,
    0, 0, zNear + zFar, -(zNear * zFar),
    0, 0, 1, 0;

    // value of l,f,t,b;
    float top, bottom, left, right;
    top = tan(eye_fov / 2) * abs(zNear);
    bottom = (-1) * top;
    right = aspect_ratio * top;
    left = (-1) *right;

    // Matrix for move to origin.
    Eigen::Matrix4f M_move = Eigen::Matrix4f::Identity();
    M_move << 1, 0, 0, (-1) * (left + right) / 2,
    0, 1, 0, (-1) * (top + bottom) / 2,
    0, 0, 1, (-1) * (zNear +  zFar) / 2,
    0, 0, 0, 1;

    // Matrix for scale.
    Eigen::Matrix4f M_scale = Eigen::Matrix4f::Identity();
    M_scale <<
    2 / (right - left), 0, 0, 0,
    0, 2 / (top - bottom), 0, 0,
    0, 0, 2 / (zNear - zFar), 0,
    0, 0, 0, 1;

    // Matrix for ortho transformation.
    Eigen::Matrix4f M_ortho = Eigen::Matrix4f::Identity();
    M_ortho = M_scale * M_move;

    projection = M_ortho * M_persp_troth;

    return projection;
}

 

- 简单分析下光栅器的代码,先从main入手

int main(int argc, const char** argv)
{
    float angle = 0;
    bool command_line = false;
    std::string filename = "output.png";

    if (argc >= 3) {
        command_line = true;
        angle = std::stof(argv[2]); // -r by default
        if (argc == 4) {
            filename = std::string(argv[3]);
        }
        else
            return 0;
    }


    // 定义一个700x700的光栅器,构造函数会根据参数创建二维数组frame_buf和deepth_buf
    rst::rasterizer r(700, 700);
    // 相机朝向的向量
    Eigen::Vector3f eye_pos = {0, 0, 5};

    // 三角形的三个顶点坐标
    std::vector<Eigen::Vector3f> pos{{2, 0, -2}, {0, 2, -2}, {-2, 0, -2}};

    // 应该是三角形的id,值的意义是对应的三个顶点在向量中的下标
    std::vector<Eigen::Vector3i> ind{{0, 1, 2}};

    /* 
    rst::pos_buf_id rst::rasterizer::load_positions(const std::vector<Eigen::Vector3f> &positions)
    {
        auto id = get_next_id();
        pos_buf.emplace(id, positions);
        return {id};
    }
    pos_buf是一个map,这个函数作用是为每个三角形坐标的顶点信息指定一个id,并存到下标为id的map中,最后返回该id
    */
    auto pos_id = r.load_positions(pos);

    /*
    rst::ind_buf_id rst::rasterizer::load_indices(const std::vector<Eigen::Vector3i> &indices)
    {
        auto id = get_next_id();
        ind_buf.emplace(id, indices);
        return {id};
    }
    迷惑,为什么要这么写
    功能和load_positions类似,只不过换成了存储和标记下标
    */
   //关于这两个函数如何工作详见draw方法
    auto ind_id = r.load_indices(ind);


    int key = 0;
    int frame_count = 0;

    if (command_line) {
        /*
        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());
            }
        }
        Buffers是一个枚举类型,clear函数的功能是如果深度或颜色参数与该枚举类中的值一样,则将深度无限大或颜色设置为(0,0,0)
        使用这个枚举类型以达到初始化frame_buf与depth_buf的目的
        */
        r.clear(rst::Buffers::Color | rst::Buffers::Depth);

        // 设置变换矩阵
        r.set_model(get_model_matrix(angle));
        r.set_view(get_view_matrix(eye_pos));
        // 这里设置了光锥的fovY为45,aspect为1(700x700),n为0.1与f为50,可以尝试修改观察变化
        r.set_projection(get_projection_matrix(45, 1, 0.1, 50));

        // draw函数绘制三角形
        r.draw(pos_id, ind_id, rst::Primitive::Triangle);
        cv::Mat image(700, 700, CV_32FC3, r.frame_buffer().data());
        image.convertTo(image, CV_8UC3, 1.0f);

        cv::imwrite(filename, image);

        return 0;
    }

    // 与上面的参数方法一样
    while (key != 27) {

        r.clear(rst::Buffers::Color | rst::Buffers::Depth);

        r.set_model(get_model_matrix(angle));
        r.set_view(get_view_matrix(eye_pos));
        r.set_projection(get_projection_matrix(45, 1, 0.1, 50));

        r.draw(pos_id, ind_id, rst::Primitive::Triangle);

        cv::Mat image(700, 700, CV_32FC3, r.frame_buffer().data());
        image.convertTo(image, CV_8UC3, 1.0f);
        cv::imshow("T_image", image);
        key = cv::waitKey(10);

        std::cout << "frame count: " << frame_count++ << '\n';

        if (key == 'a') {
            angle += 10;
        }
        else if (key == 'd') {
            angle -= 10;
        }
    }

    return 0;
}

 

- draw函数用于绘制三角形

void rst::rasterizer::draw(rst::pos_buf_id pos_buffer, rst::ind_buf_id ind_buffer, rst::Primitive type)
{
    if (type != rst::Primitive::Triangle)
    {
        throw std::runtime_error("Drawing primitives other than triangle is not implemented yet!");
    }

    // 根据参数获得id
    auto& buf = pos_buf[pos_buffer.pos_id];
    auto& ind = ind_buf[ind_buffer.ind_id];

    // 没用到不知道是干什么的
    float f1 = (100 - 0.1) / 2.0;
    float f2 = (100 + 0.1) / 2.0;

    // 计算mvp变换矩阵
    Eigen::Matrix4f mvp = projection * view * model;
    // 遍历每一个存储的ind
    for (auto& i : ind)
    {
        Triangle t;

        // 每个点做mvp变换
        Eigen::Vector4f v[] = {
                mvp * to_vec4(buf[i[0]], 1.0f),
                mvp * to_vec4(buf[i[1]], 1.0f),
                mvp * to_vec4(buf[i[2]], 1.0f)
        };

        // 归一化
        for (auto& vec : v) {
            vec /= vec.w();
        }

        // 变换到屏幕空间
        for (auto & vert : v)
        {
            vert.x() = 0.5*width*(vert.x()+1.0);
            vert.y() = 0.5*height*(vert.y()+1.0);
            vert.z() = vert.z() * f1 + f2;
        }

        // 设置三个顶点的坐标,从每个齐次坐标中取前三个值
        for (int i = 0; i < 3; ++i)
        {
            t.setVertex(i, v[i].head<3>());
            t.setVertex(i, v[i].head<3>());
            t.setVertex(i, v[i].head<3>());
        }


        t.setColor(0, 255.0,  0.0,  0.0);
        t.setColor(1, 0.0  ,255.0,  0.0);
        t.setColor(2, 0.0  ,  0.0,255.0);


        /*
        void rst::rasterizer::rasterize_wireframe(const Triangle& t)
        {
            draw_line(t.c(), t.a());
            draw_line(t.c(), t.b());
            draw_line(t.b(), t.a());
        }
        */
        // 光栅化三条线段,这个应该很好算
        rasterize_wireframe(t);
    }
}

 

 

- 光栅器中使用了draw_line方法绘制线段

- draw_line是Bresenham's line algorithm,由Bresenham提出的一种精确而有效的光栅线生成算法,可用于显示线、圆和其他曲线的整数增量运算,目前最有效的线段生成算法,推导可见:https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm

****

void rst::rasterizer::draw_line(Eigen::Vector3f begin, Eigen::Vector3f end)
{
    auto x1 = begin.x();
    auto y1 = begin.y();
    auto x2 = end.x();
    auto y2 = end.y();

    Eigen::Vector3f line_color = {255, 255, 255};

    int x,y,dx,dy,dx1,dy1,px,py,xe,ye,i;

    dx=x2-x1;
    dy=y2-y1;
    dx1=fabs(dx);
    dy1=fabs(dy);
    px=2*dy1-dx1;
    py=2*dx1-dy1;

    if(dy1<=dx1)
    {
        if(dx>=0)
        {
            x=x1;
            y=y1;
            xe=x2;
        }
        else
        {
            x=x2;
            y=y2;
            xe=x1;
        }
        Eigen::Vector3f point = Eigen::Vector3f(x, y, 1.0f);
        set_pixel(point,line_color);
        for(i=0;x<xe;i++)
        {
            x=x+1;
            if(px<0)
            {
                px=px+2*dy1;
            }
            else
            {
                if((dx<0 && dy<0) || (dx>0 && dy>0))
                {
                    y=y+1;
                }
                else
                {
                    y=y-1;
                }
                px=px+2*(dy1-dx1);
            }
//            delay(0);
            Eigen::Vector3f point = Eigen::Vector3f(x, y, 1.0f);
            set_pixel(point,line_color);
        }
    }
    else
    {
        if(dy>=0)
        {
            x=x1;
            y=y1;
            ye=y2;
        }
        else
        {
            x=x2;
            y=y2;
            ye=y1;
        }
        Eigen::Vector3f point = Eigen::Vector3f(x, y, 1.0f);
        set_pixel(point,line_color);
        for(i=0;y<ye;i++)
        {
            y=y+1;
            if(py<=0)
            {
                py=py+2*dx1;
            }
            else
            {
                if((dx<0 && dy<0) || (dx>0 && dy>0))
                {
                    x=x+1;
                }
                else
                {
                    x=x-1;
                }
                py=py+2*(dx1-dy1);
            }
//            delay(0);
            Eigen::Vector3f point = Eigen::Vector3f(x, y, 1.0f);
            set_pixel(point,line_color);
        }
    }
}

 

posted @ 2023-12-03 01:17  memo2586  阅读(64)  评论(0)    收藏  举报