2.1 OpenGL Fundamentals
OpenGL(以下简称为GL)只定义了一套如何控制framebuffer中最终绘制结果、以及从framebuffer中读回数据的API,不涉及任何鼠标、键盘等外设的行为。所以,应用程序需要自行检测鼠标键盘的输入,并将输入转换为glAPI调用。
GL支持多种相互独立的绘制模式,在每种模式下,GL绘制的基本单元都是primitive(图元),其类型包括point(点)、line segment(线段)和polygon(多边形)。每个primitive由若干个vertex(顶点)组成,每个vertex具有positional coordinate(位置坐标)、color(颜色)、normal(法线)和texture coordinate(纹理坐标)等属性。在GL pipeline中,每个vertex的处理过程都是相同而且独立的,所以可以用并行的方法来加快处理速度。唯一的例外是当primtive被clip时,某些vertex会被抛弃,某些vertex则可能被自动生成,这取决于primitive类型。
glAPI以C函数的形式存在,其被调用顺序决定了其被OpenGL执行的顺序,先被调用的一定先被执行,但是具体的执行时间则由OpenGL实现决定,一般来说,为了提高效率,其执行时间总是被退后延迟的,一个理想的OpenGL实现会将执行时间推迟到不能再推迟为止,当然,这样在逻辑上就非常复杂了,所以一个OpenGL实现需要平衡执行效率性和逻辑复杂性,从而决定一个恰当的执行时间。有些glAPI可以显式的要求之前被调用的glAPI都被执行,有些glAPI内含了之前被调用的glAPI都被执行的要求,这些都会被OpenGL实现所遵从的。
data binding on call这段容易产生误解,也难以用简短几句话就将问题解释清楚,不讲也罢。
GL支持三维空间建模的基本要素输入,比如输入光源信息、物体表面的材质参数等,GL pipeline就会根据这些信息计算出物体表面的最终颜色是什么,并绘制到framebuffer中。GL并不建议应用程序自行根据光源参数和材质参数计算出颜色值,然后将此颜色值作为GL的输入,这样就失去了GL的意义,也无法享受到了graphics hardware加速带来的好处了。
回顾section1.3,应用程序首先需要创建window和context并进行绑定,然后才能调用glAPI进行绘制,在绑定之前调用的glAPI行为结果未定义。GL采用了和X类似的client-server模型,应用程序处于client端,GL context处于server端,这样,应用程序和OpenGL实现就可以处于两台不同的机器上,他们是网络透明的。举例来说,我从本机A远程登录到机器B,然后在B上运行一个OpenGL应用程序,结果在本机A的屏幕上显示,此时,机器B就是一个client,其上应用程序的glAPI经由网络被传送到本机的OpenGL驱动之上,借助本机的OpenGL实现绘制到本机的framebuffer中,此时的本机就是一个server。
framebuffer包括两种类型:window system-provided的default framebuffer,application-created的framebuffer object。default framebuffer在创建window时自动创建,这是平台native window system的API控制的,参见section1.7.2。在default framebuffer和GL context绑定时,或者在绑定后window发生变化时,向GL提供诸如width、height、stride、start address、pixel format等信息,这些信息用以初始化context并决定绘制结果是如何被写入framebuffer的。没有任何glAPI可以控制default framebuffer的属性,比如设置grammar correction等。framebuffer object则完全由glAPI控制。当context和default framebuffer绑定时,绘制结果在屏幕上显示;而当context和framebuffer绑定时,绘制结果并不会在显示设备上显示,所以又被称为offscreen rendering。
OpenGL的设计目标是满足各类高中低端设备的要求,所以,OpenGL并不定义具体的实现方法,只规定了实现方法应该符合的指标,任何符合规定指标的实现方法都被认为是符合OpenGL spec的,所以,不同档次的设备就可以采用和自身定位匹配的实现方法,这也使得不同设备下的绘制结果不可能所有像素都是相同的。
最后,为了区别,本spec中规定的函数、常量、类型分别用gl、GL_和GL开头,而为了简洁,本spec将省略这些前缀。
2.1.1 Numeric Computation数值计算
先复习一下,整数和分数(包括有限小数和无限循环小数)构成有理数,无限不循环小数则是无理数,实数由有理数和无理数构成。在计算机科学中,无论如何都无法表示出整个实数域,浮点数则可以表示最大可能的数值范围。一个浮点数包括符号位、指数部分和尾数部分,不同的浮点数格式其指数部分和尾数部分所占的比特数各不相同,指数部分和尾数部分所使用比特数则决定了该格式浮点数所能表示的最大范围和最小精度。由于GL存在着大量的浮点数计算,所以需要特别注意,以避免容许范围外的数值溢出和精度损失。
GL定义了一些浮点数具体格式,比如16-Bit Floating-Point、Unsigned 11-Bit Floating-Point和Unsigned 10-Bit Floating-Point(其具体内容略)。对于未定义具体格式的浮点数,GL也规定了所有GL实现需要达到的最大数值范围和最小精度的最低要求(内容略),该要求不包括shader内部的计算(shader内部计算要求由GLSL spec定义)。一般来说,对未定义具体格式的浮点数,GL实现都会采用32位来表示(同32位机器上c/c++的float数据类型)。
另外,如果发生除零的情况,其结果未定义,但不可以造成系统崩溃。
2.1.2 Normalized Fixed-Point <—> Floating-Point
这里的normalized fixed-point其实就是整数integer,分为有符号和无符号,以下以8bits为例,介绍其转换。
[0, 255] -> [0.0, 1.0] y = x/255.0
[-128, 127] -> [-1.0, 1.0] y = (2x+1)/255.0
当glAPI设置vertex属性值时使用signed normalized fixed-point格式时,采用这样的转换
[-127, 127] -> [-1.0, 1.0] y = x/127.0 且 –128->-1.0
当texture或者framebuffer使用signed normalized fixed-point格式时,采用这样的转换
[0.0, 1.0] -> [0, 255] y = x*255
[-1.0, 1.0] -> [-128, 127] y = (x*255 – 1)/2
在调用glAPI查询内部为浮点数格式而返回类型为整数时,采用这样的转换
[-1.0, 1.0] -> [-127, 127] y = x*127
当用浮点数格式的glAPI参数来设置signed normalized fixed-point格式的texture或framebuffer时,采用这样的转换。