现代3D图形编程学习-基础简介(2) (译)

本书系列

现代3D图形编程学习

基础简介(2)

图形和渲染

接下去的内容对渲染的过程进行粗略介绍。遇到的部分内容不是很明白也没有关系,在接下去的章节中,会被具体阐述。

你在电脑屏幕上看到的任何东西,包括你现在阅读的文字是简单的二维像素点的集合。如果你在屏幕上截图,并放很大,就会看到一个个的方格。

Figure8. An Image

没一个格子就是一个像素。pixel单词,是从Picture Element中提取出来的。你屏幕上的没一个像素都有一个独立的颜色。二维的像素数组就组成了 图像

因此,任何一种图像操作的目的都是确定将什么颜色放到哪个像素上。不同颜色在不同像素位置的设定形成了文字,窗口,等其他东西。

既然所有的图像都是二维的像素组成的,那么3D图像是怎么来的呢?3D图像是一系列带颜色的像素组成的,并且给人的视觉上看上去像是一个3D的世界,而不是单纯的2D的图。这个将3D的世界转变成2D图像的过程叫做 渲染

3D图像的渲染有不同的方法。实时图形显卡中引入了一种很巧妙的方法来实现渲染。这个过程被称为光栅化,实现光栅化的系统被成为 光栅器(rasterizer)

通过光栅,你看到的所有的对象都是空壳。有一些技术可以用来将这些空壳打开,但是这样也仅仅是用一部分壳替换了另一部分的壳。其实所有的东西都是壳(shell)。

上面这段话,可以理解成,所有的对象都是由不同的等直面组成的,就好比在一个鸡蛋壳里面,有鸡蛋壳,一层套这一层,当将最外面的壳打开后,你能够看到里面的壳(等值面)。

这些壳都是由三角形组成的。即使是那些看起来非常平滑的曲面,如果很靠近的去观察它,可以发现它其实也是由壳组成的。有技术可以产生更多的三角形,使得物体之间看起来更加接近或是更加大,以至于观察者几乎看不到物体的侧面。不过,它们也都是由三角形组成的。

上面这段话意思是,鸡蛋壳,通过光栅器,看到的并是不光滑的面,而是由一个个小三角形组成的,并且靠近观察者的地方,小三角形看起来比较大,而在远离观察者的地方,如靠近侧面,小三角形看起来就比较密。另一面的内容,你无法看到,但是它还是由许多小三角形组成的。

注意事项
一些光栅的实现使用的是平面四边形:拥有四条边,并且所有的点都在一个平面上。硬件使用的光栅在原理上使用三角形的原因是,三角形能够保证所有的边都能够在一个平面上。这样可以很大程度的简化光栅化的过程。

一个对象的外表面是由一系列相邻的三角形组成的。这样的一系列的三角形通常被称为 geometry , model 或者是 mesh 。这些术语会被交换的使用。

光栅化的过程拥有几个阶段。这些阶段按顺序归入到一个pipeline中,最开始的是输入的一系列三角形,最终输出的是2D图。这也是回升么光栅化的过程是易于硬件加速的:按照特定的顺序,每一次都对一个三角形进行操作。三角形可以在pipeline的开始阶段输入,然而更早输入的三角形也可以出现在光栅化的某个阶段。

三角形和各种网格的输入顺序会影响它的输出结果。需要记住的是,不管你怎样输入三角形网格数据,光栅都将以一定的顺序处理各个三角形,当前一个三角形被处理完了之后,才会处理下一个三角形。

OpenGL有获取基于硬件的光栅的API。正因为这样,它会使得模型适用于光栅化的渲染。光栅会接受用户输入的一些列三角形,然后针对这些三角形进行操作,并将像素结果输出。这个过程简单描述了OpenGL中光栅的工作机制,但,对于我们而言是足够了。

三角形和顶点. 三角形包括三个顶点。 顶点可以是任何数值。为了简单起见,我们约定顶点是三维空间上有值的点。任何三个不再一条直线上的点可以组成一个三角形。因此,我们可以得到的信息是,一个三角形包含3个有三个维度的点。

三维空间中的一个点,含有三个分量,分别是x,y,z轴。通常用一下形式表示\((X,Y,Z)\)

