现代计算机图形学 作业5 解析(1)
昨天在做这个作业5的时候开始给我整的有点蒙,而且做完以后发现渲染出来的结果还是不错的,所以想着深挖一下代码,
这篇文章就是对这个作业代码整体的一个解析,也希望能为在做这个作业时感到困惑的同学提供一些帮助
首先在main.cpp里创建场景,然后定义了两个Sphere,两个Light,和一个MeshTriangle
注意这里的make_unique
就是构建智能指针的意思,引用计数结束后就会把智能指针管理的对象删除掉
两个Sphere的类型分别是DIFFUSE_AND_GLOSSY
和REFLECTION_AND_REFRACTION
,具体是什么意思我们之后可以看到
然后就是用move
把刚才构建的对象传给scene,让scene统一管理这些对象
没有了解过或者忘记的同学可以简单复习一下,move
可以把传入的参数的类型编程将亡值,这样在构建参数的时候就会使用移动构造函数而不是拷贝构造函数来进行构造
那么这里的话,我们就会使用unique_ptr
的移动构造函数把我们刚才的light和object传进来,并再一次调用move,将对象移动到objects和lights中
那么这么想的话,是不是使用emplace_back
也可以呢?
然后我们回来,就正式的进入到了Render中
首先我们初始化帧缓冲区,也就是最后储存每一个像素颜色的地方,即我们的屏幕
接着定义的scale
和imageAspectRatio
我们等下说。然后是我们的摄像机的位置
接着我们遍历每一个像素,注意这里是先y再x的遍历,因为这样的话像素是连续的,所以就有下面的那行代码framebuffer[m++] = castRay(eye_pos, dir, scene, 0)
。
然后就是第一块我们要实现的部分了,我们要对每一个像素都射出一条光线,并把从光线中得到的颜色存储到帧缓冲区中
下面的UpdateProgress
可以不看,因为这是更新进度条的代码,比较简单
那么下面的问题就是如何生成光线了,首先光线的起点很简单,就是我们摄像机的位置,所以就是原点。那么对于方向,其实就是从原点到当前像素对应在世界坐标中的位置。
我们回忆,最开始的时候做的mvp变换,这里我们主要关注投影变换。我们首先使用投影变换把视锥挤压成了一个长方体。然后使用正交变换把长方体转化成立方体,并平移到了原点。
然后我们获得了标准正交的立方体,然后我们进行视口变换,把[-1, 1]映射到我们的屏幕中。
那么刚才的过程就是把世界坐标,也就是我们原始的空间中的点映射到了我们的屏幕中。现在我们要发射光线去照射场景中的这些物体,我们自然需要场景中的坐标来创建光线
所以对于每一个屏幕上的像素,我们需要计算对应在场景中的坐标,并创建光线射出来
更准确一点来说,我们需要的这个场景中的点,是我们的视平面上的点,或者叫近平面,就是在做透视投影的时候的那个近平面。因为在实际上,我们是在原点发出这个视锥,并在近平面上进行成像。只不过我们需要把看到的东西放到屏幕上,所以才有了视口变换这个过程。更简单的你可以把这个近平面想象成我们的屏幕,我们就是通过屏幕去看这些点的。这样想的话就会发现,视口变换就是在从屏幕到实际成像的平面的一个映射。
这里有一个很隐晦的地方,就是注意看下面,我们创建dir的时候使用的是x, y, -1
,其实也就是在跟我们说,这个近平面的深度是1,注意我们是向-z方向看的
那么了解到这个之后,我们首先要做视口变换的逆变换,把屏幕坐标映射到[-1, 1]的空间中
然后我们在做正交变换的逆变换,即把这个[-1, 1]的空间映射到我们的成像平面中。对于高度而言,我们只需要用zNear * tan(a / 2),其中a是我们视野的角度。注意这里算出来实际上是乘了height / 2的大小,因为我们是[-1, 1],所以整体的高度就是height,那么对于宽度来说,只要乘上宽高比即可。
最后我们就可以获得世界坐标中对应成像平面的点,那么光线的方向自然就是这个点的坐标,因为我们是从原点打出的
未完待续 :-)
文章评论