GAMES101(0~1作业)

搭建虚拟机环境

  1. 安装Oracle VM VirtualBox虚拟机,安装虚拟硬盘,配置Linux Ubuntu-64 bit系统,
  2. 启动虚拟机,发生冲突错误:
    1. 将Vmware虚拟设备取消挂起状态,关机
    2. 确保 Hyper-V 完全关闭:bcdedit /set hypervisorlaunchtype off
    3. 重启计算机
  3. 安装增强功能,未找到iso错误:ISO下载地址:Index of http://download.virtualbox.org/virtualbox
    1. 首先升级虚拟机版本置7.0.2
    2. 通过update Guest additions
    3. 最后安装增强功能
  4. 通过终端调用cmake编译文件,
    1. 使用硬盘中的vscode编写main.cpp
    2. 通过终端创建文件touch CMakeLists.txt
    3. 编写文件(作业框架里有)
    4. cmake编译目录
    5. make编译
    6. 执行运行程序
# 指定最低版本要求
cmake_minimum_required(VERSION 2.8.5)
# 项目名称
project(Hello)
# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 11)
#
find_package(Eigen3 REQUIRED)
include_directories(EIGEN3_INCLUDE_DIR)
# 添加源文件
add_executable(pa0 main.cpp)

 作业0:

题目

给定一个点 P=(2,1), 将该点绕原点先逆时针旋转 45◦,再平移 (1,2), 计算出 变换后点的坐标(要求用齐次坐标进行计算)。

首先为向量增加一个分量(1),将变换的值带入,SL:3*3的旋转(绕z) * 平移矩阵公式中,最后乘以具有3个分量的向量

    auto angle = 45 * 3.14 / 180.0f;
    Eigen::Matrix3f R_Mat3;
    R_Mat3 <<
        std::cos(angle),-std::sin(angle),0,
        std::sin(angle), std::cos(angle),0,
        0,0,1;
    Eigen::Matrix3f T_Mat3;
    T_Mat3 <<
        1,0,1,
        0,1,2,
        0,0,1;
    Eigen::Vector3f p(2.0f,1.0f,1.0f);
    auto result = T_Mat3 * R_Mat3* p;
    std::cout << result;

作业1(OpenCV,Eigen):

题目

在给定作业框架填写一个旋转矩阵get_model_matrix()和一个透视投影矩阵get_projection_matrix(),给定三维下三个 点 v0(2.0, 0.0, −2.0), v1(0.0, 2.0, −2.0), v2(−2.0, 0.0, −2.0), 你需要将这三个点的坐 标变换为屏幕坐标并在屏幕上绘制出对应的线框三角形

解:

旋转矩阵:就是利用绕z旋转矩阵公式

    double fangle = rotation_angle / 180 * MY_PI;
    Eigen::Matrix4f rotation;
    rotation << 
        cos(fangle), -sin(fangle), 0, 0,
        sin(fangle), cos(fangle), 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1;
    model = rotation * model;

透视投影矩阵:同样带入公式即可

    Eigen::Matrix4f proj, ortho;
    proj << 
        zNear, 0, 0, 0,
        0, zNear, 0, 0,
        0, 0, zNear + zFar, -zNear * zFar,
        0, 0, 1, 0;

    double w, h, z;
    h = zNear * tan(eye_fov / 2) * 2;
    w = h * aspect_ratio;
    z = zFar - zNear;
    
    ortho << 
        2 / w, 0, 0, 0,
        0, 2 / h, 0, 0,
        0, 0, 2 / z, -(zFar+zNear) / 2,
        0, 0, 0, 1;
             				
    projection = ortho * proj * projection;
标题

提高部分题目:

在 main.cpp 中构造一个函数,该函数的作用是得到绕任意 过原点的轴的旋转变换矩阵。Eigen::Matrix4f get_rotation(Vector3f axis, float angle)

解:

很明显从函数参数可以看出应用的是罗德里格斯公式,所以这里其实就是带入公式

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=Eigen::Matrix4f::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;
    return Rod;
}

理解代码

对于c语言使用.h,.c的扩展名,而对于c++使用.h,hpp(为了区分 C 和 C++ 代码),.cpp扩展名

main.cpp:变换矩阵的计算,模拟了图形管线,旋转三角形的控制

int argc,命令行参数的个数 const char** argv |const char* argv[]是一个数组,每个元素都是指向常量字符的指针

为什么argv[2]没有解引用?因为std::cout 会自动将 const char* 解释为一个以 \0 结尾的字符串。

