games101作业1

发布时间 2023-07-23 19:29:55作者: zhywyt

games101 HomeWork 1

说起来我自己写games101的作业也是曲曲折折,虚拟机很卡就拿VS配环境,Windows不会配环境,就装Linux,现在装上了Linux,却因为没有经验把Windows格式化了(我是真的沙比),好在还是开始做了,也挺顺利的,所以再来记录一下作业。

基础部分

这里需要完成两个函数,一个是模型变换矩阵,一个是透视投影矩阵。

模型变换矩阵

逐个元素地构建模型变换矩阵并返回该矩阵。在此函数中,你只需要实现三维中绕 z 轴旋转的变换矩阵,而不用处理平移与缩放。
这个部分的实现非常简单,只需要记住这个公式就好了。这里给出绕三个轴旋转的旋转矩阵:

image
代码实现:

Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
    Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
	//角度制转弧度制
    rotation_angle = rotation_angle / 180.0f * MY_PI;
    // TODO: Implement this function
    // Create the model matrix for rotating the triangle around the Z axis.
    // Then return it.
    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;
}

透视投影矩阵

使用给定的参数逐个元素地构建透视投影矩阵并返回
该矩阵。

这个题目的参数定义其实不是很明显(至少位蒙b了很久),先看一下函数原型:

Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)

来解读一下前两个参数,eye_fov指的是摄像机的垂直可视角度,aspect_ratio指的是摄像机的长宽比。使用这四个值也能算出正交投影矩阵。(下图只供参考)
image

我们从透视投影矩阵开始:
根据课堂上的推导,我们已经知道透视投影矩阵的最终结果,并且所需要的值只有zNearzFar,于是我们能直接写出这个矩阵:

        P << zNear, 0, 0, 0,
            0, zNear, 0, 0,
            0, 0, zNear + zFar, -zNear * zFar,
            0, 0, 1, 0;

然后是我们的正交投影部分了,正交投影需要用到的数据有六个,分别是长方体的参数。
$$[l,r]\times[b,t]\times[f,n]$$
先把正的数据处理掉,直接给出答案,再证明

  • \(t=zNear*tan(eye\_fov/2)\)(弧度化后)
  • \(r=t*aspect\_ratio\)
    另外的lb分别等于rt的相反数。
    最后还剩下nf,这两个数和他们的名字差别很大,分别是后和前,赋值的时候需要小心,而且他们的值并不是zNear和zFar分别赋值,而是相反数赋值。如下
f = -zNear;
n = -zFar;

有了数据,我们就可进行投影矩阵的实现了,正交投影矩阵如下:

        eye_fov = eye_fov / 180 * MY_PI;
        float l, r, b, t, n, f;
        //注意这里的f和n代表的意义
        f = -zNear;
        n = -zFar;
        t = -zNear * tan(eye_fov/2);
        b = -t;
        r = t * aspect_ratio;
        l = -r;
        S << 2 / (r - l), 0, 0, 0,
            0, 2 / (t - b), 0, 0,
            0, 0, 2 / (n - f), 0,
            0, 0, 0, 1;
        M << 1, 0, 0, -(r + l) / 2,
            0, 1, 0, -(t + b) / 2,
            0, 0, 1, -(n + f) / 2,
            0, 0, 0, 1;
		//S*M得到正交投影矩阵
普通要求代码汇总
//main.cpp
bool ProjectMode=true;//这是一个控制模式的参数
Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
    Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
    rotation_angle = rotation_angle / 180.0f * MY_PI;
    // TODO: Implement this function
    // Create the model matrix for rotating the triangle around the Z axis.
    // Then return it.
    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.
    if (!ProjectMode)
    {
        Eigen::Matrix4f S, M, P;
        eye_fov = eye_fov / 180 * MY_PI;
        float l, r, b, t, n, f;
        //注意这里的f和n代表的意义
        f = -zNear;
        n = -zFar;
        t = -zNear * tan(eye_fov/2);
        b = -t;
        r = t * aspect_ratio;
        l = -r;
        S << 2 / (r - l), 0, 0, 0,
            0, 2 / (t - b), 0, 0,
            0, 0, 2 / (n - f), 0,
            0, 0, 0, 1;
        M << 1, 0, 0, -(r + l) / 2,
            0, 1, 0, -(t + b) / 2,
            0, 0, 1, -(n + f) / 2,
            0, 0, 0, 1;
        P << zNear, 0, 0, 0,
            0, zNear, 0, 0,
            0, 0, zNear + zFar, -zNear * zFar,
            0, 0, 1, 0;
        return S * M * P;
    }
    else//这是精简版本
    {
        eye_fov = eye_fov / 180 * MY_PI;
        projection << 1 / (aspect_ratio * tan(eye_fov / 2.0f)), 0, 0, 0,
            0, 1 / tan(eye_fov / 2.0f), 0, 0,
            0, 0, -(zFar + zNear) / (zFar - zNear), 2 * zFar * zNear / (zNear - zFar),
            0, 0, -1, 0;
        return projection;
    }
}

