OpenGL/ES数据类型和修饰符
概述
使用编程语言进行编程时,需要用到各种变量来存储各种信息。变量保留的是它所存储的值的内存位置。这意味着,当您创建一个变量时,就会在内存中保留一些空间。您可能需要存储各种数据类型(比如字符型、宽字符型、整型、浮点型、双浮点型、布尔型等)的信息,操作系统会根据变量的数据类型,来分配内存和决定在保留内存中存储什么。
修饰符用于改变基本类型的含义,所以它更能满足各种情境的需求。
OpenGL是一个跨平台的API,数据类型的大小会随使用的编程语言以及处理器(64位,32位,16位)等的不同而不同,所以OpenGL定义了自己的数据类型。当传递数据到OpenGL时,你应该坚持使用这些OpenGL的数据类型,从而保证传递数据的尺寸和精度正确。不这样做的后果是可能会导致无法预料的结果或由于运行时的数据转换造成效率低下。不论平台或语言实现的OpenGL都采用这种方式定义数据类型以保证在各平台上数据的尺寸一致,并使平台间OpenGL代码移植更为容易
OpenGL各种数据类型
- GLuint: 无符号四字节整型,包含数值从0 到 4,294,967,295
- GLfloat: 四字节精度IEEE 754-1985 浮点数
- GLint: 有符号四字节整型,包含数值从−2,147,483,648 到 2,147,483,647
- GLbyte: 有符号单字节整型,包含数值从-128 到 127
- GLenum: 用于GL枚举的无符号整型。通常用于通知OpenGL由指针传递的存储于数组中数据的类型(例如,GL_FLOAT用于指示数组由GLfloat组成)。
- GLboolean: 用于单布尔值。OpenGL ES还定义了其自己的“真”和“假”值(GL_TRUE和GL_FALSE)以避免平台和语言的差别。当向OpenGL传递布尔值时,请使用这些值而不是使用YES或NO(尽管由于它们的定义实际没有区别,即使你不小心使用了YES或NO。但是,使用GL-定义值是一个好的习惯。)
- GLbitfield: 用于将多个布尔值(最多32个)打包到单个使用位操作变量的四字节整型。我们将在第一次使用位域变量时详细介绍,请参阅 wikipedia
- GLbyte: 有符号单字节整型,包含数值从-128 到 127
- GLshort: 有符号双字节整型,包含数值从−32,768 到 32,767
- GLsizei: 有符号四字节整型,用于代表数据的尺寸(字节),类似于C中的size_t
- GLubyte: 无符号单字节整型,包含数值从0 到 255。
- GLushort: 无符号双字节整型,包含数值从0 到 65,535
- GLclampf: 这也是四字节精度浮点数,但OpenGL使用GLclampf特别表示数值为0.0 到 1.0
- GLvoid: void值用于指示一个函数没有返回值,或没有参数
- 定点数 使用整型数存储实数。由于大部分计算机处理器在处理整型数比处理浮点数快很多,这通常是对3D系统的优化方式。但因为iPhone具有用于浮点运算的矢量处理器,我们将不讨论定点运算或GLfixed数据类型。
- GLclampx: 另一种定点型,用于使用定点运算来表示0.0 到 1.0之间的实数。正如GLfixed,我们不会讨论或使用它。
OpenGLES数据类型
数据类型.jpgIntegers
整型主要作为编程的援助角色。在硬件级别上,真正地整数帮助有效的实现循环和数组索引,纹理单元索引。然而,着色语言没必要将整型数映射到硬件级别的整数。我们并不赞成底层硬件充分支持范围广泛的整数操作。OpenGL ES着色语言会把整数转化成浮点数进行操作。整型数可以使用十进制(非0开头数字),八进制(0开头数组)和十六进制表示(0x开头数字)。
Floats
浮点型用于广泛的标量计算。
Vectors
OpenGL ES着色语言包含像2-,3-, 4-浮点数、整数、booleans型向量的泛型表示法。浮点型向量可以保存各种有用的图形数据,如颜色,位置,纹理坐标。
vec2 texCoord1, texCoord2;
vec3 position;
vec4 rgba;
ivec2 textureLookup;
bvec3 lessThan;
向量的初始化工作可以放在构造函数中完成。
Matrices
矩阵是另一个在计算机图形中非常有用的数据类型,OpenGL ES着色语言支持22, 33, 4*4浮点数矩阵。
mat2 mat2D;
mat3 optMatrix;
mat4 view, projection;
矩阵的初始化工作可以放在构造函数中完成
Sampler
采样器类型(如sampler2D)实际上是纹理的不透明句柄。它们用在内建的纹理函数来指明要访问哪一个纹理。它们只能被声明为函数参数或uniforms。除了纹理查找函数参数, 数组索引, 结构体字段选择和圆括号外,取样器不允许出现在表达式中。取样器不能作为左值,也不能作为函数的out或inout参数。这些限制同样适用于包含取样器的的结构体。作为uniforms时,它们通过OpenGL ES API初始化。作为函数参数,仅能传入匹配的采样器类型。这样可以在着色器运行之前进行着色器纹理访问和OpenGL ES纹理状态的一致性检查。
Structures
通过结构体用户可以创建自己的数据类型。结构体不支持内部匿名结构体对象,也不支持内部嵌入结构体,但可以声明另一个结构体的变量。
Arrays
同种类型的变量可以放在一个数组中保存和管理。数组长度必须是大于0的常整型数。用负数或大于等于数组程度的索引值索引数组是不合法的。数组作为函数形参必须同时指明数组长度。仅支持一维数组,基本数据类型和结构体类型都可以作为数组元素。不能在着色器中声明数组的同时进行初始化。
Scopes
声明的范围决定了变量的可见性。GLSL ES使用了静态嵌套范围,允许在着色器内重定义一个变量。
存储修饰符
存储修饰符.jpgconst
本地变量只能使用存储修饰符const, 函数参数只能用const。函数返回值类型和结构体字段不要使用const。从一个运行时着色器到下一个运行时着色器之间进行数据类型通信是不存在的。这阻止了同一个着色器在多个顶点和片元之间同时执行。没有存储修饰符或仅仅使用const修饰符的全局变量,可能在main()执行前进行初始化。Uniforms, attributes和varyings可能没有初始化器。
默认修饰符
如果在全局变量前没有修饰符,那么它们就与应用程序和其他处理器上的着色器没有关联。对于全局或本地的无修饰符变量,声明都会在其所属的那个处理器上分配内存。这个变量将提供对分配的内存的读写访问。
常量修饰符
命名的编译时常量可以用const声明。任何使用const声明的变量在其所属的着色器中均是只读的。将变量声明为常量可以减少使用硬连线的数字常数。const可以用来修饰任何基本数据类型。通常const变量在声明的同时要进行初始化:
const vec3 zAxis = vec3 (0.0, 0.0, 1.0);
结构体字段不能使用const修饰吗,但是变量可以,并通过构造器进行初始化。包含数组的数组和结构体不能声明为常量,因为它们不能被初始化。
Attribute
attribute修饰符用于声明通过OpenGL ES应用程序传递到顶点着色器中的变量值。在其它任何非顶点着色器的着色器中声明attribute变量是错误的。在顶点着色器被程序使用之前,attribute变量是只读的。attribute变量的值通过OpenGL ES顶点API或者作为顶点数组的一部分被传进顶点着色器。它们传递顶点属性值到顶点着色器,并且在每一个运行的顶点着色器中都会改变。attribute修饰符只能修饰float, vec2, vec3, vec4,mat2,mat3,mat4。attribute变量不能声明为数组或结构体
Uniform
uniform修饰符用来修饰那些在整个图元被处理的过程中保持不变的全局变量。所有的uniform变量都是只读的,可以通过应用程序调用API命令初始化,或者通过OpenGL ES间接初始化。uniform修饰符可以和任意基本数据类型一起使用,或者包含基本数据类型元素的数组和结构体。每种类型的着色器的uniform变量的存储数量是有限制的,如果超过这个限制,将会引起编译时或链接时错误。声明了但是没有被静态使用的uniform变量不会受到这个限制。静态使用(static use)是指着色器包含变量在预处理以后的一个引用。用户定义的uniform变量和着色器中被静态使用的内建uniform变量将共同决定有没有超出可用uniform存储范围。
当顶点着色器和片元着色器被链接到一起,它们将共享同一个名称空间。这就意味着,所有被连接到同一个可执行程序的着色器中的同名变量必须也同时具有相同的类型和精度
varying
1344480339_7346.jpgvarying变量提供了顶点着色器,片元着色器和二者通讯控制模块之间的接口。顶点着色器计算每个顶点的值(如颜色,纹理坐标等)并将它们写到varying变量中。顶点着色器也会从varying变量中读值,获取和它写入相同的值。如果从顶点着色器中读取一个尚未被写入的varying变量,将返回未定义值。
通过定义,每个顶点的varying变量以一种透视校正的方式被插入到正在渲染的图元上。如果是单采样,插值为片元中心。如果是多采样,插值可以是像素中的任何地方,包括片元中心或者其中一个片元采样。
片元着色器会读取varying变量的值,并且被读取的值将会作为插值器,作为图元中片元位置的一个功能信息。varying变量对于片元着色器来说是只读的。
在顶点和片元着色器中都有声明的同名varying变量的类型必须匹配,否则将引起链接错误。 下表总结了顶点和片元着色器匹配的规则:
术语“静态使用”意思是在预处理之后,着色器至少包含一个访问varying变量的语句,即使这个语句没有真正执行过。
varying vec3 normal;
varying修饰符只能用在float, vec2, vec3, vec4, mat2, mat3, mat4和包含这些类型元素的数组上,不能用于修饰结构体。
varying变量需要声明为全局变量。
参数修饰符
函数参数修饰符有如下几种:
(1)<none: default>,默认情况下,是in
(2)in,作为函数的传入参数
(3)out,作为函数的传出参数
(4)inout,即作为传入参数,又作为传出参数
精度和精度修饰符
用于存储和展示浮点数、整数变量的范围和精度依赖于数值的源(varying,uniform,纹理查找,等等),是不是顶点或者片元着色器,还有其他一些底层实现的细节。最低存储需要通过精度修饰符来声明。典型地,精度操作必须要保留变量包含的精度存储。仅有的例外是需要大量复杂计算的内建函数,如atan(),返回值的精度低于声明的精度。
强烈建议顶点语言提供一种匹配IEEE单精度浮点数或更高精度的浮点数的浮点范围和精度。这就需要顶点语言提供浮点变量的范围至少是(-2^62, 2^62),精度至少是65536。
顶点语言必须提供一种至少16位,加上一个符号位的整数精度。
片元语言提供与顶点着色器相同的浮点数范围和精度是很有必要的,但不是必须的。这就需要片元语言提供的浮点数的范围至少是(-16384,+16384),精度至少是1024。
片元语言必须提供一种至少10为,加上一个符号位的整数精度
精度修饰符
1344480599_3802.jpg1344480634_3228.jpg精度修饰符声明了底层实现存储这些变量必须要使用的最小范围和精度。实现可能会使用比要求更大的范围和精度,但绝对不会比要求少。
以下是精度修饰符要求的最低范围和精度:
Floating Point Magnitude Range是非零值量级的范围。对于Floating Point Precision,relative意思是任何度量的值的精度都是相对于这个值的。对于所有的精度级别,0必须被精确的表示出来。任何不能提供着色器存储变量所声明的精度的实现都会引起一个编译或链接错误。
对于高精度和中级精度,整型范围必须可以准确地转化成相应的相同精度修饰符所表示的float型。这样的话,highp int 可以被转换成highp float, mediump int 可以被转换成mediump float,但是lowp int 不能转换成相应的lowp float。
顶点语言要求编译和链接任何lowp, mediump和highp应用都不能出现错误。
片元语言要求编译和链接任何lowp, mediump应用都不能出现错误。但是highp支持是可选的。
字符常量和布尔型没有精度修饰符.当浮点数和整数构造器不含带有精度修饰符的参数时也不需要精度修饰符。
在这段文档中,操作包含运算符,内建函数和构造器,操作数包含函数参数和构造器参数。
对于精度没有定义的常量表达式或子表达式,评估的精度结果是所有操作数中的最高精度(mediump或者highp) 。带评估的常量表达式必须是固定不变的,并且在编译期进行.
另外,对于没有精度修饰符的操作数,精度将来自于其他操作数。如果所有的操作数都没有精度,那么接着看使用计算结果的其他表达式。这个操作是递归的,直到找到一个有精度的操作符为止。如果必要,这个操作也包含赋值运算的左值,初始化声明的变量,函数形参,函数返回值.如果这样依然不能决定精度,如果组成表达式的所有操作数都没有精度,如果结果没有被赋值,也没有当作参数传进函数,那么将使用默认或更大的类型.当这种情况出现在片元着色器中,默认的精度必须被定义.
比如:
uniform highp float h1;
highp float h2 = 2.34.7;操作和结果都是高精度
mediump float m;
m = 3.7h1*h2;//所有操作都是高精度
h2 = m * h1;//操作是高精度
m = h2 - h1;//操作是高精度
h2 = m + m;//加法和结果都是mediump精度
void f(highp p);
f(3.3);//3.3将作为高精度值传入函数
默认精度修饰符
precision precision-qualifier type;
precision可以用来确定默认精度修饰符。type可以是int或float或采样器类型,precision-qualifier可以是lowp, mediump, 或者highp。任何其他类型和修饰符都会引起错误。如果type是float类型,那么该精度(precision-qualifier)将适用于所有无精度修饰符的浮点数声明(标量,向量,矩阵)。如果type是int类型,那么该精度(precision-qualifier)将适用于所有无精度修饰符的整型数声明(标量,向量)。包括全局变量声明,函数返回值声明,函数参数声明,和本地变量声明等。没有声明精度修饰符的变量将使用和它最近的precision语句中的精度。
在顶点语言中有如下预定义的全局默认精度语句:
precision highp float;
precision highp int;
precision lowp sampler2D;
precision lowp samplerCube;
在片元语言中有如下预定义的全局默认精度语句:
precision mediump int;
precision lowp sampler2D;
precision lowp samplerCube;
片元语言没有默认的浮点数精度修饰符。因此,对于浮点数,浮点数向量和矩阵变量声明,要么声明必须包含一个精度修饰符,要不默认的精度修饰符在之前已经被声明过了