games101-1 光栅化与光线追踪中的空间变换
在学习了一些games101的课程之后,我还是有点困惑,对于计算机图形学的基础知识,总感觉还是缺乏一些更加全面的认识,幸而最*在做games101的第五次作业时,查询资料找到了scratchpixel这个网站,看了一些文章,终于把脑子里的一团乱麻组织起来了,也就有了这篇关于图形学的第一篇博客。
想要更好的理解这篇博客,强烈推荐先学习games101中关于transformation,rasterization和ray tracing的第一部分
以下内容参考:https://www.scratchapixel.com/lessons/3d-basic-rendering/computing-pixel-coordinates-of-3d-point/perspective-projection.html
https://www.scratchapixel.com/lessons/3d-basic-rendering/computing-pixel-coordinates-of-3d-point/mathematics-computing-2d-coordinates-of-3d-points.html
https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-generating-camera-rays/generating-camera-rays.html
如果内容有误,欢迎指出
光栅化与光线追踪的理解
在图形学中一个很重要的问题就是,我们如何把一个三维空间中的物体,去展示到一个二维*面上。
这件事其实我们可以分成两步去做,第一步是解决visibility的问题,第二步是解决shading的问题。
怎么理解这两种方法呢?我们先想象,现在在一个无穷大的场景里面,有很多物体,还有一个摄像机,摄像机面前一定距离有个*面,现在我们想知道通过摄像机在它面前这个二维*面上看到的所有三维物体的形象。
光栅化方法,就是把场景内的所有物体先都投影到这个*面上,然后进行着色shading,这里就是光栅化比较麻烦的地方,我们需要设计着色模型(games101中的bling-phone模型),考虑到物体的空间先后顺序我们需要z-buffer,考虑到阴影效果我们需要shadow map,以及纹理贴图来帮助我们。
而光线追踪,模拟了光线在场景中的传播,在光线追踪中,从观察点(或相机)出发的光线被跟踪以确定它们与场景中的物体相交点,然后计算反射、透射和光照等效果,是一种基于物理的渲染方法。
通过上述描述,我们可以发现光栅化可以短时间内处理大量物体,但在光照效果等方面不如光线追踪,而光线追踪速度较慢,因为需要考虑到光线的种种复杂的传播情况,这也就导致光栅化主要用于实时渲染,而光线追踪主要用于离线渲染
我们再来考虑针对这两种方法的空间变换,我们会发现这两种方法其实在干相反的事情,光栅化的坐标变换是把三维变二维,光线追踪是需要摄像机到*面上每个像素点连接的光线,然后求光线与物体的交点,实际上需要我们把二维的点转换为三维,具体的过程我们在接下来的部分阐述
不同空间的介绍
要理解光栅化与光线追踪的空间变换,我们首先要搞清楚几个空间的定义
世界空间:世界空间就是我们之前提到的无穷大的场景,所有的点的定义最初都是在这个三维空间中,与之对应的是世界坐标系
相机空间:相机空间就是以相机为坐标原点的坐标系建立的空间,我们可以使用games101中提到的camera transformation对应的矩阵,实现世界坐标系到相机坐标系之间的转换,一般来说,我们的相机的位置在(0,0,0)这个坐标原点,朝向世界坐标系的-z方向。
屏幕空间:屏幕空间是一个二维空间,相机空间中的点经过透视变换可以到image plane上,然后我们根据画布(canvas)的范围,可以在image plane这个无穷大的*面上划分出屏幕空间

NDC空间:把屏幕空间坐标系标准化到【0-1】的空间中
栅格空间:NDC空间中的2D点被转换为2D像素坐标,为此,我们将标准化点的 x 和 y 坐标乘以像素宽度和高度,从 NDC 到栅格空间还需要反转该点的 y 坐标。 由于像素坐标是整数,因此最终坐标需要四舍五入到最接*的以下整数。
下面的图很形象地展示了上面的三个空间的区别:

