在OpenGL中,绘制一个长方体,需要将每个顶点的坐标放在一个数组中。保存坐标时有一些技巧(由于字母下标不好表示,因此将下标表示为单引号,如A1将在后文中表示为A' ):
(1)将对立面坐标保存在相邻的位置,如坐标的保存顺序为:前面(A'ABB'),后面(D'DCC'),上面(D'A'B'C'),下面(DABC),左面(D'A'AD),右面(C'B'BC)。因为对立面的坐标除了其垂直的那根轴的坐标相反以外,其他坐标值一样:如前面和后面(垂直于z轴),x和y的坐标一模一样,z轴的坐标值刚好相反。
//前面
-1.0, 1.0, 1.0,
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
//后面
-1.0, 1.0, -1.0,
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
(2)对立面按相同的顶点和顺序保存坐标。如前面(A'ABB')和后面(D'DCC'),由于是对立面,所以前面由A‘开始逆时针保存坐标,而对立面后面则从D’开始按逆时针保存坐标。这样就可以按照第(1)条的方式保存坐标数据了。
(3)保存坐标时,没有固定的顺序,如前面,可以从A‘开始保存,也可以从A, B, B'开始保存。
如,将上图的一张图片贴到长方体的一个面上(以正面为例)。一张图片在OpenGL ES中表示的时候,长度和宽度都是1,表示上图的坐标为(0,1)(0,0)(1,0)(1,1)如果只想截取图片的一部分,可以自己设置坐标值,如,我要截取左下角的1/4图片,那么坐标就是(0,0.5)(0,0)(0.5,0)(0.5,0.5)。特别需要注意的是,贴图的开始坐标,大小和方向都会影响贴图的实际效果:
(1)(0,1)(0,0)(1,0)(1,1)和(0,0)(1,0)(1,1)(0,1)都表示了这张图片,但是在贴图时的实际方向是不一样的(旋转角度不同)
(2)(0,1)(0,0)(1,0)(1,1)贴到长方体上,实际上刚好和原图的方向相反(即原图的上边将成为贴图后的下边),要正确显示这张图片的坐标应该是
(1,0)(1,1)(0,1)(0,0)(逆时针)
(3)表示图片的宽度和高度都是1
(4)要想表示原图的一部分,可以修改对应的坐标值
(5)如果贴图的格式为gif动画,那么只会显示第一帧图片
(6)贴图的长宽应为2的n次方,如2,4,8,16,64,128……否则,图片将不能显示出来。
OpenGL ES之glEnable和glDisable函数
glEnable/glDisable —— 开启和关闭服务器端GL功能
函数原型:
void glEnable(GLenum cap);
void glDisable(GLenum cap);
函数参数:
cap 指明一个GL功能的标识符。
描述:
glEnable/glDisable可以用来开启和关闭各种功能。使用glIsEnabled或glGet 来获取当前设置的GL功能。GL_DITHER和GL_MULTISAMPLE的初始值为GL_TURE,其他功能的初始值都是GL_FALSE。
glEnable/glDisable都只接受一个cap参数,cap 的取值可以是:
GL_ALPHA_TEST
alpha测试功能,参考glAlphaFunc函数。
GL_BLEND
混合功能,将片元颜色和颜色缓冲区的颜色进行混合,参考glBlendFunc。
GL_COLOR_LOGIC_OP
将当前的逻辑操作应用于经过计算的片元颜色和颜色缓冲区值,参考glLogicOp。
GL_CLIP_PLANEi
剪切用户定义的剪裁面i,参考glClipPlane。
GL_COLOR_MATERIAL
使用环境和漫反射材质来跟踪当前颜色。
GL_CULL_FACE
根据在窗口坐标中的弯曲来剔除多边形。
GL_DEPTH_TEST
进行深度比较和更新深度缓冲。注意,即使深度缓冲区存在并且表示深度缓冲区的掩码也不为0,但是如果深度测试被禁止的话,也是不会更新深度缓冲区的。参考glDepthFunc和glDepthRange。
GL_DITHER
在写入颜色缓冲区之前抖动颜色组件。
GL_FOG
将一个雾(fog)颜色混合进一个贴图后的颜色,参考glFog。
GL_LIGHTi
使用光i进行光照方程式计算,参考 glLightModel和 glLight。
GL_LIGHTING
使用当前光照计算顶点颜色。否则只是将当前的颜色和顶点简单的关联在一起,参考glMaterial, glLightModel, 和glLight.
GL_LINE_SMOOTH
使用正确的过滤来绘制线,参考glLineWidth.
GL_MULTISAMPLE
使用多个片元采样来计算最终的像素颜色,参考glSampleCoverage。
GL_NORMALIZE
在转换之后和光照之前将法线向量标准化成单位长度。通常这个功能效率要比GL_RESCALE_NORMAL低。参考glNormal和 glNormalPointer.
GL_POINT_SMOOTH
使用合适的过滤器来绘制点,参考glPointSize.
GL_POINT_SPRITE_OES
激活点精灵。参考glPointSize和 glTexEnv。
GL_POLYGON_OFFSET_FILL
在深度对比之前,给多边形片元的深度值加上一个偏移量。参考glPolygonOffset。
GL_RESCALE_NORMAL
在转换之后和光照之前,通过一个由模型矩阵计算出来的因子来对法线向量进行缩放。如果模型矩阵缩放空间保持一致,那么和存储转换后的法线为单位长度的效果是一样的。这个功能通常要比GL_NORMALIZE的效率高。参考glNormal和glNormalPointer。
GL_SAMPLE_ALPHA_TO_COVERAGE
计算一个临时的覆盖值,该值的每一位都由相应的采样位置的alpha值决定。临时覆盖值和片元覆盖值进行与操作。
GL_SAMPLE_ALPHA_TO_ONE
使用最大的可展现的alpha值来代替每一个采样alpha值。
GL_SAMPLE_COVERAGE
片元覆盖值和临时覆盖值进行与操作。如果GL_SAMPLE_COVERAGE_INVERT的值被设置为 GL_TRUE,那么将会反转覆盖值。参考glSampleCoverage。
GL_SCISSOR_TEST
丢弃在剪切区域外的片元,参考glScissor。
GL_STENCIL_TEST
进行模板测试和模板缓冲区更新。参考glStencilFunc,glStencilMask,glStencilOp。
GL_TEXTURE_2D
对活动的材质单元进行二维贴图。参考glActiveTexture, glTexImage2D,glCompressedTexImage2D, glCopyTexImage2D.
注意:
GL_CLIP_PLANEi 和GL_POINT_SPRITE_OES只在GL 1.1和更高版本中才被支持。
OpenGL ES几何变换和坐标变换
OpenGL ES支持两种变换方式:
geometric transformation 几何变换,即当对象发生平移、缩放、旋转等变换操作时,相对于原来的坐标点进行变换。
coordinate transformation 坐标变换,即当对象发生平移、缩放、旋转等变换操作时,对象本身不动,而坐标系统进行变换。
glCullFace —— 指明前面或后面的多边形是否要剔除
函数原型:
void glCullFace(GLenum mode);
函数参数:
mode 指明前面或后面的多边形是否要剔除(就是不显示)。可以使用的标识符有GL_FRONT,GL_BACK, 和GL_FRONT_AND_BACK。初始值是GL_BACK。
描述:
指明当剔除功能启用时,前面或后面的多边形是否要剔除(由mode 指定的那一面)。使用带GL_CULL_FACE参数的glEnable 和 glDisable函数来启用和关闭剔除功能。剔除功能默认是关闭的。
注意:
如果mode的值为GL_FRONT_AND_BACK,那么多边形将不会被绘制到屏幕上,但其他图元如点和线还是会绘制到屏幕上的。
错误:
GL_INVALID_ENUM 如果传入mode参数的值是不可接受的。
glDrawElements —— 渲染数组数据中的图元
函数原型:
void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid * indices);
函数参数:
mode 指定要渲染的图元类型,可选的值有 GL_POINTS,GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES,
GL_TRIANGLE_STRIP,GL_TRIANGLE_FAN,GL_TRIANGLES。
count 指定要渲染的元素个数。
type 指定indices数组中的元素类型。必须是GL_UNSIGNED_BYTE或者GL_UNSIGNED_SHORT。
indices 指向保存索引的数组。
描述:
glDrawElements可以通过非常少的子程序调用来指定多个几何图元。
注意:
如果mode的值为GL_FRONT_AND_BACK,那么多边形将不会被绘制到屏幕上,但其他图元如点和线还是会绘制到屏幕上的。
错误:
GL_INVALID_ENUM 如果传入mode参数的值是不可接受的。
-(void)setClipping { float aspectRatio; const float zNear = 0.1; //1 指定了近裁面的距离 const float zFar = 1000; //2 指定了远裁面的距离 const float fieldOfView = 60.0; //3 设定视角为60度。 GLfloat size; CGRect frame = [[UIScreen mainScreen] bounds]; //4 获取屏幕的尺寸大小 aspectRatio=(float)frame.size.width/(float)frame.size.height; //5 根据屏幕的尺寸计算最终屏幕的纵横比例 //Set the OpenGL projection matrix. glMatrixMode(GL_PROJECTION); //6 将当前矩阵从模型视图矩阵设置成投影矩阵 glLoadIdentity(); size = zNear * tanf(GLKMathDegreesToRadians (fieldOfView) / 2.0); //7 计算锥形视角的左右上下的限制值 glFrustumf(-size, size, -size /aspectRatio, size /aspectRatio, zNear, zFar); //8 计算的左右上下以及近剪裁面和远剪裁面的值传进glFrustumf函数 glViewport(0, 0, frame.size.width, frame.size.height); //9 设置视口,一般为屏幕的大小 //Make the OpenGL ModelView matrix the default. glMatrixMode(GL_MODELVIEW); //10 将当前的矩阵从投影矩阵设置为模型视图矩阵 }
glFrustumf中文API文档参考OpenGL ES之glFrustum函数
第1、2行指定了近裁面和远裁面的距离。这两个值的意思是,任何远于1000或近于0.1的对象都将被过滤掉。你可能会问一千什么?就是一千!单位看你自己设想。你可以把它想象成光年,或者英尺,都无所谓。不信的话你自己随便设置一下试试。
第3行设定视角为60度。
第4行获取屏幕的尺寸大小。
第5行根据屏幕的尺寸计算最终屏幕的纵横比例。它的高度和宽度的值决定了相对高度的视域(FOV),如果将其翻转的话,将变成相对于宽度的视域。如果我们要设置一个60度视域,就像一个宽角度镜头,那么它将基于窗口的高度而非宽度。当渲染到一个非正方形的屏幕上时尤为重要。
由于glfrustumf影响的是投影矩阵,所以我们需要确认将当前矩阵从模型视图矩阵设置成投影矩阵。第6行就是要做这个滴。
第7行计算锥形视角的左右上下的限制值。你可以把它想象成3D空间中的虚拟窗口。原点在屏幕中央,所以x轴和y轴的值都是从-size到+size。这就是为什么会有GLKMathDegreesToRadians (fieldOfView) / 2.0将窗口分为两部分——视角的角度是从-30度到+30度的。乘以zNear就可以计算出近剪裁面在坐标轴各个方向上的大小。这就是正切函数的作用了,眼睛在z轴上,到原点的距离是zNear,视域被z轴分为上下两部分各为30度,所以就可以明白size就是近剪裁面在x和y轴上的长度。
第8行将计算的左右上下以及近剪裁面和远剪裁面的值传进glFrustumf函数。这里下边和上边的值都除以了aspectRatio(屏幕宽高比),而左右边没有,这是因为调用glLoadIdentity函数标准化投影矩阵的时候将所有的顶点数据都标准化到了-1~1的范围内,屏幕宽度和高度实际大小不一样,但都被标准化成了1。所以如果左右值和上下值一样的话得到的就是一个宽度比较大而高度比较小的长方形,而不是预期的正方形,所以左右值不变,而上下值要除以宽高比。
第9行用来设置视口,一般为屏幕的大小。不过你可以根据需要来设置坐标和宽度、高度。
第10行为将当前的矩阵从投影矩阵设置为模型视图矩阵。
由于设置投影视角的方法基本上是固定的,只不过需要调整一下近剪裁面和远剪裁面以及视角的大小,所以可以将setClipping函数进一步封装为:
-(void)setClippingWithNear:(const float)zNear far:(const float)zFar angle:(const float) fieldOfView { float aspectRatio; GLfloat size; CGRect frame = [[UIScreen mainScreen] bounds]; //4 aspectRatio=(float)frame.size.width/(float)frame.size.height; //5 //Set the OpenGL projection matrix. glMatrixMode(GL_PROJECTION); //6 glLoadIdentity(); size = zNear * tanf(GLKMathDegreesToRadians (fieldOfView) / 2.0); //7 glFrustumf(-size, size, -size /aspectRatio, size /aspectRatio, zNear, zFar); //8 glViewport(0, 0, frame.size.width, frame.size.height); //9 //Make the OpenGL ModelView matrix the default. glMatrixMode(GL_MODELVIEW); //10 }
OpenGL ES之glLight函数
glLight —— 设置光源参数
函数原型:
void glLightf(GLenum light, GLenum pname, GLfloat param);
void glLightx(GLenum light, GLenum pname, GLfixed param);
函数参数:
light 指定一个光。光的数量要看具体的实现,但是至少要支持8个。标识符的形式如GL_LIGHTi,其中i大于等于0,小于GL_MAX_LIGHT。
pname 为light指定一个单值光源参数。可取的值有
GL_SPOT_EXPONENT,
GL_SPOT_CUTOFF,
GL_CONSTANT_ATTENUATION,
GL_LINEAR_ATTENUATION, 和
GL_QUADRATIC_ATTENUATIONparam 指定光源light的pname参数的值
函数原型:
void glLightfv(GLenum light, GLenum pname, const GLfloat * params);void glLightxv(GLenum light, GLenum pname, const GLfixed * params);
函数参数:
light 指定一个光。光的数量要看具体的实现,但是至少要支持8个。标识符的形式如GL_LIGHTi,其中i大于等于0,小于GL_MAX_LIGHT。
pname 为light指定一个光源参数。可取的值有
GL_AMBIENT,
GL_DIFFUSE,
GL_SPECULAR,
GL_POSITION,
GL_SPOT_CUTOFF,
GL_SPOT_DIRECTION,
GL_SPOT_EXPONENT,
GL_CONSTANT_ATTENUATION,
GL_LINEAR_ATTENUATION, and
GL_QUADRATIC_ATTENUATION
param 指定光源light的pname参数的值或指向一组值的数组指针
描述:
glLight用来设置独立光源参数的值。light用来命名光,并且是形式如GL_LIGHTi的标识符,其中i大于等于0,小于GL_MAX_LIGHT。pname用来指定十种光源的某一种,光源也是使用标示符来表示的。params要么是一个单值要么是一个指向包含新值的数组指针。
调用带GL_LIGHTING参数的glEnable和glDisable函数来开启和关闭光照计算。初始时,光照是关闭的。光源i调用带GL_LIGHTi参数的glEnable和glDisable函数来开启和关闭。
十个光源参数如下:
GL_AMBIENT
params包含四个定点或浮点数值来指定环境RGBA光强度。初始环境光强度为(0,0,0,1)。
GL_DIFFUSE
params包含四个定点或浮点数值来指定漫反射RGBA光强度。GL_LIGHT0的初始值为(1,1,1,1),对于其它的光,初始值都是(0,0,0,0)。
GL_SPECULAR
params包含四个定点或浮点数值来指定镜面反射RGBA光强度。GL_LIGHT0的初始值为(1,1,1,1),对于其它的光,初始值都是(0,0,0,0)。
GL_POSITION
params包含四个定点或浮点数值来指定在奇次对象坐标系统中的光的位置。当glLight被调用时,由模型视图矩阵对点进行转换,并且存储在眼睛坐标系统(eye coordinates)中。如果位置的w组件的值为0,那么该光将被认为是定向光源。漫反射和镜面光照要使用到光的方向,但考虑到衰减被关闭,因此并不是它的真正的位置。否则,漫反射和镜面光照计算将基于眼睛坐标系统中的光的真实位置,此时要开启衰减。初始位置为(0,0,1,0),所以初始时光源是定向的,并且平行于z轴,在-z轴方向。
GL_SPOT_EXPONENT
params是一个指定光照强度的单定点或浮点数值。取值范围是[0, 128]。有效的光照强度是通过光照方向和光到被光照的顶点方向的夹角的cos值进行衰减得到的,直到点光指数的值。因此点光指数越大,聚光效果就越好,此处不考虑点光的切角问题。初始点光指数为0,得到的是一个均匀的光照分布。
GL_SPOT_DIRECTION
params包含三个定点或浮点数值来指定在奇次对象坐标系统中的光照方向。当glLight被调用时,点光的方向由上3*3模型视图矩阵来转换,并存储在眼睛坐标系统中。它只在GL_SPOT_CUTOFF不是180时才是有效的,这是初始设定的。初始方向是(0,0,-1)。
GL_SPOT_CUTOFF
params是一个单定点或浮点数值来指定一个光源的最大扩展角度。取值范围是[0, 90]和特定值180.如果光线方向和光到被照射顶点的方向之间的夹角比点光的切角大,那么,光照将被完全掩盖。否则,它的强度由点光指数和衰减因子控制。初始点光切角是180,得到的是一个均匀光照分布。
GL_CONSTANT_ATTENUATION,GL_LINEAR_ATTENUATION,GL_QUADRATIC_ATTENUATION
params是一个单定点或浮点数值来指定三个光照衰减因子之一。取值范围是非负数。如果光是可定位的而不是定向的,它的强度由常数因子总和的倒数来衰减,线性因子与光和被照射顶点之间的距离有关,平方因子与该距离的平方有关。初始衰减因子为(1,0,0),即没有衰减。
注意:
GL_LIGHTi = GL_LIGHT0 + i 。
错误:
GL_INVALID_ENUM 如果传入light或pname参数的值是不可接受的。
glNormalPointer —— 定义一个法线数组
函数原型:
void glNormalPointer(GLenum type, GLsizei stride, const GLvoid * pointer);
函数参数:
type 指明数组中每个坐标的数据类型,可选的标识符有
GL_BYTE,
GL_SHORT,和
GL_FIXED 。默认值是GL_FLOAT。
stride 指定连续法线间的字节偏移。如果取值为0,说明数组中法线是连续不间断保存的。初始值为0。
pointer 一个指向数组中第一个法线的第一个坐标的指针。
描述:
glNormalPointer指明渲染时使用到的法线数组的数据位置。type指明法线坐标的数据类型。stride指明两个相邻的法线数据间的字节偏移,这将允许顶点数据和顶点属性数据保存在同一个数组中或不同的数组中。在某些实现中,单个数组保存的效率会更高。
注意:
glNormalPointer 通常在客户端实现。
错误:
GL_INVALID_ENUM 如果传入type参数的值是不可接受的。
GL_INVALID_VALUE 如果stride的值为负数。
OpenGL ES之glNormal函数
glNormal —— 设置当前法线数组
函数原型:
void glNormal3f(GLfloat nx, GLfloat ny, GLfloat nz);
void glNormal3x(GLfixed nx, GLfixed ny, GLfixed nz);
函数参数:
nx, ny, nz 指定新的当前法线的x, y, z坐标。初始值为(0,0,1)。
描述:
glNormal指定的法线不需要为单位长度。如果开启了GL_NORMALIZE,由glNormal指定的任何长度的法线都将在转换后被标准化。如果开启了 GL_RESCALE_NORMAL,法线将会通过派生自模型视图矩阵的缩放因子进行缩放。GL_RESCALE_NORMAL需要原来指定的法线为单位长度。调用带GL_NORMALIZE或GL_RESCALE_NORMAL参数的glEnable和glDisable函数来开启和关闭标准化,默认标准化是被关闭的。
OpenGL ES之glLightModel函数
glLightModel —— 设置光照模型参数
函数原型:
void glLightModelf(GLenum pname, GLfloat param);
void glLightModelx(GLenum pname, GLfixed param);
函数参数:
pname 指定一个单值光照模型参数。必须是GL_LIGHT_MODEL_TWO_SIDE。
param 指定要设置的参数值
函数原型:
void glLightModelfv(GLenum pname, const GLfloat * params);void glLightModelxv(GLenum pname, const GLfixed * params);
函数参数:
pname 指定一个单值光照模型参数。必须是GL_LIGHT_MODEL_TWO_SIDE或者GL_LIGHT_MODEL_AMBIENT。
params 指定要设置的参数指针。
描述:
GL_LIGHT_MODEL_AMBIENT
params包含四个定点或浮点值来指定整个场景的环境光强度。初始值是(0.2,0.2,0.2,1.0)。
GL_LIGHT_MODEL_TWO_SIDE
params是一个单定点或浮点数来指定多边形要不要进行一边或两边的光照计算。它对点和线的光照计算没有任何作用。如果params的值是0,一边的光照被指定,前面和后面的多边形将被赋予相同的计算颜色。如果params的值不是0,那么两边的光照都被指定。在这种情况下,在光照方程式使用到背面多边形的顶点之前,背面多边形的顶点法线将被反转。前面多边形顶点将一直被光照,并且法线也不会改变。初始值是0。注意,可以被前面和后面多边形共享的材质属性只有一种。顶点的光照颜色是材料发射强度,材料环境反射系数和光照模型全景环境光强度的乘积,每个开启的光源的分布的颜色总和。每个光源都包含三个重要特性的总和:环境光,漫反射,和镜面反射。环境光源分布是材料环境光反射系数和光的环境强度的乘积。
OpenGL ES之glFrontFace函数
glFrontFace —— 定义前面和背面多边形
函数原型:
void glFrontFace(GLenum mode);
函数参数:
mode 指明前面多边形的方向。可选的值有GL_CW和GL_CCW。默认值是GL_CCW。
描述:
在一个全部由不透明封闭表面组成的场景中,背面多边形是永远看不见的。剔除这些不可见的多边形对于加速图形的渲染有很大的益处。开启和关闭剔除功能可以调用带GL_CULL_FACE参数的glEnable和glDisable函数。默认剔除功能是关闭的。如果一个虚构的对象的顶点是按照多边形内部顺时针的方向进行绘制的,那么可以称这个多边形基于窗口坐标的投影是顺时针的。反之,则为逆时针。
glFrontFace就是用来指定多边形在窗口坐标中的方向是逆时针还是顺时针的。GL_CCW说明逆时针多边形为正面,而GL_CW说明顺时针多边形为正面。默认是逆时针多边形为正面
错误:
GL_INVALID_ENUM 如果传入mode参数的值是不可接受的。