提升部分

提升部分要求:在 main.cpp 中构造一个函数,该函数的作用是得到绕任意
过原点的轴的旋转变换矩阵。根据101中的推导,我们需要计算的东西并不多,按要求写好就行了。我选择重载get_model_matrix函数,来实现任意旋转轴的旋转操作。

image

Eigen::Matrix4f get_rotation(Vector3f axis, float rotation_angle)
{
    rotation_angle = rotation_angle / 180.0f * MY_PI;
    Eigen::Matrix4f Result = Eigen::Matrix4f::Identity();
    Eigen::Matrix3f I = Eigen::Matrix3f::Identity();//单位矩阵
    Eigen::Matrix3f N = Eigen::Matrix3f::Identity();
    Eigen::Matrix3f ResultMat3 = Eigen::Matrix3f::Identity();
    N << 0, -axis[2], axis[1],
        axis[2], 0, -axis[0],
        -axis[1], axis[0], 0;
    ResultMat3 = I * cos(rotation_angle) + (1 - cos(rotation_angle)) * axis * axis.transpose() + sin(rotation_angle) * N;
    Result << ResultMat3(0, 0), ResultMat3(0, 1), ResultMat3(0, 2), 0,
        ResultMat3(1, 0), ResultMat3(1, 1), ResultMat3(1, 2), 0,
        ResultMat3(2, 0), ResultMat3(2, 1), ResultMat3(2, 2), 0,
        0, 0, 0, 1;

    return Result;
}

其余代码和普通要求一致。得到结果如下:

结果

因为是第一次作业,所以我这里给出编译的操作:
先来到作业目录
image
创建build文件夹
image
来到build文件夹
image
使用上级目录创建项目文件
image
构建编译,这里的-j8是表示调用的核心数量,最后一句target Rasterizer表示可执行文件为Rasterizer
image
运行
image
image
按下Esc或者Ctrl+C停止
image
全部指令汇总

mkdir build
cd build
cmake ..
make -j8
./Rasterizer

普通要求

运行指令与结果

./Rasterizer 不带参数运行

image

./Rasterizer -r 0 output.png无旋转保存图片

image
image

./Rasterizer -r 90 output90.png旋转保存图片

image

提升要求

先介绍一下main函数的两个参数argcargv

  • argc全称arugment count,表示调用程序的时候,传入的参数的个数。
  • argv全称argument vector,表示调用程序的时候,传入的参数向量,类型为字符串
    默认的,argv[0]是调用程序的完整路径,然后argv[0]-argv[argc-1]都是可以访问的字符串。在程序中,我们可以使用标准库定义的stof函数,把字符串转成浮点型,或者使用stod把字符串转化成整形。知道这一点之后,我们就可以设计一个main函数来把我们自定义的旋转轴作为参数传入程序了。

这里是我自己设计的一个传参方案,有兴趣的可以读一下程序。

main.cpp
#include "Triangle.hpp"
#include "rasterizer.hpp"
#include <eigen3/Eigen/Eigen>
#include <iostream>
#include <opencv2/opencv.hpp>

constexpr double MY_PI = 3.1415926;
bool ProjectMode=true;

Eigen::Matrix4f get_view_matrix(Eigen::Vector3f eye_pos)
{
    Eigen::Matrix4f view = Eigen::Matrix4f::Identity();

    Eigen::Matrix4f translate;
    translate << 1, 0, 0, -eye_pos[0], 0, 1, 0, -eye_pos[1], 0, 0, 1,
        -eye_pos[2], 0, 0, 0, 1;

    view = translate * view;

    return view;
}

Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
    Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
    rotation_angle = rotation_angle / 180.0f * MY_PI;
    // TODO: Implement this function
    // Create the model matrix for rotating the triangle around the Z axis.
    // Then return it.
    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;
}