int main(int argc, const char** argv)//在运行是输入命令行参数
{
    float angle = 0;//摄像机角度
    bool command_line = false;//设置命令行开关
    std::string filename = "output.png";//字符串

    if (argc >= 3) {//当3个参数
        command_line = true;
        angle = std::stof(argv[2]); // -r by default//从命令行获取旋转角度
        if (argc == 4) {//当4个参数
            filename = std::string(argv[3]);//从命令行获取文件名
        }
        else
            return 0;
    }

    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}};//3个顶点

    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) {//如果传入参数>=3
        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) {//非ECS
        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("image", image);//显示图像
        key = cv::waitKey(10);

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

        if (key == 'a') {//逆时针旋转10
            angle += 10;
        }
        else if (key == 'd') {//顺时针旋转10
            angle -= 10;
        }
    }

    return 0;
}

rasterizer.hpp , rasterizer.cpp光栅器 | 渲染器,生成渲染器界面与绘制。 

rst::pos_buf_id rst::rasterizer::load_positions(const std::vector<Eigen::Vector3f> &positions) // 保存到
{
    auto id = get_next_id();        // 从0开始分配,每次调用后索引增加
    pos_buf.emplace(id, positions); // 存放到顶点数组中

    return {id};
}

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

// Bresenham's line drawing algorithm,
// Bresenham算法,绘制线
// Code taken from a stack overflow answer: https://stackoverflow.com/a/16405254
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};
    /*x, y当前绘制的像素点的坐标,
    dx, dy直线在 x 和 y 方向上的增量,
    dx1, dy1用于计算偏移量,
    px, py用于确定下一个像素点的坐标 ,
    xe, ye: 用于循环控制,表示直线的终点*/
    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)/* 如果在y轴上的偏移量比x轴上要小,更趋向于水平线 */
    {
        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)/* 像素的 y 坐标不需要改变 */
            {
                px = px + 2 * dy1;/* 更新 px 的值 */
            }
            else
            {
                if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0))/* 表示 y 方向向上移动 */
                {
                    y = y + 1;
                }
                else/* 表示 y 方向向下移动。 */
                {
                    y = y - 1;
                }
                px = px + 2 * (dy1 - dx1);/* 更新 px 的值 */
            }
            //delay(0);
            Eigen::Vector3f point = Eigen::Vector3f(x, y, 1.0f);/* 新的点绘制到屏幕上 */
            set_pixel(point, line_color);
        }
    }
    else
    {
        ……
    }
}

auto to_vec4(const Eigen::Vector3f &v3, float w = 1.0f) // 默认创建点,引入齐次坐标
{
    return Vector4f(v3.x(), v3.y(), v3.z(), w);
}

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!");
    }
    //2维数组
    auto &buf = pos_buf[pos_buffer.pos_id];/* 根据id访问实际数据,3个顶点的坐标 */
    auto &ind = ind_buf[ind_buffer.ind_id];/* 三个顶点的索引 */

    float f1 = (100 - 0.1) / 2.0;
    float f2 = (100 + 0.1) / 2.0;

    Eigen::Matrix4f mvp = projection * view * model; // MVP
    for (auto &i : ind)//第一次为{0,1,2}
    {
        Triangle t;/* 三角形对象 */
        /* MVP变换 ,对3个顶点 */
        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();
        }
        /* 对每个顶点,分解为xyz,变换到视口,应用视口矩阵 */
        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);

        rasterize_wireframe(t); // 绘制
    }
}

void rst::rasterizer::rasterize_wireframe(const Triangle &t) // 绘制三角形
{
    draw_line(t.c(), t.a());/* abc为3个顶点 */
    draw_line(t.c(), t.b());
    draw_line(t.b(), t.a());
}

void rst::rasterizer::set_model(const Eigen::Matrix4f &m) // 设置成员变量
{
    model = m;
}

void rst::rasterizer::set_view(const Eigen::Matrix4f &v)
{
    view = v;
}

void rst::rasterizer::set_projection(const Eigen::Matrix4f &p)
{
    projection = p;
}

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

rst::rasterizer::rasterizer(int w, int h) : width(w), height(h) // 根据宽高比设置帧缓冲大小和深度缓冲大小
{
    frame_buf.resize(w * h);
    depth_buf.resize(w * h);
}

int rst::rasterizer::get_index(int x, int y) // 根据坐标求像素在缓冲区的序号
{
    return (height - y) * width + x;
}