光栅化概述

光栅化的流程对于现代硬件来说是非常复杂的。这里非常简单的概述了整个流程。在我们进一步通过OpenGL学习渲染之前,有必要对这个流程有一个初步的认识。更详细的人荣处理更高层次的概述,是没有办法讲清楚的。

裁剪空间转换 光栅化的第一步是将三角形中的每一个顶点转化到特定的空间区域。在空间体中的任何东西都会被渲染到输出图像中,在空间体外的其他内容是不会渲染到图像中的。这个区域与用户想要渲染的位置有关。

这个三角形即将被转换到的空间,在OpenGL中的称呼为, 裁剪空间 clip space 。三角形的定点在裁剪空间中的位置被称为 裁剪坐标

裁剪坐标和常规的点有一点区别。一个点在3D空间中有三个坐标轴。一个点在裁剪空间有4个坐标轴。前三个是X,Y,Z,第四个被称为W。最后一个坐标实际上定义了这个定点的裁剪空间的范围。

在裁剪空间,X的正方向是向右,Y的正方向是上,Z的正方向是原理观察者。

顶点位置转换到裁剪空间的过程有很多途径。OpenGL提供了很多灵活的方式。对于这个过程在以后的章节中会详细的介绍。

因为裁剪空间是世界中的可见部分转换而来的,所以任何在这个区域外的三角形会被抛弃掉。任何部分在裁剪空间外的三角形,都有经历 裁剪 的过程。这个过程将一个三角形分割成一些小的三角形,使得所有的三角形都能够在裁剪空间中。因此,这个空间就被称为“裁剪空间”。

标准化坐标 裁剪空间比较有意思,但是并不是很方便。这个空间的大小对每一个顶点来说是不一样的,这使得一个三角形的可视化变得非常困难。因此,裁剪空间会被转换成更加合理的坐标空间: 标准化设备空间

这个过程非常发简单,就是将每一个顶点的X,Y,Z除以W,就能够得到标准化设备空间。

标准化设备空间,除了它的X,Y,Z坐标范围为[-1,1]以外,其实就可以理解成裁剪空间。两个空间坐标系的方向是一致的。除数W,在将3D的三角形投影到2D的图像的时候,起到了非常重要的作用;我们将在后续的内容中进一步说明。

Figure9. Normalized Device Coordinate Space

这个立方体,表示标准设备空间的边界。

窗口转换 接下去的一个阶段是再次变换每个三角形的顶点。这一次是将标准设备坐标系转换到 窗口坐标系 。从名字上可以看出,窗口坐标系就是OpenGL运行的窗口。

尽管它指的是窗口,但仍然是三维的坐标系。和裁剪空间一样,X是右方向,Y是上方向,Z是远离观察者的方向。唯一不同的是,这些轴的范围依赖于看到的窗口。还有一点需要注意的是,虽然这些点都在窗口坐标系中,但是点的精度并没有损失。点并没有成为整数点,依然是浮点数,因此点的精度和单个像素是不一样的。

Z的范围是[0,1],0表示最近的,1表示最远的。在这个范围以外的点是不可见的。

需要注意的是,窗口坐标系中,左下角的位置是原点(0,0)。这个与用户通常认知中的屏幕坐标系,左上角是原点是不一样的。如果你想要,通过一定的更改,可以将变换变换到左上角是原点的坐标系中。

这个过程会在辅助材料中进行详细讨论。

扫描转换 Scan Conversion 将三角形的顶点转换到窗口坐标系后,这个三角形还得经历 扫描转换 的过程。这个过程就是依据三角形覆盖的像素点的位置,将三角形分解成不同的像素的过程。

Figure10. Scan Converted Triangle

中间的图显示了输出图像的像素格子的大小;圆圈表示了每个像素的中心位置。每一个像素的中心表示一个 取样(sample) :像素区域内的离散位置。在扫描转换中,一个三角形将会在三角形3D区域内部每一个像素取样的位置产生一个 碎片(fragment)

右边的图像显示的就是通过扫描转换产生的碎片。这个产生了三角形形状的一个近似。