其实我们电脑的屏幕就相当于栅格空间,不同的分辨率代表着不同的水*像素数与竖直像素数,也就代表着不同的像素宽度与高度,代表着不同的栅格空间计算方法
注意我们这里的讨论实际上简化了屏幕坐标系与NDC坐标系,games101中的投影变换实际上就是直接从相机空间变换到了NDC空间或者说变换到了裁剪空间然后经过齐次除法到NDC空间,空间是三维的,因为我们不仅要完成投影,还需要记录点的z坐标信息来进行深度缓存等等,确定物体的先后关系:

可以看到二维空间与三维空间的区别就是二维空间舍弃了z坐标,它们在xy坐标方面做的都是投影,三维空间相当于多做了z坐标的投影
注意games101其实并没有过多的讨论裁剪空间,而是推导了一个矩阵直接转换到NDC空间,这也是未来博客中要探讨的内容,同时z-fighting的现象也未提及(*远*面距离过大导致的问题)
在我们这里的讨论屏幕坐标系与NDC坐标系是二维的,这样可以把光栅化与光线追踪的visibility处理统一起来,因为光线追踪中我们并不需要使用投影矩阵,就不存在光栅化中的视锥体与立方体,只需要二维的屏幕空间。
光栅化做的就是从世界空间到栅格空间,实现每个物体的visibility,然后着色
而光线追踪做的是实现每个光线的可视化,然后着色,需要我们从栅格空间转换到世界空间。
光栅化中的空间变换
世界空间到观察空间或者说是摄像机空间:
这一步很简单,和games100课程中的一样,实际上就是进行坐标系变换,进行旋转与*移,值得注意的是默认,相机正对的是z的负半轴,所以我们可见的物体的z坐标也是负的
那么在进行从观察空间到屏幕空间的过程中,我们要进行投影,将3维的点投影到2维的屏幕上:

这样我们根据相似关系,可以得到屏幕空间与观察空间的xy坐标映射,注意这里我们假设和**面的距离是1,可以得到:
\(\begin{array}{l} P'.x = \dfrac{P_{camera}.x}{P_{camera}.z}\\ P'.y = \dfrac{P_{camera}.y}{P_{camera}.z}. \end{array}\)
但是注意我们之前提到过物体的z坐标是负的,所以这样进行除法会导致xy发生颠倒,因此,我们要再加上负号:
\(\begin{array}{l} P'.x = \dfrac{P_{camera}.x}{-P_{camera}.z}\\ P'.y = \dfrac{P_{camera}.y}{-P_{camera}.z}. \end{array}\)
之前提到过屏幕空间是有范围的,由宽度与高度决定,所以我们要加上这个限制
\(\text {visible} = \begin{cases} yes & |P'.x| \le {W \over 2} \text{ or } |P'.y| \le {H \over 2}\\ no & \text{otherwise} \end{cases}\)
从屏幕空间到NDC空间,需要我们把原来在[-width/2]--[width/2]和[-height/2]到[height/2]之间的点转换到[0-1]的点(有的NDC空间采用的是[-1-1]):
\(\begin{array}{l} P'_{normalized}.x = \dfrac{P'.x + width / 2}{ width }\\ P'_{normalised}.y = \dfrac{P'.y + height / 2}{ height } \end{array}\)
从NDC空间到栅格空间,我们需要乘以像素宽度与高度,因为我们认为像素点是一个个小矩形来进行栅格化,并且取整,这一步也被称为viewport变换
\(\begin{array}{l} P'_{raster}.x = \lfloor{ P'_{normalized}.x * \text{ Pixel Width} }\rfloor\\ P'_{raster}.y = \lfloor{ P'_{normalized}.y * \text{Pixel Height} }\rfloor \end{array}\)
同时我们注意到栅格空间的y是向下的,所以改变方向取反:
\(\begin{array}{l} P'_{raster}.x = \lfloor{ P'_{normalized}.x * \text{ Pixel Width} }\rfloor\\ P'_{raster}.y = \lfloor{ (1 - P'_{normalized}.y) * \text{Pixel Height} }\rfloor \end{array}\)
以上我们就将三维空间中的点可视化到了二维空间中
光线追踪中的空间变换
针对光线追踪,我们需要的是可视化我们的光线,因为我们需要实际计算光线与物体相交,所以我们的光线需要世界坐标下的三维表示,简单来说一个光线可以如下定义:

我们已经知道了O的位置,它就是坐标原点(0,0,0)
现在需要知道P的位置
现在考虑下面这张图:

第一步转换到NDC空间:
\(\begin{array}{l}
PixelNDC_x = \dfrac{(Pixel_x + 0.5)}{ImageWidth},\\
PixelNDC_y = \dfrac{(Pixel_y + 0.5)}{ImageHeight}.
\end{array}\)
注意这里我们只考虑光线穿过像素点的中心,所以我们要加上0.5,然后再分别除以栅格空间的宽度与高度
第二步转换到屏幕空间:
注意屏幕空间的y轴发生了反转,同时由于我们是将原本的图像压缩到了-1-1的这个空间中,所以我们现在要将其还原回去,即宽度从[-1--1]转换成[-width/2---width/2],高度同理,我们使用宽高比以及fov角度来表示就可以得到:
\(\begin{array}{l} PixelCamera_x = (2 * {PixelNDC_x } - 1) * ImageAspectRatio * tan(\dfrac{\alpha}{2}),\\ PixelCamera_y = (1 - 2 * {PixelNDC_y }) * tan(\dfrac{\alpha}{2}). \end{array}\)
其中tan(a/2)表示图像高度的一半,因为我们在这里假设**面坐标是-1
最后转换到世界空间中,只需要加入z坐标-1,然后从相机空间转换到世界空间即可
上述过程也就是games101第五次作业求光线的实现
games101-1 光栅化与光线追踪中的空间变换的更多相关文章
- OpenGL中的空间变换
OpenGL中的空间变换 在使用OpenGL的三维虚拟程序中.当我们指定了模型的顶点之后.在屏幕上显示它们之前,一共会发生3种类型的变换:视图变换.模型变换.投影变换. ...
- 3D游戏中各种空间变换到底是怎么回事
每一个游戏可以呈现炫丽效果的背后,需要进行一系列的复杂计算,同时也伴随着各种各样的顶点空间变换.渲染游戏的过程可以理解成是把一个个顶点经过层层处理最终转化到屏幕上的过程,本文就旨在说明,顶点是经过了哪 ...
- OpenGL中着色器,渲染管线,光栅化
https://www.zhihu.com/question/29163054 光栅(shan一声)化(Rasterize/rasteriztion).这个词儿Adobe官方翻译成栅格化或者像素化 ...
- 回顾Games101图形学(一)几何变换中一些公式的推导
回顾Games101 chatper1 - 6 前言 本文只写回顾后重新加深认识的知识 透视除法的意义 经过MVP矩阵之后,将模型空间下某点的坐标,转换成了裁剪空间下的坐标,此时因为裁剪空间的范围是x ...
- opengles2.0之图元装配和光栅化
光栅化的过程就是把三维世界中的物体转换成屏幕上像素的过程. glGetfloatv(); --------v表示的是数组 gles2.0里面有两种绘图命令.glDrawArrays和glDraw ...
- DirectX11 With Windows SDK--11 混合状态与光栅化状态
前言 虽然这一部分的内容主要偏向于混合(Blending),但这里还需提及一下,关于渲染管线可以绑定的状态主要有如下四种: 光栅化状态(光栅化阶段) 采样器状态(像素着色阶段) 混合状态(输出合并阶段 ...
- OpenGL ES 3.0 图元组合和光栅化(三)
图元是能够被OpenGL ES 绘制的几何物体,如三角形.线条或者精灵.在图元组合过程 中,对每个图元必须判断是否位于投影 截体内,如果图元不完全在平截体内部,将被视图平截体剪贴,如果完全在平截体外, ...
- GPU大百科全书 第二章 凝固生命的光栅化
光栅化——死神来了…… 前言:在上一期的GPU大百科全书里,我们目睹了可爱的香草从抽象世界走向现实,从方程还原成实体的全过程.可以说香草活了,因为几何单元,我们赋予了她完整的灵魂. 如果你正在为G ...
- 光栅化规则(Rasterization Rules)
光栅化规则不是唯一的,只要能满足在扫描线填充过程中,对于一条分割线两边的像素能够被不重复不遗漏地填充即可. 在gdi3d中目前使用的是下面光栅化规则: xLeft_int=ceil(xLeft-0.5 ...
- DirectX11 With Windows SDK--07 添加光照与常用几何模型、光栅化状态
原文:DirectX11 With Windows SDK--07 添加光照与常用几何模型.光栅化状态 前言 对于3D游戏来说,合理的光照可以让游戏显得更加真实.接下来会介绍光照的各种分量,以及常见的 ...
随机推荐
- 在HTML中引入React和JSX
前言 Vue 可以非常方便地与 Pure HTML 结合,代替 jQuery 的功能,有一次遇到类似的场景时,我就想 React 能不能也以这种方式接入 HTML 网页,从而提高开发效率. 结果当然是 ...
- 使用Stable Diffusion制作AI数字人视频的简明教程
基本方法 搞一张照片,搞一段语音,合成照片和语音,同时让照片中的人物动起来,特别是头.眼睛和嘴. 语音合成 语音合成的方法很多,也比较成熟了,大家可以选择自己方便的,直接录音也可以,只要能生成一个语音 ...
- 学习LXC(Linux 容器)技术
安装LXC.LXD.zfs 测试机器为ubuntu sudo apt-get install lxc lxd zfsutils-linux -y 创建LXD的zfs存储池 sudo lxd init ...
- expect: telnet2switch
#!/usr/bin/expect if {$argc != 1} { puts "usage: ./telnet2sswitch <r2|r3>" exit } if ...
- VScode 中golang 单元测试,解决单元测试超时timeout30s
目的:单元测试的主要目的是验证代码的每个单元(函数.方法)是否按照预期工作. 提示:解决单元测试超时30s的问题在序号4 1 准备以_test.go结尾文件和导入testing包 在命名文件时需要让文 ...
- npm与package.json的联系
在nodejs编写的脚手架项目中,npm是不可缺少的包管理工具,当使用npm初始化时,会生成package.json文件来对项目进行整体的管理和描述 以下是新建的练习项目中package.json ...
- Node.js安装中出现的问题及其解决方案
Node.js安装与配置流程,请参考 1.npm -v测试时出现警告 更好的选择是安装一个更完善的版本 问题出现的原因 node更新后是最新版 但是npm的版本没有相应的更新存在版本滞后导致问题出现 ...
- 产品代码都给你看了,可别再说不会DDD(二):DDD概念大白话
这是一个讲解DDD落地的文章系列,作者是<实现领域驱动设计>的译者滕云.本文章系列以一个真实的并已成功上线的软件项目--码如云(https://www.mryqr.com)为例,系统性地讲 ...
- 避坑|在读取excel.xlsx文件中的内容时发现明明只有3行,但跑起来却认为有13行,导致有10行None,UI自动化测试代码空跑了10次;|UI自动化测试|数据驱动
在读取excel.xlsx文件中的内容时发现明明只有3行,但跑起来却认为有13行,导致有10行None,UI自动化测试代码空跑了10次: 原因:excel.xlsx内容清除时用delete快捷键导致, ...
- 拓展kmp的应用
Smiling & Weeping ---- 我与月亮,进行了一次深夜谈话 它与我谈论太阳,而我与它谈论你. 题目链接:P3435 [POI2006] OKR-Periods of Words ...