// Rodrigues rotation formula
Eigen::Matrix4f get_rotation(Vector3f axis, float angle)
{
    angle = angle / 180.0f * MY_PI;
    Eigen::Matrix4f Result = Eigen::Matrix4f::Identity();
    Eigen::Matrix3f E = Eigen::Matrix3f::Identity();
    Eigen::Matrix3f N = Eigen::Matrix3f::Identity();
    Eigen::Matrix3f ResultMat3 = Eigen::Matrix3f::Identity();
    N << 0, -axis[2], axis[1],
        axis[2], 0, -axis[0],
        -axis[1], axis[0], 0;
    ResultMat3 = E * cos(angle) + (1 - cos(angle)) * axis * axis.transpose() + sin(angle) * N;
    Result << ResultMat3(0, 0), ResultMat3(0, 1), ResultMat3(0, 2), 0,
        ResultMat3(1, 0), ResultMat3(1, 1), ResultMat3(1, 2), 0,
        ResultMat3(2, 0), ResultMat3(2, 1), ResultMat3(2, 2), 0,
        0, 0, 0, 1;

    return Result;
}


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.
    if (!ProjectMode)
    {
        Eigen::Matrix4f S, M, P;
        eye_fov = eye_fov / 180 * MY_PI;
        float l, r, b, t, n, f;
        //注意这里的f和n代表的意义
        f = -zNear;
        n = -zFar;
        t = -zNear * tan(eye_fov/2);
        b = -t;
        r = t * aspect_ratio;
        l = -r;
        S << 2 / (r - l), 0, 0, 0,
            0, 2 / (t - b), 0, 0,
            0, 0, 2 / (n - f), 0,
            0, 0, 0, 1;
        M << 1, 0, 0, -(r + l) / 2,
            0, 1, 0, -(t + b) / 2,
            0, 0, 1, -(n + f) / 2,
            0, 0, 0, 1;
        P << zNear, 0, 0, 0,
            0, zNear, 0, 0,
            0, 0, zNear + zFar, -zNear * zFar,
            0, 0, 1, 0;
        return S * M * P;
    }
    else
    {
        std::cout<<"true answer"<<std::endl;
        eye_fov = eye_fov / 180 * MY_PI;
        projection << 1 / (aspect_ratio * tan(eye_fov / 2.0f)), 0, 0, 0,
            0, 1 / tan(eye_fov / 2.0f), 0, 0,
            0, 0, -(zFar + zNear) / (zFar - zNear), 2 * zFar * zNear / (zNear - zFar),
            0, 0, -1, 0;
        return projection;
    }
}
int main(int argc, const char **argv)
{
    float angle = 0;
    bool command_line = false;
    Eigen::Vector3f axis = Eigen::Vector3f(0.f, 1.f, 0.f);
    std::string filename = "output.png";

    if (argc >= 3)
    {
        std::cout<<argc<<std::endl;

        angle = std::stof(argv[2]); // -r by default
        if (argc == 4)
        {
            command_line = true;
            filename = std::string(argv[3]);
        }
        else if(argc==6){//DIY(
        axis.x()=std::stof(argv[3]);
        axis.y()=std::stof(argv[4]);
        axis.z()=std::stof(argv[5]);
        axis.normalize();
        }
        //
    }

    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}};

    std::vector<Eigen::Vector3i> ind{{0, 1, 2}};

    auto pos_id = r.load_positions(pos);
    auto ind_id = r.load_indices(ind);

    int key = 0;
    int frame_count = 0;

    if (command_line)
    {
        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::imwrite(filename, image);

        return 0;
    }
 



    while (key != 27)
    {
        r.clear(rst::Buffers::Color | rst::Buffers::Depth);

        r.set_model(get_model_matrix(angle));
        //DIY
        if (argc == 6){
            r.set_model(get_rotation(axis, angle));
            std::cout<<"axis x:"<<axis.x()<<" y:"<<axis.y()<<" z:"<<axis.z()<<std::endl<<"angle:"<<angle<<std::endl;
        }
        //
        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("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;
}

部分效果如下:
image
games101 Hw1 到此就结束啦!