第一章遗留的东西:
1.2.1 数学方法
下表描述了常见的一些数学方法,注意表中的规则也有一些例外,主要是在其它文献中经常使用的符号,比如L表示亮度 E表示辐射度 σs表示散射系数
R表示角度和标量,向量用加粗的括号表示
比如
列向量是我们经常使用的,我们经常写成
Type |
Notation |
Examples |
angle |
lowercase Greek |
αi,φ,ρ,η,γ242,θ |
scalar |
lowercase italic |
a,b,t,uk,v,wij |
vector or point |
lowercase bold |
a,u, vs h(ρ), hz |
matrix |
capital bold |
T(t), X, Rx(ρ) |
plane |
π: a vector and a scalar |
π : n · x + d = 0, π1 :n1 ·x+d1 =0 |
triangle |
△ 3 points |
△V0V1V2, △cba |
line segment |
two points |
uv, aibj |
geometric entity |
capital italic |
AOBB , T, BAABB |
齐次坐标 就是 v = (x ,y, z w)T,向量的w为0,点的w为1 ,因为向量无论怎么位移还是表示的同一个向量
平面用 π表示 : n · x + d = 0 ,n表示平面的法线, d是标量. 法线描述平面是朝向哪的.一个平面的法线同时也是它平面上所有点的法线, 平面π: 表示把空间划分为正反面, n · x + d > 0表述正面, n · x + d < 0表示反面
1.2.下面是操作符号的定义
v⊥ = (?y, x)T .表示和v点乘为0的向量,
|A| 表示矩阵 A的行列式. 有时我们使用 |A| = |a b c| = det(a, b, c), a b c都是矩阵A 的列矩阵
第8个表示:把x的最小值限制到0
第九个表示:把x的值限制到(0-1)
第十个:阶乘,0!=1
第十一个:二项式
Function |
Description |
|
1: |
atan2(y, x) |
two-value arctangent |
2: |
log(n) |
natural logarithm of n |
atan2(y,x): 它是 arctan(x)的一个扩展. 主要的区别是 ? π < arctan(x) < π , 0 ≤ atan2(y, x) < 2π, arctan(y/x),当 x = 0, 会出现除以0的情况
log(n):是以e为底 loge(n),而不是以10 为底, log10(n).
1.2.2 几何定义
最基础的渲染图形,也叫 drawing primitives,分表有点 线 三角面
一个场景是由多个模型,每一个模型都是由点 线 三角面组成的.场景同时也包括材质球,光照和摄像机
1.2.3 Shading
书中的 “shading,” “shader,”计算机生成的虚拟表面效果叫shading (比如 “shading model,” “shading equation,” “toon shading”),可编程的渲染组件叫shader (e.g., “vertex shader,” “shading language”)
第二章:图形渲染管线
这一张主要介绍了实时渲染的核心组件,渲染管线的主要功能就是渲染出一张2维的图像,这个2维的图像是通过摄像机,光照,模型等等共同生成的,因此渲染管线只是实时渲染的一个工具,通过它能够实时渲染出一张张2维图像,显示在屏幕上
这一章只是介绍管线上每一个阶段的主要功能,而不是具体的细节,比如如何实现的
2.1架构
流水线,就是包含了多个生产阶段,这些阶段并行工作,每一个阶段依赖于上一阶段的结果,理想情况下,一个非流水线系统被划分为n个流水线阶段,可以提供n倍的加速。比如加工一个三明治,每个人都准备一种食材,比如肉,面包,调料等. 如果每个人花费20秒,则一分钟可以加工三个. 这样就是并行的,但是如果有一个人准备需要30秒,则这个流水线就会停住了,一分钟只能加工2个,这个时间较长的阶段就是瓶颈,因为它决定了整个流水线的速度.
实时渲染流水线主要分为四个大阶段,这四个大阶段又分为多个小阶段,分别是application, geometry processing, rasterization, and pixel processing,
每一个阶段都有指定的任务去执行,一个给定的实现可以将两个功能阶段合并成一个单元或使用可编程核心去执行,把另一个更耗时的功能阶段划分为几个硬件单元去执行。
渲染的速度用FPS表示,每一秒渲染了多少张图像,也可以使用 (Hz)来表示,渲染一张图像使用多少秒,也就是画面更新的频率. 一般来说生成每张图像的时间是不同的,取决于每一帧执行计算的复杂度.FPS用来表述帧率,或者软件一段时间内的平均性能 ,HZ用来表述硬件的显示频率,一般是一个定值,你屏幕支持高刷,你的帧率上去才有意义
application 阶段只要在软件中执行,在cpu上执行,cpu一般都是多核的,可以同时处理多个线程,这能够加速application阶段的执行,一些传统的任务在cpu上执行,比如碰撞检测,全局加速算法,动画,物理模拟和其他一些在应用阶段执行的任务
下一个阶段 geometry processing,处理点的坐标 变换,投影等,这个阶段执行的计算包括哪些点是需要被绘制的,以及如何绘制的, 绘制到什么地方, 这个阶段是在GPU上执行的
rasterization stage 就是把输入的顶点,每三个构成一个三角形,计算这个三角形中包含了哪些像素,然后传递给下一个阶段
最终 pixel processing 阶段中,逐像素执行自己写的shader,来决定像素最终的颜色,以及可不可见.rasterization 和 pixel processing 阶段也在GPU中执行.
2.2 应用阶段
程序员可以控制应用阶段的流程,应为它一般是在CPU中执行,所以你可以修改其中的步骤来提高性能,比如一些算法或者设置可以减少渲染的三角形面数。
综上所述,一些应用阶段的工作也可以在GPU中执行,使用一个单独的模式,compute shader,这个模式把GPU当做一个高度并行的图形处理器,忽略了它专门用来渲染图形方面的功能.在应用阶段的末尾,图形数据被传递到 geometry processing 阶段这些数据就是渲染的基本图形,点、线、三角形面.
总结就是 应用阶段,在cpu 中准备好基本的图形渲染数据
因为应用阶段是在CPU中执行的,所以它不像其它阶段,可以分成多个子阶段,为了提高性能,一般会利用CPU的多核特点,并行来处理数据,在CPU设计中,这被称为超标量结构 (superscalar ). 18.5章节列出了使用GPU多核处理的方法
在应用阶段,通常也实现的过程是碰撞检测,在检测两个物体的碰撞关系之后,会给碰撞体和设备返回一个反馈,应用阶段同时也是处理其它输入源的地方,比如键盘、鼠标输入,或者是头显,根据不同的输入,做出不同的反应
加速算法,比如特殊的剔除算法,也是在这个阶段执行,其它阶段不能处理.
2.3 几何处理阶段
geometry processing 阶段是发生在GPU上的,主要是对三角形和顶点的操作,进一步划分为 vertex shading、 projection、 clipping、 screen mapping
2.3.1vertex shading 顶点着色
vertex shading主要有两个任务,即:计算顶点的位置以及计算后续阶段需要的数据(顶点输出数据里面),比如法线和uv,一般来说,通过对顶点添加光照,来计算它最终的颜色,并把颜色数据存储到顶点输出数据里面 . 三角形的颜色,是顶点颜色的线性插值,因此这个可编程的顶点处理单元被叫做顶点着色器 . 随着硬件的发展,特别是GPU,一些或者所有的着色操作会在片元着色器中处理(逐像素),而顶点着色器中不会执行任何的着色操作,现在顶点着色器专注于设置每个顶点的数据 比如, 顶点动画
下面介绍顶点位置是怎么计算的,顶点坐标要从局部坐标转换到世界坐标,再到视图坐标(方便投影和裁剪),再带裁剪坐标,再到屏幕坐标
这些坐标系的转换是通过 4×4 的矩阵实现的
为了实现一个真实的场景,只渲染模型的形状和位置是不够的,还包括模型的外表,这部分通过材质球,和光照来表现
决定材质球上的光照效果的操作,叫做着色,在顶点着色器阶段、片元着色器阶段都可以对顶点或者像素着色. 顶点着色的结果被传递给下面的阶段,光栅化和像素处理阶段,通过插值,来计算表面效果
顶点着色阶段还要执行 投影 和裁切 ,把视锥体转换一个单位的立方体,它的坐标在(?1, ?1, ?1) 到 (1, 1, 1)之间.这个立方体叫做标准视锥体 (canonical view volume). 首先要做的是投影(Projection) ,投影阶段包括正交投影(也叫平行投影)和透视投影
正交投影只是平行投影的其中的一种类型,还有一些其它的投影,如斜向投影和轴测投影。古老的街机游戏《Zaxxon》就是以后者命名的。
投影是通过矩阵来表示的,也就是正交投影矩阵和透视投影矩阵
经过投影坐标转换之后,模型处于裁剪坐标系.顶点的坐标是齐次坐标,也就是vertex shading 阶段要输出齐次坐标的顶点,来便于下个阶段,裁剪
最终生成的图片是二维的,那么这些顶点的z坐标,也就是深度 ,存储在z-buffer中
2.3.2 可选择的顶点处理
当顶点处理结束后,也就是顶点处理几点的输出数据已经有了,得到了裁剪空间下的坐标,会有几个可选择的阶段,当然这些阶段也是发生爱GPU中的,依次是: tessellation、geometry shading、 stream output. 这些阶段和GPU的能力有关,有的GPU不支持,它们之间相互独立,一般来说不会同时使用
tessellation.曲面细分着色器,假如你有一个弹力球,你用一组三角形面表示它,五米之外可能看起来没问题,但是离得近了,就不行了,模型就会变得很粗糙,如果你的模型有很多三角形面,又会很浪费性能和内存,曲面细分会生成指定数量的三角形面,也就是我们说的LOD
顶点可以用来描述曲面,比如球体,它的表面有多个多边形面组成,每一个多边形面又包含了多个顶点. 曲面细分阶段也由很多个阶段组成,比如hull shader、tessellator、domain shader(把多个三角形面合并到一起组成一些大的三角形面),场景中的摄像机决定了最终生成的三角形面数,距离越远,生成的面数越少
geometry shader. 几何着色器,它比曲面细分着色器更早出现,所以在大多数GPU上都可以被找到,它和曲面细分着色器很像,也是把点线面分类,生成新的顶点,它是一个非常简单的阶段,因为它执行的条件和输出结果的类型都有限的多,它一般用于粒子生成上 ,就像一个爆炸效果,每一个火花都可以用一个顶点表示,几何着色器可以把这个点转换成一个平面,由两个三角形面组成,让效果更加逼真
stream output. 这个阶段能够把GPU当做一个几何引擎,可以把顶点数据输出到一个数组里面,供我们处理,而不是把数据传递给下个阶段,渲染到屏幕上,这些数据 也可以被CPU使用,或者GPU,它经常用于粒子模拟上,比如我们的烟花
tessellation, geometry shading, stream output依次调用,每一个都是可选择的,不管我们选择哪个,如果我们继续我们的流水线,我们这点裁剪空间下的顶点,会执行到裁剪阶段,决定哪些可以被摄像机看到
2.3.3裁切
只有完全在视锥体下的点线面才可以被传到光栅化阶段,才能被绘制出来. 完全在视锥体外面的将会被裁剪掉,对于那些一部分在视锥体内,一部分在视锥体外的对象,就要被裁切,比如一条线一个顶点在外面,一个顶点在里面,这样就会裁切掉外面的顶点,生成一个新的顶点. 使用投影矩阵. 转换后的顶点,被裁剪到立方体内,在裁剪之前执行视图转换和投影的优点是它使裁剪空间保持一致,这样点线面就总会被裁剪到单位立方体内
除了视锥体的六个裁剪面之外,你还可以自定义裁剪面,这个方法叫做切片法,19章有描述
裁切,也就是投影变换,使用的是顶点的齐次坐标,坐标值在透视坐标空间下并不是线性插值过来的,第四个坐标w,被用来透视坐标空间下的裁剪,最终,通过透视除法,也就是除以自身坐标的w值,把三角形面的位置转换到三维空间下,立方体的范围是 (?1,?1,?1) to (1,1,1). 几何阶段的最后一步就是把该坐标转换到屏幕坐标系下。
2.3.4 屏幕映射
只有在视锥体内的点线面才可以参与屏幕映射,上一阶段产生的是三维坐标,范围在-1到1之间,下面要把它们的x,y坐标转成屏幕坐标,屏幕坐标也是包含z 坐标的,OpenGL是[-1,1], DirectX是[0-1],默认是0-1,它的值可以用API更改,当然屏幕映射后的z坐标一样会被传递给光栅化阶段
接下来我们讨论整数和浮点数,是如何和像素坐标(uv)坐标相关联的,在二维笛卡尔坐标系中,左下角是[0,0] . 像素的中心点是0.5,所以像素的索引范围如果是 [0, 9] ,覆盖的坐标是[0.0, 10.0).
d 是像素的索引,c是像素中的值
OpenGL 是左下角00,右上角11,DX是左上角00,右下角11,在位移的时候需要注意
2.4 光栅化
通过几何阶段传递下来的数据,找出三角形中包含的像素,这个过程叫光栅化,它又分为两个功能阶段:三角形设置、三角形遍历 . 这俩阶段也可以处理点和线,只是因为三角形比较通用,所以这俩功能阶段的名字中含有三角形,光栅化,也叫扫描转换,把二维的屏幕坐标的顶点,每一个顶点包含z坐标值,也就是它的深度值,和其它的一些数据,转换成像素,它也可以被看做是几何阶段到逐像素阶段的桥梁,把顶点组成三角形,传递给像素处理阶段
一个三角形是否覆盖了一个像素,取决于你如何设置GPU的渲染管线,比如,你可以使用点采样的方式,决定哪些像素被包含在三角形中,采样点是每个像素的中心点,只要中心点在三角形内,就认为该像素在三角形内,你也可以使用多重采样方法,比如超级采样和多重抗锯齿采样. (supersampling or multisampling antialiasing techniques) (Section 5.4.2). 或者采用传统的采样方法,只要像素有一部分在三角形内,就认为它整体都在三角形内(Section 23.1.2).
2.4.1 三角形设置
在这个阶段,微分,边缘微分 和其他一些关于三角形的数据被计算出来,In this stage the differentials, edge equations, and other data for the triangle are computed. 这些操作是特定功能的硬件来执行的
2.4.2 三角形遍历
这个阶段会遍历三角形中覆盖的像素,并为之生成一个片元,一个像素对应一个片元,找出哪个像素包含在三角形中,称之为三角形遍历,每一个三角形片段的数据,是三个顶点数据的差值,这些属性包括该片段的深度,和其它几何阶段的数据,所有在点线面中的像素,都会被传递给下一阶段,也就是逐像素处理阶段
2.5 像素处理
上面所有阶段的处理结果,就是找到了三角形中所包含的像素,像素处理阶段,分为两个功能阶段:片元着色、合并,像素处理阶段,是逐像素的
2.5.1 像素着色
任何逐像素的渲染计算,都会在这个阶段执行,结果就是生成一种或者多种颜色,传递给下一个极端,不像三角形设置和三角形遍历,有专门的硬件来处理,逐像素着色阶段是被可编程的GPU core来执行的,程序员提供片元着色器,里面包含了计算的方法,各种各样的技术都会在这里面执行,一个最重要的就是纹理采样,简单说就是给模型穿衣服,贴图可以是二维的、三维的,大部分是二维的,然后为每一个像素生成一个颜色,传递给下一个阶段
2.5.2 合并 merging
最终每个像素的数据保存在color buffer中,color buffer是一个方形数组,里面存储了像素的颜色, 然后把该颜色(当前像素的颜色)和color buffer中已经存在的颜色合并,这个阶段也叫 ROP, “raster operations (pipeline)” or “render output unit,”不像着色阶段,这一阶段不是完全可编程的的,但是高度可配置的
下一个阶段通过 z-buffer (also called depth buffer),也叫深度测试,来决定哪些像素可见,也就是颜色缓存中包含哪些像素的颜色. z-buffer和 color buffer一样大, 每一个像素都有一个深度值,它保存了当前距离摄像机最近的点线面的值,这意味着当一个基础图形被渲染的时候,它要和之前该像素的深度值作比较,深度值越小,越被渲染. 因此深度缓存和颜色缓存的值随着被绘制的基本图形的改变而改变. 如果深度测试没通过,则不会更改颜色缓存中的值,深度测试的复杂度是O(n) n 是被渲染图形的数量,也就是要比较多少次,然而它仅仅存储的是到摄像机的距离,所以不适用于透明物体,透明物体必须在所有的不透明物体渲染之后按照从后往前的顺序再渲染. 或者使用一个单独的算法
除了z-buffer、color-buffer 还有其它的buffer可以存储片元的信息,比如alpha 通道,alpha channel 存储了每一个像素的透明度,在以前,alpha 值用来做alpha测试,现在有一个专门的程序执行,任何类型的计算都可以达到alpha 测试的效果,不仅仅使用alpha 值了
stencil buffer 永安里记录基本图元的位置,一个像素通常包含8bit ,stencil buffer 通常用来控制渲染到color buffer 和z-buffer中的物体,
所有的这些操作都可以叫做混合,把当前的颜色和color buffer中的颜色混合起来,混合是高度可配置的,不是完全可编程的.
framebuffer 通常由一个系统中所有的 buffers 组成.
当基本图元传递到光栅化阶段,摄像机中能看到的点被展示到屏幕上,屏幕上展示的就是color buffer中的内容,为了防止看到光栅化然后传递到屏幕上这一过程,使用了两个buffer,后置buffer,和前置buffer,后置buffer负责渲染到color buffer中,然后传递给前置buffer中,闲置出来,传递通常发生在垂直扫描阶段,所以比较安全