GAMES101 作业记录

作业1

  • 模型变换矩阵实现绕轴旋转
    绕某一轴旋转的矩阵:
    R_x(\alpha)=\begin{pmatrix}1&0&0&0\\0&cos\alpha&-sin\alpha&0\\0&sin\alpha&cos\alpha&0\\0&0&0&1\end{pmatrix},R_y(\alpha)=\begin{pmatrix}cos\alpha&0&sin\alpha&0\\0&1&0&0\\-sin\alpha&0&cos\alpha&0\\0&0&0&1\end{pmatrix},R_z(\alpha)=\begin{pmatrix}cos\alpha&-sin\alpha&0&0\\sin\alpha&cos\alpha&0&0\\0&0&1&0\\0&0&0&1\end{pmatrix}
    绕任意轴旋转的矩阵:
    R(a,\theta)=\begin{pmatrix}a_x^2(1-C)+C&a_xa_y(1-C)-Sa_z&a_xa_z(1-C)+Sa_y&0\\a_xa_y(1-C)+Sa_z&a_y^2(1-C)+C&A_ya_z(1-C)-Sa_x&0\\a_xa_z(1-C)-Sa_y&a_ya_z(1-C)+Sa_x&a_z^2(1-C)+C&0\\0&0&0&1\end{pmatrix}
  • 透视投影变换矩阵
    透视变换:将由n,f,aspect\_ratio,FovY定义的视锥体(frustum)转换成标准坐标空间(Normalized device Coordinaate)中的规则观察体(Canonical View Volume)。
    M_{ortho}=\begin{pmatrix}\frac{2}{r-l}&0&0&\frac{r+l}{l-r}\\0&\frac{2}{t-b}&0&\frac{t+b}{b-t}\\0&0&\frac{2}{n-f}&\frac{n+f}{f-n}\\0&0&0&1\end{pmatrix},M_{persp}=\begin{pmatrix}n&0&0&0\\0&n&0&0\\0&0&n+f&-nf\\0&0&1&0\end{pmatrix}
    M_{proj}=M_{ortho}\cdot M_{persp}
    其中r,l,t,b分别为透视变换后的观察体的右面、左面、上面和下面。t=n*tan(\frac{fov}{2}),\ d=-t,\ r=aspect\_ratio*t,\ l=-r