待渲染的三角形有公共的边是一个很常见的事情。只要公共的定点是相同的,OpenGL就能够保证扫描转换出来的结果中,两个三角形之间没有采样间隔。

Figure11. Shared Edge Scan Conversion

为了更简单的使用扫描转换,OpenGL同时也保证相同的输入,能够得到完全相同的渲染输出。这个也被称为, 不变性保证 。因此,需要用户保证使用相同的输入点,来获得没有间隔的扫描转换。

扫描转换是一个固有的2D操作。这个过程仅仅使用了三角形在窗口坐标系中的X和Y的值,来确定需要产生哪些碎片。不要忘了Z值,但是Z值和三角形扫描转换并没有直接的关系。

三角形扫描变化的结果是覆盖在三角形上的有顺序的碎片。每一个碎片都有一个数值与它关联。这个数值包括了碎片在窗口坐标系中的2D位置,同时也有Z的位置。Z值是碎片的深度。还有一些与碎片相关的其他参数,这将在后续的内容中阐述。

碎片处理(Fragment Processing)

这个阶段,通过扫描转换将得到的碎片设置一种或更多的颜色,和一个深度值。单个三角形中的碎片被处理的顺序是无关紧要的,因为一个三角形位于同一个平面上,产生的碎片不会有重叠的可能。但是,可能和另一个三角形的碎片发生重叠。由于光栅器中顺序的重要性,使得一个三角形中的碎片必须在另一个三角形的碎片处理之前处理完。

这个阶段有很大的自由性,OpenGL的用户有各种不同的选择来决定要赋予碎片的颜色。在辅助教材中,我们会展开详细的论述。

碎片输出(fragment writing) 在产生一个或多个颜色值和一个深度信息之后,这个碎片会输出到目标图像中。这个过程不单单是将颜色信息赋值到图像上。需要经过计算,才能将碎片的将颜色和深度信息以及图像中已经有的颜色信息一起融合到一块。在不同的章节中都会涉及到这部分的内容。

颜色

正如前面所述,一个像素表示图中拥有特定颜色的一个元素。一个颜色可以以很多不同的方式展现。

在计算机图形学中,对颜色的描述通常是不同通道下[0,1]范围内的数字组成。最终的颜色是各个通道下颜色的组合。

各个通道下颜色的集合叫做 颜色空间 。最常见的颜色空间就是RGB颜色空间,该颜色空间有三个颜色通道,分别是红,绿,蓝。针对打印的颜色空间通常是CMYK(Cyan青色,Magenta洋红,Yellow黄色,Black黑色)。由于我们处理的场景是将图像渲染到屏幕上,因此我们使用的颜色空间是RGB颜色空间。

注意
你也可以对可编程的着色器进行一些特殊的处理,来允许你对不同的颜色空间进行操作。因此,从技术上来说,我们仅仅需要将结果输出到线性的RGB颜色空间即可。

因此,在OpenGL中一个像素有三个[0,1]范围的值,用来表示RGB颜色空间中的RGB三个分量。将这三个颜色的不同大小进行组合,我们能够得到成千上万的不同的颜色。当我们处理透明度的时候,这个还会得到进一步的扩展。

着色器

着色器 被设计用来在渲染器上运行部分操渲染操作的程序。不管使用的渲染系统的类型,着色器只能在渲染过程中特定的过程被使用。这些 着色阶段(shader stages) 指的是用户可以添加任意算法实现特别视觉效果的位置。

根据上面总结出来的光栅化过程,有几个着色阶段的实现既保证了运行的性能,有向用户提供了可编程的简易性。例如,将定点转换到裁剪坐标系,有比如将碎片设定特定的颜色和深度。

OpenGL的着色器是运行在渲染硬件上的。这样可将CPU空置出来执行其他的任务,或者如果没有执行任意代码的灵活性,那么简单的执行一些操作也将是困难的事情。这样的缺点是,着色器的使用是有一定限制的,而CPU是没有限制的。

目前有各种各样的着色器语言。本书中使用的是OpenGL的主要的着色器语言,被称为“OpenGL Shading Language”,简称GLSL。它的语法看着非常像C语言,但并不是C语言。

posted @ 2016-12-20 23:21  grassofsky  阅读(1539)  评论(0编辑  收藏  举报