games101 实验hw1(作业1)

本文详细介绍了在GAMES101课程中如何构建和应用绕z轴旋转的模型矩阵、视图矩阵以及透视和正交投影矩阵,展示了如何通过MVP过程实现三维图形的变换和渲染,包括使用罗德里格斯旋转公式进行任意轴旋转。
摘要由CSDN通过智能技术生成

参考文章:
GAMES101作业(更新中)
GAMES101】作业1(提高)与框架理解
GAMES101 学习笔记 Lecture 1~6
闫令琪:Games101 现代计算机图形学-作业Assignment02解析

关于变换的流程

a.旋转矩阵:作业02中省略
b.平移矩阵:把相机的位置平移到原点,物体也会被平移
c.仿射变换:把透视相机变成平行相机
d.平移矩阵:把变成平行相机的相机空间移到原点
e.缩放矩阵:把相机空间缩放到[-1,1]
f.把齐次坐标的第四分量也就是w分量变为1
g.把[-1,1]的坐标空间映射到屏幕空间,也就是映射到有多少分辨
这才算是全部的变换完成,然后接下来才是光栅化。
完成变化之后,便是光栅化

三部分:

MVP的过程
在这里插入图片描述

Model Transformation(绕z轴旋转的变换矩阵)

在这里插入图片描述

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.

    //角度转弧度,便于计算
    rotation_angle = rotation_angle / 180.0f * MY_PI;

    //模型旋转矩阵(绕z轴)
    Eigen::Matrix4f translate;
    translate << cos(rotation_angle), -sin(rotation_angle), 0, 0,
                 sin(rotation_angle),  cos(rotation_angle), 0, 0,
                                   0,                    0, 1, 0,
                                   0,                    0, 0, 1;

    model = translate * model;

    return model;
}

View Transformation(代码中已经写好)

在这里插入图片描述
Tview已经写好,而Rview没用上

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

透视投影矩阵(Projection Transformation)

在这里插入图片描述
在这里插入图片描述

步骤1:挤压

在这里插入图片描述

步骤2:正交投影

在这里插入图片描述

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.

    eye_fov = eye_fov / 180.0f * MY_PI;
    //求出所需参数
    float height = tan(eye_fov / 2.0f) * abs(zNear) * 2.0f;
    float width = height * aspect_ratio;
    float length = zNear - zFar;

    //透视投影中将frustum给挤压成一个长方体
    Eigen::Matrix4f p2o;
    p2o << zNear,      0,            0,             0,
                0, zNear,            0,             0,
                0,     0, zNear + zFar, -zNear * zFar,
                0,     0,            1,             0;

    Eigen::Matrix4f orthoT;
   
    //以下两个为正交投影
    orthoT << 1.0f,    0,    0,                      0,
                 0, 1.0f,    0,                      0,
                 0,    0, 1.0f, -(zNear + zFar) / 2.0f,
                 0,    0,    0,                   1.0f;

    Eigen::Matrix4f orthoS;
    //在观测投影时xoy平面视角默认为中心,所以不需要平移x和y了
    orthoS << 2.0f / width,             0,             0, 0,
                         0, 2.0f / height,             0, 0,
                         0,             0, 2.0f / length, 0,
                         0,             0,             0, 1;
   
    //1.先挤压 2.正交投影
    projection = orthoS * orthoT * p2o * projection;
   
    return projection;
}

到绕任意过原点的轴的旋转变换矩阵

在这里插入图片描述

Eigen::Matrix4f get_rotation(Vector3f axis, float angle) {//任意轴旋转矩阵(罗德里格斯旋转公式,默认轴过原点)
    double fangle = angle / 180 * MY_PI;
    Eigen::Matrix4f I, N, Rod;
    Eigen::Vector4f axi;
    Eigen::RowVector4f taxi;

    axi << axis.x(), axis.y(), axis.z(), 0;

    I.Identity();

    N << 0, -axis.z(), axis.y(), 0,
         axis.z(), 0, -axis.x(), 0,
         -axis.y(), axis.x(), 0, 0,
         0, 0, 0, 1;
    
    Rod = cos(fangle) * I + (1 - cos(fangle)) * axi * axi.transpose() + sin(fangle) * N;
    Rod(3, 3) = 1;//这里要注意,非齐次坐标的公式应用在齐次坐标上时记得运算完成后把矩阵的右下角改为1,否则会导致图形比例错误
    return Rod;
}

总代码

main.cpp