作业2

  • 三角形内部测试
    给定三角形,求出三角形所在的轴对齐包围盒(Axis Aligned Bounding Box,AABB),遍历AABB内的像素P,测试像素P在不在三角形内。
    三角形内部测试方法:
  1. 计算PAxPB PBxPC PCxPA同号
  2. 计算重心坐标u v,u和v分别在[0,1]内
  3. 点P点A在直线BC同侧 && 点P点B在直线AC同侧 && 点P点C在直线AB同侧
  4. RTR4中介绍的:硬件对于三角形的每一个边都计算该边的边缘函数e(P_x,P_y)e(P_x,P_y)>0代表P在边缘内侧,e(P_x,P_y)<0代表P在边缘外侧,e(P_x,P_y)=0代表P在边缘上。对三条边都计算边缘函数,如果都满足e(P_x,P_y)\geq 0,则表示P在三角形内。
    e(P_x,P_y):\mathbf{n} \cdot (P-T_0)
    上式表述了三角形T_0T_1T_2中,T_0T_1边的边缘函数。其中,P:(P_x,P_y)为测试点,T_0可以为边上任意一点(这里取了边上顶点),\mathbf{n}为边上的法向量(我们通过边上的顶点\mathbf{T_0}\mathbf{ T_1}来得到边的向量\mathbf{ T_1 - T_0},将\mathbf{ T_1 - T_0}逆时针旋转90°得到边法向量\mathbf{n}

注意:参与测试的像素要选取像素中心,也就是第i*j个像素坐标为(i+0.5,j+0.5)

  • 深度插值与Z-buffer
    对于通过了内部测试的像素,要通过重心坐标对其进行深度插值
    需要注意的是:作业框架在此给出了提示,框架这里是将深度插值和透视除法一起计算的,由于进行了透视除法,所以插值后做了透视校正。最终得到插值后的该像素的深度。
 // 计算重心坐标
auto [a, b, c] = computeBarycentric2D((float)i+0.5, (float)j+0.5, t.v);
// 深度插值和透视除法
float z_interpolated = a* v[0].z() / v[0].w() + b * v[1].z() / v[1].w() + c * v[2].z() / v[2].w();
// 透视校正
float w_reciprocal = 1.0/(a / v[0].w() + b / v[1].w() + c / v[2].w());
z_interpolated *= w_reciprocal;

然后比对该像素深度与Z-buffer中该像素的深度(深度测试),如果目前深度比Z-buffer中该像素的深度更小(也就是离相机更近),则更新Z-buffer中该像素深度。

  • 光栅化
    通过了深度测试的像素,插值出颜色,写入到framebuffer中,完成光栅化。

  • SSAA
    SSAA在每像素使用N个采样点来代替单个像素中心,都做一次着色,然后将像素内的多个采样点降采样成一个像素,实现超采样的效果。在这里的SSAA中,我简化了过程,把每个采样点当作前文中的像素中心,在每个像素点中,都遍历每个采样点进行内部测试、深度插值(不做深度测试和着色了,记录采样点的个数在像素内只着色一次,这种行为更像是MSAA)。记录每个像素中通过内部测试的采样点个数,在光栅化该像素时,将像素颜色与framebuffer中原本的颜色,根据采样点比例做线性插值,达到柔和边缘的效果。

if(minDepth<depth_buf[get_index(i,j)]){
  depth_buf[get_index(i,j)] = minDepth;
  // 颜色插值混合
  set_pixel(Vector3f(i, j, minDepth), t.getColor()*count/4 + frame_buf[(height-1-j)*width + i]*(4-count)/4);
}

采样点的位置选取也有讲究,不会在像素内均匀分布采样点,而是使用低差异采样序列,比如RGSS、Halton 序列、Poisson disk等。

作业3

  • 参数插值
    调用给定的插值函数对属性插值即可。

  • Blinn-Phong模型的实现
    Blinn-Phong着色模型考虑了物体的材质性质、光源的性质、入射光和出射光,所以需要在着色点对于所有的光源遍历。Blinn-Phong模型将物体着色考虑为固定数值环境光(L_a)+漫反射(L_d)+镜面反射(L_s),是比较简单的着色模型。公式为:
    \begin{align} L&=L_a+L_d+L_s\\&=k_aI_a + k_d (\frac{I}{r^2}) max(0, \mathbf{n\cdot l}) + k_s(\frac{I}{r^2}) max(0,\mathbf{ n \cdot h })^p \end{align}
    其中,k_a,k_d,k_s分别为表面的环境光系数、漫反射系数和镜面反射系数。I_a,I_d,I_s分别为光源的环境光、漫反射和镜面反射项。\mathbf{ n , l , h}分别为表面法线、入射光线和半程向量。p为高光项,控制高光亮度和范围。
    Phong与Blinn-Phong的区别:高光项上,phong是计算视线与反射光线方向夹角,blinn-phong是计算法线与半程向量夹角。phong夹角过大时取0,导致blinn-phong高光范围更大

  • 纹理映射
    与Blinn-Phong模型相似,不同之处在于,采样纹理得到的纹理颜色值需要作为表面的漫反射项k_d参与到Blinn-Phong模型的着色中。

  • 凹凸贴图与位移贴图
    凹凸贴图和法线贴图本质上是一样的,凹凸贴图定义了位置的法线差异,而法线贴图直接定义了法线,二者是可以互相计算出来的。这里有几个注意问题:

  1. 作业里做凹凸贴图使用的是法线图,注意作业里的法线贴图是将纹理的\sqrt{r^2+g^2+b^2}作为了切线空间的法线的差异,从而求得u方向的增量\dd uv方向的增量\dd v,变换后的切线空间的法线就是(-\dd u,-\dd v, 1),所以将切线空间的法线通过TBN矩阵转换回世界空间,就获得了世界空间的变换后的法线。
  2. 由于作业中法线贴图保存的是法线的移动距离,所以在计算位移时,\sqrt{r^2+g^2+b^2}就是位移的移动量,将顶点坐标沿法线方向移动\sqrt{r^2+g^2+b^2}后,就是位移后的顶点位置。
  3. 作业中计算TBN矩阵的方式只用到了着色点的法线,不需要其他信息,这是一种巧妙的方式,但不完善,正确的做法应该求三角形边缘向量映射到纹理空间中的转换。即:
    \begin{bmatrix}T_x&T_y&T_z\\B_x&B_y&B_z \end{bmatrix}=\begin{bmatrix}\Delta u_1&\Delta v_1\\ \Delta u_2&\Delta v_2 \end{bmatrix}^{-1}\begin{bmatrix}E_{1x}&E_{1y}&E_{1z}\\E_{2x}&E_{2y}&E_{2z} \end{bmatrix}
    (\Delta u_1, \Delta v_1)=(u_1,v_1)-(u_0,v_0)(\Delta u_2, \Delta v_2)=(u_2,v_2)-(u_0,v_0)E_1=V_1-V_0E_2=V_2-V_0
    其中,三角形上三个顶点为V_0,V_1,V_2,对应的纹理坐标为(u_0,v_0),(u_1,v_1),(u_2,v_2)
  • 双线性插值采样纹理
    找到采样点周围的4个像素,对这4个像素根据采样点的位置两两横向插值一次,再将得到的插值结果根据采样点的位置纵向插值,得到双线性插值后的结果。

作业5

Whitted风格的光线追踪:从每个着色像素发射光线,光线到达光滑物体发生反射和折射,并继续追踪反射和折射光线;当光线到达漫反射物体时,对每个光源都计算Phong着色,终止光线追踪。
由于场景中物体较少,所以没有使用加速结构,每次追踪光线都遍历所有场景内的物体,判断与光线的相交。

  • 光线生成
    光线追踪将摄像机看作一个针孔摄像机,而需要渲染的像素就是底片,但是我们不将底片放在摄像机镜头后(因为小孔成像会倒置),而是放在镜头前避免倒置。通常将底片放在镜头前方距离为1(世界空间)的位置。所以生成光线的过程,就是在世界空间中摆放底片,并通过摄像机和像素位置生成光线的过程。
    我们需要将屏幕光栅空间的像素位置,转换回世界空间(摄像机空间),并摆放在摄像机前方1单位的位置上,得到像素在世界空间的虚拟位置,连接摄像机和像素的光线,就是要生成和追踪的光线。
    具体操作:Ray-Tracing: Generating Camera Rays (Generating Camera Rays)

  • 光线和三角形求交
    Moller Trumbore算法:简单来说,Moller Trumbore算法通过光线起始\mathbf{\vec{O}}、光线方向\mathbf{ \vec{D}}和三角形三个顶点\mathbf{P_0, P_1, P_2 },计算光线传播时间t和重心坐标b_1\ b_2的方法。最终,如果tb_1b_21-b_1-b_2均不小于0,则判定为相交。

    MollerTrumbore.png

作业6

  • 光线与包围盒求交
    方法:分别求光线与6个面的交点t。然后可知光线与整个AABB的出入的t值——t_{enter}=max({t_{min}})t_{exit}=min({t_{max}})(光线和三对面都有交点了,光线才算是进入到AABB中,所以进入时间是进入三对面时最大的;只要再和另一个面相交,光线就离开AABB了,所以离开时间是光线和三对面中另一侧相交最小的)。如果t_{enter}<t_{exit}说明光线在AABB中逗留了一段时间,也就是和AABB有交点。反之则没有交点。
    所以,光线和AABB有交点的条件是:t_{enter}<t_{exit}\ \&\&\ t_{exit} \ge 0(光源在AABB内部也算)。
    注意:这里在源码的Bounds3::IntersectP函数中,有一个std::array<int, 3>& dirIsNeg参数,用来判断光线相交的方向是否与传播方向相反,如果相反则要将相应的对平面的进出时间对换。

  • 光线与BVH相交的查找
    由于物体只保存在BVH的叶节点上,所以我们需要一直查找到直到到达BVH树的叶节点上。
    遍历的过程就是二叉树的中序遍历,在叶节点出调用物体与光线求交的方法。

作业7

  • Path Tracing
    Path Tracing简单来说是用蒙特卡洛方法求解渲染方程。对于每条光路的着色结果,需要计算这条光路接触到的表面的直接光照和间接光照。直接光照需要直接采样面光源并考虑遮挡,间接光照需要随机采样一条出射光路,计算再次接触到的表面作为次级光源对着色表面的贡献。间接光照的采样添加俄罗斯轮盘赌的方法,限制光路弹射次数的同时,理论上做到不损失能量(所以不需要定义光线最深弹射次数)。每个像素发射多条光路(sample per pixel,spp),将多条光路的着色效果加权平均作为该像素的最终着色结果。
    由于实现假设了所有的表面均是diffuse的,所以每次弹射的pdf可以认为是在法线半球均匀分布的。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 227,250评论 6 530
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 97,923评论 3 413
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 175,041评论 0 373
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 62,475评论 1 308
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,253评论 6 405
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 54,801评论 1 321
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,882评论 3 440
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,023评论 0 285
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,530评论 1 331
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,494评论 3 354
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,639评论 1 366
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,177评论 5 355
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,890评论 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,289评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,552评论 1 281
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,242评论 3 389
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,626评论 2 370

推荐阅读更多精彩内容

  • 课程链接:GAMES101-现代计算机图形学入门-闫令琪课程讲师:闫令琪本系列笔记为本人根据学习该门课程的笔记,仅...
    AlbertLiDesign阅读 1,979评论 0 2
  • 课程链接:GAMES101-现代计算机图形学入门-闫令琪课程讲师:闫令琪本系列笔记为本人根据学习该门课程的笔记,仅...
    AlbertLiDesign阅读 1,708评论 0 3
  • 前言   游戏的优化能从各个角度入手,渲染、逻辑、内存、IO、GC、功耗等等,未经过优化的游戏往往在每个方面都会出...
    crossous阅读 7,279评论 2 13
  • 转载自VR设计云课堂[//www.greatytc.com/u/c7ffdc4b379e]Unity S...
    水月凡阅读 1,029评论 0 0
  • 1 简介 本系列的文章到目前为止已经介绍完了OpenGL的基础知识,你应该已经了解OpenGL中的大部分特性,也在...
    RichardJieChen阅读 3,554评论 0 15