void rst::rasterizer::set_pixel(const Eigen::Vector3f &point, const Eigen::Vector3f &color) // 将屏幕像素点 (x, y) 设 为 (r, g, b) 的颜色,并写入相应的帧缓冲区位置。
{
    // old index: auto ind = point.y() + point.x() * width;
    if (point.x() < 0 || point.x() >= width ||
        point.y() < 0 || point.y() >= height)
        return;
    auto ind = (height - point.y()) * width + point.x();
    frame_buf[ind] = color;
}

Triangle.hpp, Triangle.cpp三角形数据 ,提供了数据设置的接口

class Triangle
{
  public:
    /*the original coordinates of the triangle, v0, v1, v2 in counter clockwise order*/
    /*Per vertex values*/
    /* 顶点,颜色,纹理坐标,法线 */
    Vector3f v[3]; 
    Vector3f color[3];      // color at each vertex;
    Vector2f tex_coords[3]; // texture u,v
    Vector3f normal[3];     // normal vector for each vertex

    // Texture *tex;
    Triangle();
    /* 返回三角形顶点坐标 */
    Eigen::Vector3f a() const { return v[0]; }
    Eigen::Vector3f b() const { return v[1]; }
    Eigen::Vector3f c() const { return v[2]; }

    void setVertex(int ind, Vector3f ver); /*set i-th vertex coordinates */
    void setNormal(int ind, Vector3f n);   /*set i-th vertex normal vector*/
    void setColor(int ind, float r, float g, float b); /*set i-th vertex color*/
    void setTexCoord(int ind, float s,
                     float t); /*set i-th vertex texture coordinate*/
    std::array<Vector4f, 3> toVector4() const;
};

#endif // RASTERIZER_TRIANGLE_H

//cpp

Triangle::Triangle()/* 初始值都为0 */
{
    v[0] << 0, 0, 0;
    v[1] << 0, 0, 0;
    v[2] << 0, 0, 0;

    color[0] << 0.0, 0.0, 0.0;
    color[1] << 0.0, 0.0, 0.0;
    color[2] << 0.0, 0.0, 0.0;

    tex_coords[0] << 0.0, 0.0;
    tex_coords[1] << 0.0, 0.0;
    tex_coords[2] << 0.0, 0.0;
}

void Triangle::setVertex(int ind, Eigen::Vector3f ver) { v[ind] = ver; }/* 设置指定索引的值 */

void Triangle::setNormal(int ind, Vector3f n) { normal[ind] = n; }

void Triangle::setColor(int ind, float r, float g, float b)/* 将颜色从0--255,转到0--1 */
{
    if ((r < 0.0) || (r > 255.) || (g < 0.0) || (g > 255.) || (b < 0.0) ||
        (b > 255.)) {
        throw std::runtime_error("Invalid color values");/* 超出范围抛出异常 */
    }

    color[ind] = Vector3f((float)r / 255., (float)g / 255., (float)b / 255.);
    return;
}
void Triangle::setTexCoord(int ind, float s, float t)
{
    tex_coords[ind] = Vector2f(s, t);
}

std::array<Vector4f, 3> Triangle::toVector4() const/* 三个顶点从 Vector3f 类型转换为 Vector4f 类型 */
{
    std::array<Vector4f, 3> res;
    std::transform(std::begin(v), std::end(v), res.begin(), [](auto& vec) {/* 遍历每个顶点 */
        return Vector4f(vec.x(), vec.y(), vec.z(), 1.f);
    });
    return res;
}

阅读复杂指针

如何阅读C/C++中的复杂的指针类型声明(源码中常遇到)_c++复杂阅读指针-CSDN博客

右左法则:从标识符开始,从右到左,优先级越高

括号法则:运算优先级更高

规律:普通变量(左边是本身类型),指针|引用(左边表示指向的类型),数组(左边表示元素类型),函数(左边表示返回类型)

int *  a  [10];a是一个包含10个数据的数组,每个元素是一个指向int的指针

int (*a) [3]; a是一个指针,指向3个元素的数组,每个元素是int类型

int *foo();foo是一个函数,返回指向int指针

int (*foo)();foo是一个指针,指向函数,函数返回int

int (*(*vtable)[])();vtable是一个指针,指向数组,每个元素是指针,并指向函数,函数返回类型为int

int name[] [n],name是一个数组,每个元素也是数组,包含n个int类型元素

const char** argv,argv是一个指针,指向常量字符的指针

const char* argv[]argv是一个数组,每个元素都是指向常量字符的指针

总结

整体课程相比于OpenGL还是偏底层的,手写MVP矩阵,手写Bresenham算法绘制线,和三角形,计算齐次坐标等,并没有opengl封装好的接口

  • 21
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值