#include "Triangle.hpp"
#include "rasterizer.hpp"
#include <eigen3/Eigen/Eigen>
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace Eigen;
constexpr double MY_PI = 3.1415926;

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_rotation(Vector3f axis, float angle)
{
    // 罗德里格旋转公式
    Eigen::Matrix4f rot = Eigen::Matrix4f::Identity();

    angle = angle / 180.0f * MY_PI;

    Eigen::Matrix3f N;
    N <<       0, -axis[2],  axis[1],
             axis[2],        0, -axis[0],
            -axis[1],  axis[0],        0,
                   0,        0,        0;

    Eigen::Matrix3f translate3f;

    translate3f << cos(angle)       * Eigen::Matrix3f::Identity()
                 + (1 - cos(angle)) * (axis * axis.transpose())
                 + sin(angle)       * N;

    Eigen::Matrix4f translate4f;
    translate4f.block<3, 3>(0, 0) = translate3f;

    translate4f(0, 3) = 0.0f;
    translate4f(1, 3) = 0.0f;
    translate4f(2, 3) = 0.0f;
    translate4f(3, 3) = 1.0f;

    rot = translate4f * rot;

    return rot;
}

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.

    //角度转弧度,便于计算
    rotation_angle = rotation_angle / 180.0f * MY_PI;

    //模型旋转矩阵(绕z轴)
    Eigen::Matrix4f translate;
    translate << cos(rotation_angle), -sin(rotation_angle), 0, 0,
                 sin(rotation_angle),  cos(rotation_angle), 0, 0,
                                   0,                    0, 1, 0,
                                   0,                    0, 0, 1;

    model = translate * model;

    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.

    eye_fov = eye_fov / 180.0f * MY_PI;
    //求出所需参数
    float height = tan(eye_fov / 2.0f) * abs(zNear) * 2.0f;
    float width = height * aspect_ratio;
    float length = zNear - zFar;

    //透视投影中将frustum给挤压成一个长方体
    Eigen::Matrix4f p2o;
    p2o << zNear,      0,            0,             0,
                0, zNear,            0,             0,
                0,     0, zNear + zFar, -zNear * zFar,
                0,     0,            1,             0;

    Eigen::Matrix4f orthoT;
   
    //以下两个为正交投影
    orthoT << 1.0f,    0,    0,                      0,
                 0, 1.0f,    0,                      0,
                 0,    0, 1.0f, -(zNear + zFar) / 2.0f,
                 0,    0,    0,                   1.0f;

    Eigen::Matrix4f orthoS;
    //在观测投影时xoy平面视角默认为中心,所以不需要平移x和y了
    orthoS << 2.0f / width,             0,             0, 0,
                         0, 2.0f / height,             0, 0,
                         0,             0, 2.0f / length, 0,
                         0,             0,             0, 1;
   
    //1.先挤压 2.正交投影
    projection = orthoS * orthoT * p2o * projection;
   
    return projection;
}


int main(int argc, const char** argv)
{
    float angle = 0;//定义角度
    bool command_line = false;//定义命令行开关标志,默认为关
    std::string filename = "output.png";//定义文件名,默认为output.png"

    Eigen::Vector3f raxis(0, 0, 1);
    double rangle = 0, ra;

    if (argc >= 3) {//接收到的参数大于三个,即检测到通过命令行传入参数时
    	cout<<"argc[1]="<<argv[1]<<endl;
	cout<<"argc[2]="<<argv[2]<<endl;
	cout<<"argc[3]="<<argv[3]<<endl;
        command_line = true;//设命令行开关标志为开
        angle = std::stof(argv[2]); //从命令行获取角度参数
        if (argc == 4) {//接收到的参数为四个,那么说明命令行输入了文件名参数
            filename = std::string(argv[3]);//从命令行获取文件名
        }
    }

    rst::rasterizer r(700, 700);//设定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) {//如果命令行开关标志为开(这一段if代码是为了应用命令行传入的参数,比如初始角度和文件名)

        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));//向光栅器传入MVP矩阵
        r.set_rodrigues(get_rotation(raxis, rangle));

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

    bool rflag = false;

    std::cout << "Please enter the axis and angle:" << std::endl;
    std::cin >> raxis.x() >> raxis.y() >> raxis.z() >> ra;//定义罗德里格斯旋转轴和角

    while (key != 27) {//只要没有检测到按下ESC就循环(ESC的ASCII码是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));

        if (rflag) //如果按下r了,就开始绕给定任意轴旋转
            r.set_rodrigues(get_rotation(raxis, rangle));
        else
            r.set_rodrigues(get_rotation({ 0,0,1 }, 0));

        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);//等待10号码接收键盘输入,没有输入就为空,图像不做调整,保持原状

        std::cout << "frame count: " << frame_count++ << '\n';//显示当前是第几帧画面

        if (key == 'a') {//按下a,逆时针旋转10°
            angle += 10;
        }
        else if (key == 'd') {//按下d,顺时针旋转10°
            angle -= 10;
        }
        else if (key == 'r') {//按下r,绕给定任意轴旋转
            rflag = true;
            rangle += ra;
        }
    }

    return 0;

}



在这部分的课程中,我们将专注于使用光线追踪来渲染图像。在光线追踪中 最重要的操作之一就是找到光线与物体的交点。一旦找到光线与物体的交点,就 可以执行着色并返回像素颜色。在这次作业中,我们需要实现两个部分:光线的 生成和光线与三角的相交。本次代码框架的工作流程为: 1. 从 main 函数开始。我们定义场景的参数,添加物体(球体或三角形)到场景 中,并设置其材质,然后将光源添加到场景中。 2. 调用 Render(scene) 函数。在遍历所有像素的循环里,生成对应的光线并将 返回的颜色保存在帧缓冲区(framebuffer)中。在渲染过程结束后,帧缓冲 区中的信息将被保存为图像。 3. 在生成像素对应的光线后,我们调用 CastRay 函数,该函数调用 trace 来 查询光线与场景中最近的对象的交点。 4. 然后,我们在此交点执行着色。我们设置了三种不同的着色情况,并且已经 为你提供了代码。 你需要修改的函数是: • Renderer.cpp 中的 Render():这里你需要为每个像素生成一条对应的光 线,然后调用函数 castRay() 来得到颜色,最后将颜色存储在帧缓冲区的相 应像素中。 • Triangle.hpp 中的 rayTriangleIntersect(): v0, v1, v2 是三角形的三个 顶点, orig 是光线的起点, dir 是光线单位化的方向向量。 tnear, u, v 是你需 要使用我们课上推导的 Moller-Trumbore 算法来更新的参数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值