[学习笔记24.9.4]GAMES101作业1

小白完成GAMES101作业1花费一周多,主要原因在于完成此作业需配置opencv库,较为繁琐。而后发现windows环境不能顺利完成任务,故用easyx.h图形库做了一个轻量的光栅化器替代原本的光栅化器模板。该光栅化器不涉及深度缓冲,但本次作业并不需要所以足够支撑我完成作业。以下是作业复盘。(作业1使用右手系,旋转默认逆时针;复盘内容仅涉及代码问题,不含括数学)

- 必要的前置函数

目前包括一个角度转弧度的函数,因为<cmath>库提供的三角函数只支持弧度制。此前并未注意该问题,遂导致出错。

const float M_PI = 3.1415926;
float ToRadian(float angle) {
    return M_PI * (angle / 180.0f);
}

- Model变换矩阵

传入一个浮点型参数angle,该矩阵实现绕Z-axis旋转angle度。

Eigen::Matrix4f get_model_matrix(float angle) {
    angle = ToRadian(angle);
    Eigen::Matrix4f ret = Eigen::Matrix4f::Identity();
    ret(0, 0) = cos(angle);
    ret(0, 1) = -sin(angle);
    ret(1, 0) = sin(angle);
    ret(1, 1) = cos(angle);
    return ret;
}

- View变换矩阵

main.cpp中,相机向量的定义如下:

Eigen::Vector3f eye_fov(0.0f, 0.0f, 5.0f);//相机在(0,0,5),view矩阵用于相对成像

 这代表相机位于(0,0,5),所以View变换的目的是将相机移动至原点,方便后续计算。即View变换矩阵本质上是一个平移变换矩阵。实现代码如下:

Eigen::Matrix4f get_view_matrix(Eigen::Vector3f vec) {
    Eigen::Matrix4f ret;
    ret << 1.f, 0.f, 0.f, -vec.x(),
        0.f, 1.f, 0.f, -vec.y(),
        0.f, 0.f, 1.f, -vec.z(),
        0.f, 0.f, 0.f, 1.f;
    return ret;
}

 - Projection变换矩阵

投影变换分为透视投影到正交投影的变换与正交投影变换两步。透视投影到正交投影的变换矩阵实现如下:

persp << zNear, 0.f, 0.f, 0.f,
        0.f, zNear, 0.f, 0.f,
        0.f, 0.f, zNear + zFar, -zNear * zFar,
        0.f, 0.f, 1.f, 0.f;

 正交变换矩阵略为复杂,需要根据 eye_fov, aspect_ratio, zNear, zFar 得到变换长方体的宽、高以及纵深。代码实现如下:

float w, h, z;
    w = abs(zNear) * tan(ToRadian(fovY / 2)) * 2;
    h = w / aspect_ratio;
    z = zFar - zNear;
    ortho << 2 / w, 0.f, 0.f, 0.f,
        0.f, 2 / h, 0.f, 0.f,
        0.f, 0.f, 2 / z, -(zNear + zFar) / 2,
        0.f, 0.f, 0.f, 1.f;

在求完两个关键矩阵后,Projection变换矩阵就等于两个矩阵乘起来。注意矩阵乘法的顺序,写代码的时候写反了调试了几个小时才改过来。

Eigen::Matrix4f get_projection_matrix(float fovY, float aspect_ratio, float zNear, float zFar) {
    
    Eigen::Matrix4f ret = Eigen::Matrix4f::Identity(), ortho, persp;
    persp << zNear, 0.f, 0.f, 0.f,
        0.f, zNear, 0.f, 0.f,
        0.f, 0.f, zNear + zFar, -zNear * zFar,
        0.f, 0.f, 1.f, 0.f;
    float w, h, z;
    w = abs(zNear) * tan(ToRadian(fovY / 2)) * 2;
    h = w / aspect_ratio;
    z = zFar - zNear;
    ortho << 2 / w, 0.f, 0.f, 0.f,
        0.f, 2 / h, 0.f, 0.f,
        0.f, 0.f, 2 / z, -(zNear + zFar) / 2,
        0.f, 0.f, 0.f, 1.f;
    ret = ortho * persp * ret;
    return ret;
}

- 提高:绕任意轴旋转 

数学工具需要用到Rodrigues's Rotation Formula,这里不进行数学证明,仅放出公式:

\mathbf{R}(\mathbf{n},\alpha )=\mathrm{cos}(\alpha)\mathbf{I} + (1-\mathrm{cos}(\alpha))\mathbf{n}\mathbf{n}^T+\mathrm{sin}(\alpha)\begin{pmatrix} 0& -n_z &n_y \\ n_z& 0 &-n_x \\ -n_y& n_x &0 \end{pmatrix}

照着公式完成函数,代码如下:

Eigen::Matrix4f get_rodrigues_matrix(Eigen::Vector3f aixs, float angle) {
    Eigen::Matrix4f I = Eigen::Matrix4f::Identity(), ret, N;
    N << 0.f, -aixs.z(), aixs.y(), 0.f,
        aixs.z(), 0.f, -aixs.x(), 0.f,
        -aixs.y(), aixs.x(), 0.f, 0.f,
        0.f, 0.f, 0.f, 1.f;
    Eigen::Vector4f aff_;
    aff_ << aixs(0), aixs(1), aixs(2), 0.f;//向量齐次化第四维为0
    angle = ToRadian(angle);//转弧度
    ret = cos(angle) * I + (1 - cos(angle)) * aff_ * aff_.transpose() + sin(angle) * N;
    ret(3, 3) = 1;//齐次化
    return ret;
}

然后在光栅器类中增加接口,并把该矩阵乘在 Model 之前即可,因为该变换属于模型变换。而main函数中增加几行代码实现输入即可:

for (int i = 1; i<=10;i++) {
        r.darw(Triangle(v0, v1, v2));
        std::string input{ "" };
        std::getline(std::cin, input, ':');
        Eigen::Vector3f aixs;
        if (input == "Rod:") {
            for(int j=0;j<=2;j++)
                std::cin >> aixs(j);
            std::cin >> angle;
            r.set_rodrigues(get_rodrigues_matrix(aixs, angle));
        }
        else {
            std::cin >> angle;
            r.set_model(get_model_matrix(angle));
        }
        
    }

- 运行效果 

 

(注:这是伪光栅化器运行效果,与GAMES101作业1中提供的光栅化器略有差别。)

在这部分的课程中,我们将专注于使用光线追踪来渲染图像。在光线追踪中 最重要的操作之一就是找到光线与物体的交点。一旦找到光线与物体的交点,就 可以执行着色并返回像素颜色。在这次作业中,我们需要实现两个部分:光线的 生成和光线与三角的相交。本次代码框架的工作流程为: 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、付费专栏及课程。

余额充值