第一章的内容可以看作是整本书的一个概括,其中涉及到一些个零零散散的基本概念我先不做过多的赘述,后续的介绍会给大家渗透基本知识,我是按照一个新手到高手的阶梯进行介绍的,大家不要着急,所以第一章我们选择略过。
大家想学习opengl的目的我想都是对图形学感兴趣,OpenGL就是用来做艺术家做的事情,我们在电脑上画画,我们的笔就是OpenGL。
大家看一下这个标题,提到了一个词状态有点难懂,我们把opengl看作一台机器,也可以看成是我们小时候买的那种一根笔可以换好几种颜色的那种笔,我们给OpenGL设置一个状态,他就开始接下来在这种状态的运行,就像我们换了个红色,这只特殊的笔在我们接下来的使用中,就会是红色。
OpenGL有很多的状态,我们首先知道要如何使用以及如何管理,同时本章最有意思的,就是教我们如何绘制几何物体。
书中正式开始介绍内容之前,有一段引言是非常重要的,说:所有我们在计算机中绘制的复杂图形,都是由最简单的图形组成的。大家应该知道,我们的计算机的能力没有那么大,厂商生产这个有关图形的硬件的时候,他为了简单,就让我们的硬件设备具有绘制基本图形的能力,基本图形指的是:矩形 ,三角形(应用最多)…反正都是一些基本图形,还有一个茶壶是例外,我们绘制的所有图形,都是这些图形组成的,就像线是由点组成的一样。
这里讲的跟绘制几何图形没有半毛钱关系,但是,你画图必须用到这些,首先,我们得建立一个窗口什么的来展示图形,然后还得准备好给图形上的色,最后,我们强制图形在屏幕上画出来。
我们在内存中存放一个图片时,这个内存一般情况下是被计算机之前绘制的图形所占用的,这就意味着我们的显示屏幕就不是纯白色啦,反正不是你心里应该有的那种默认颜色,所以我们要首先清除窗口。书中提到了一种不知道谁想出来的问题,我是没想到,说是要用一个矩形来填充,反正这种方法不好嘻嘻,感兴趣的可以了解一下。
颜色在硬件里的存储地方叫位平面,有两种存储方法:RGBA和索引值存储法(这种方法不常见,可以不学,而且应该是面临被该版本OpenGL抛弃的征兆)。RGBA分别代表了红色,绿色,蓝色,还有一个alpha值,别问我这个alpha是啥,我刚看完第二章,我也不知道哈哈。
p21页给出了一个例子(所以我说看博客,必须有书)。
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT);
例子就是这两行!短的代码我会手打,长的自己看书!
第一行就是三种颜色都没有,那不是就是漆黑一片!所以设置的我们的默认清除颜色为黑色,每当我们调用glclear,嘿嘿,你调用也得看看glClear是咋写的啊!这里glClear的参数是这个东西,代表了我们清除的是颜色缓冲区的东西,那么在这两行代码生效以后,缓冲区的颜色被清除了,屏幕上的图像还是原样。你在程序的开头设置好了清除颜色以后,OpenGL会把这个颜色当作一个状态变量,也就是笔的一种颜色,他在后面的代码中都有效。
然后一个重点:三种颜色的取值范围是0.0-1.0
然后啊,glClear()这个函数有一个参数,就是我们要清除的缓冲区的名字(有关缓冲区,大家不要较真,一会会讲到)。缓冲区名字如下:
GL_COLOR_BUFFER_BIT 颜色缓冲区
GL_DEPTH_BUFFER_BIT 深度缓冲区
GL_ACCUM_BUFFER_BIT 累计缓冲区
GL_STENCIL_BUFFER_BIT 模板缓冲区
还有个重点,比如说我们想清除颜色缓冲区和深度缓冲度,那么我们可以利用逻辑运算符“|”,你用这个运算符比你一个个的调用glClear要快!记住!要快!
我们的指定颜色的函数:glColor3f(),在这里3代表函数里的参数有三个,f代表了参数的数据类型为浮点数,三个的原因是因为alpha值可以是默认的,默认为0.0,而且我们也用不到,那就干脆是默认的吧,如果用到的话,我们也可以写glColor4f()。
接下来说一个颜色操作序列的问题,比如说我先设置了一个颜色,之后我画图了,那么我画的图就都是这个颜色,之后我又设置了另外一种颜色,那么我接下来画的图又都是第二种颜色(大家好好理解状态机的问题吧)。
这里涉及两个函数 glFlush()和glFinish()我看书上的意思,我们用glFlush就可以,而且过多的使用glFinish会降低系统性能。
说说为啥我们要强制绘图完成:
1.我想让程序走的快点(最普通的理解)
2.cpu傻,本来我们的这一个程序有很多硬件合作完成,cpu非得死心眼等着她发的这个命令结束了才执行下一条,如果不等结束,直接进入下一条,那么硬件都会运作起来,这样大家一起干活,效率多高。
3.这里涉及到一个概念:客户机和服务器,我们现在用的叫客户机,那服务器在哪啊,服务器就是我们的图形处理硬件。我们绘图必须得需要这些硬件的帮忙才可以。假如说 这个服务器,这些硬件不是你电脑上的,是我电脑上的,那你就得通过网络给我发过来,然后我处理,你发指令,有个问题就是你是通过数据包给我发的,你这个数据包不满,你就总不想发,怕费流量,那就得等,那多么的慢,所以我们利用glFlush让我们的程序比普通情况下跑得更快,就是因为它克服了上面两个瓶颈。
如果我们有个窗口,那我们不管是移动窗口,还是重置它的大小。glut都会发个信号,我们可以在这个时候写个函数,每当你动窗口,这个函数就会触发,glutReshapeFunc(),只有一个参数,是函数名。
点:在OpenGL中,点是用三维坐标来实现的,都是浮点数,但是由于浮点数的精度问题,两个不同的点,可能是同一个点,而且,在绘制图像的时候,最小的单位是像素,两个不同的点,可能在同一个像素里。(四维坐标不常见,大家有兴趣的看看)
直线:直线是线段!而且直线是用顶点来指定的。
多边形:多边形是直线闭合组成的,绘制多边形,可以选择填充内部,只画边界,或者用点来画。多边形必须是凸多边形,为了方便多边形的渲染,我们一般选择用三角形来组成这些多边形来进行渲染,因为三角形总是位于一个平面内。
矩形:glRect{sifd}()可以选择一个大括号里面的值,参数为四个值,值的类型和大括号四个字母对应的数值类型相同,前两个为左上点的坐标,后两个为右下点的坐标。也可以是glRect{sifd}v(),v表示括号里的参数需要时指针,有两个参数,第一个参数指向坐上点这对值的指针,第二个是右下。
glVertex[234]{sifd}()
glVertex[234]{sifd}v() 中括号里的数字表示参数的个数
这两个函数都可以指定顶点,意思就是在坐标系的给定位置画一个点。
我们会画一个点了,那么怎么画一组点啊,或者怎么把点连成线,或者画一个几何图形呢
把你想表达的,描述的怎么画的过程都要放到glBegin()和glEnd()之间
glBegin有一个参数,可以指定我们在接下来怎么画,如果参数是GL_POLYGON那么你下面描述完几个点之后,这些点就会连成一个多边形。
除了这个参数,还有:
GL_POINTS 只画点
GL_LINES 一对点被解释成一条线
GL_LINE_STRIP 所有的点都被连成线
GL_LINE_LOOP 最后起始点和结束点还会相连
GL_TRIANGLES 三个点被解释成一个三角形
GL_TRIANGLE_STRIP 三角形的连接串
GL_TRIANGLE_FAN 连接成扇形的三角形序列
GL_QUADS 四个顶点被解释成一个四边形
GL_QUAD_STRIP四边形的连接串
glEnd()标志着一个顶点数据列表的结束
glBegin()和glEnd()之间的函数是有限制的 详情看书p30 表2-3
除了这些函数,不能有别的函数。不能有别的函数指的是OpenGL的函数,c语言的还是可以有的哈哈。
对于glVertex*()即对于顶点的函数 都必须在这个begin和end结构里面,在外面不起任何作用哦,除非在显示列表(先不用管)。而且就算在显示列表里,列表不在这个结构中,那也是没用。
我之前说 颜色对于opengl来说是个状态变量,在OpenGL中有很多状态变量和状态,我们需要用glEnable()和glDisable()来开启和关闭状态,我们还可以用glIsEnable来看某个状态是否打开了。状态有很多,我先不一一列举。
还有一些返回状态的函数,用于查看这个状态的状态变量,这里也不一一叙述。
默认情况下:点被画成屏幕的一个像素,直线宽一个像素,多边形为内部填充。
glPointSize()一个参数,为浮点型。
如果像素集合开启了抗锯齿功能,那么点被画成圆形,外围颜色较浅,当我们用GL_SMOOTH_POINT_SIZE_GRANULARITY为参数调用glGetFloatv()函数时,将返回opengl支持的抗锯齿点的精度,如果为0.01,那么2.373的点就四舍五入为2.37,如果没有抗锯齿,那么就四舍五入到整数。
glLineWidth()一个浮点数参数,opengl3.1版本不支持大于1.0的数 设置直线宽度。
点画线:想要做到点画线,首先利用glLineStipple()函数定义点画线模式,然后利用glEnable()启用点画线功能。
glLineStipple(1,0x3F07);
glEnable(GL_LINE_STIPPLE);
glLineStipple函数的作用是 从给出的16位序列里的最低位绘制像素,第一个参数是factor,意思是要重复几次的意思,例如上面这两行代码的例子,0x3F07写作二进制是0011111100000111,如果从最后一位开始绘制的画,要先把连续的三个像素连接起来,然后五个像素留白,然后填充六个像素,最后两个像素留白。如果factor为2,则先绘制六个像素,十个像素留白。。。。。。以此类推。
OpenGL中的多边形都是有正反两面的,对于正反两面的概念,你可以把它想成是一个有厚度的多边形(但是事实是二维的),我们可以只渲染你看到的这个面,这样可以增加渲染的效率,同时,对于多边形,我们也可以选择只画轮廓,或者填充模式,或者轮廓是用点来完成:
glPolygonMode()函数有两个参数,第一个指定是正面还是反面,还可以正反两面都可以绘制,第二个参数选择渲染的模式,第一个参数的选择值为GL_FRONT,GL_BACK,GL_FRONT_AND_BACK,模式的参数为GL_POINT,GL_LINE,GL_FULL,OpenGL只支持两面同时为参数,而且两面的渲染模式相同。
我们在描述一个多边形时,如果选择的是顺时针描述方法,那么意味着这个多边形的外侧面为反面,OpenGL定义:逆时针顶点描述的多边形为正面,那么反面就是顺时针。假设我们指定的时候把外侧面制定成了顺时针,那么我们可以使用:
glFrontFace(),该函数有一个参数,可选值是:GL_CCW(逆时针为正面),GL_CW(顺时针为正面)。
前面提到过,我们可以只渲染一种面,要么正面,要么反面,不渲染的面我们可以使用剔除函数:
glCullFace()参数为GL_FRONT,GL_BACK,GL_FRONT_AND_BACK,我们在使用之前,必须开启提出功能:
glEnable(GL_CULL_FACE)。
在前面我们提到了点画线,那么一个多边形同样也可以用点阵的形式画出来,本来一个多边形就是一个像素的集合,每个像素是否被填充可以用指定的矩阵来决定:
glPolygonStipple()函数一个参数,指向一个位图,这个位图里的值被解释成0和1的掩码(简单来说就是计算机把他解释成一个01的矩阵啊什么的),0对应位置的像素不被填充,1对应的被填充,使用函数之前,我们要使用glEnable(GL_POLYGON_STIPPLE),来开启点画图功能。
标记多边形的边界边:在OpenGL中,一个复杂的多边形我们是用简单的图形构成的,例如三角形。那么当我们想几个三角形画一个复杂多边形的时候,怎么才能只画多边形的轮廓,而不画多边形内部三角形的轮廓呢,例子见p41 图2-12.
在这里我们使用函数:
glEdgeFlag()此函数有一个参数,是bool值参数,为true时,那么这个顶点就是多边形的一条边界边的起点,如果是flase那么就不是,大家可以参考示例程序2-7.在我们描述一个顶点之前,先确定我们要描述顶点是不是多边形的边界边的顶点,如果是,那么接下来描述的顶点就都是边界边的顶点,直到我们再次调用这个函数。
法线向量:在我们绘制多边形时,每个顶点都有自己的法线向量,对于平面多边形,每个顶点的法线向量相同,对于曲面的呢,就不一样,所以我们每次指定定点之前,都先用函数glNormal3{bsidf}()(三个参数的函数)或者glNormal3fv()(一个参数的函数),当我们调用这两个函数的时候,函数都会自动把参数里的法线坐标形式进行缩放,使得三个值都位于0.0-1.0之间。
书中提到了有关法线规范化的知识,在这里她只列举了我们开启发现规范化功能的语句:glEnable(GL_NORMALIZE)和glEnable(GL_RESCALE_NORMAL),前面的应用场景为:我们想让一个法线规范化,后一个应用场景为:我们本来有一个单位法线,想对他进行缩放。后一个的操作开销更少。
接下来的内容相对于前面的比较困难,希望大家认真学习。
引入顶点数组的原因,是因为这样可以简化操作,减少冗余,提高性能。
大家在之前也看到了,我想绘制一个多边形,一个顶点一个顶点的描述,不仅如此,顶点相关的数据如法线,边界边标志乱七八糟的,我想描述三个点,得用好多函数,这么麻烦,而且我们想绘制一个立方体的话,一个顶点要描述三次,这样就造成了冗余,为了解决这些问题,我们引入了顶点数组的概念。
我们使用顶点数组有三个步骤:1激活(启用顶点数组):顶点数组最多可以指定八个,每个数组里面的数据都是一致的,比如一个数组里存放的是法线向量,那么这个数组里面所有的数据就都是法线向量(后面会讲到混合数组,一个数组里面的数据可以不同)
2:把数据放入数组中:前面提到了客户机-服务器的模型,我们的数据放到数组以后,这些数组都是存在客户机的内存里的,后面会讲到缓冲区,利用缓冲区,我们就可以把数据存到服务器上。
3;用这些数组绘制图形。把数据从客户机的内存里面取出来,然后传到服务器地址空间中,有三种方式可以完成这个操作:随机存取,系统存取,线性处理数组元素。
那么我们想要什么样的数组呢,比如说我想要一个存顶点的数组,那么我就要调用函数glEnableClientState(),参数为GL_VERTEX_ARRAY,除了这个参数,参数还有:GL_COLOR_ARRAY,GL_SCEONDARY_COLOR_ARRAY,GL_INDEX_ARRAY,GL_NORMAL_ARRAY,GL_FOG_COORDINATE_ARRAY,GL_TEXTURE_COORD_ARRAY,GL_EDGE_FLAG_ARRAY.最多可以指定六个参数,因为有的数组没有必要同时出现。如果想禁用某个数组,使用函数:glDisableClientState(),在这里,大家看一下这个函数,函数名有一个单词client,说明这个数组是在客户端的,这就是为什么在这里我们不能使用glEnable()这个函数,这个函数可以存在显示列表里面,我读到这里我的理解是,显示列表是可以存在服务器端的,而数组这个东西不可以,他只能存在客户端。在opengl3.1中,就只支持数组存在缓冲区里,也就意味着,数组也在服务器端了。
上一个步骤中,我们已经指定了启用哪个数组,就好像c语言里面我们声明一个数组一样,之后我们要往数组里面添加数据(以顶点数组为例),具体做法是,利用函数glVertexPointer()
这个函数有四个参数,第一个参数是每个顶点有几个参数,就是每个顶点是几维的坐标必须是2、3、4,type是数据来源数组中每个坐标的数据类型,stride是字节偏移量,意思是每隔几个值就又是一个顶点的坐标,最后一个参数是一个指针,指向数据来源数组。关于其他的为数组天填数据的函数,他们的参数个数可能不同,由于本身这个数据的原因,比如说法线向量,他的size总是有三个成分,那么size恒为3,就没必要再写了,所以就省略了,具体参考书上的表2-4.
定点数据被提取出来之前,这些数据都存在客户端的内容里,当你提取出来以后,就发送到服务器,然后发送到图形处理管线进行渲染。
解引用单个数组元素:我们用到的函数是
glArrayElement()参数是一个int值,意思是获取当前所有已经启用的数组的第int个数据,对于顶点坐标数组,调用的这个函数会调用glVertex{size}{type}v(),因为我们填数据的时候用的glVertexPointer()以及其他glPointer()已经指定了size和type,所以必须一致,如果我们同时开启了多个数组的话,在调用完除顶点数组以外的数组的相对应的函数之后,会调用glVertexv().
详情见示例程序2-10.
解引用数组元素的一个列表:
我们使用函数glDrawElements()
这个函数有四个参数,第一个参数是模式,与glBegin()中的参数一致(用来指定你要画什么,画三角形啊什么的),第二个是我们要画的点的个数,第三个是我们用到的索引数组的元素类型,最后一个参数是索引数组的首地址,在这里,我们用到了索引数组,就相当于把我们要的点的序列放到这个数组里,就不需要我们一次次的调用dlArrayElement()了。在这里说明一下,因为这个函数已经包括了glBegin()的作用,所以不能把他再次放到begin和end的结构当中。
接下来要介绍另外一个函数glMultiDrawElements(),这个函数有五个参数,先说一下这个函数是干嘛的:他的作用就是调用很多次的glDrawElements().那么对应的参数也应该有所不同,这个函数有五个参数,第一个是模式,同上一个函数(后面叫函数1,现在介绍的叫函数2),第二个是一个数组,这个数组存了每次调用函数1要画的点的个数,type是索引数组的元素的数据类型,接下来是一个指针数组,里面的每个指针指向一个索引数组,最后一个参数是我们要调用的函数1的次数。
接下来介绍的函数有优化的作用,由于它可以告诉opengl在索引数组中哪块区域的内容是我们用到的,这样在渲染之前就把这部分数据提取出来,就可以加快我们的程序,这个函数就是glDrawRangeElements(),大家都看到那个标志性的range了吧,这个函数在函数2的基础上增加了两个参数,start和end,就来标志你提取的索引值必须在这两个值之间,同时没有函数2的最后一个参数,在使用这个函数之前,我们可以调用,glGetIntegerv(GL_MAX_ELEMENTS_VERTICES)和glGetintergerv(GL_MAX_ELEMENTS_INDICES),分别获取end-start+1的最大值,以及count的最大值,保证正确的大小关系,是保证性能的重要指标。
解引用一个数组元素序列:
接下来这个函数是要按顺序访问数组的元素,glDrawArrays()三个参数,第一个是模式,第二个是访问的第一个位置,count是访问的个数。这个可以看作是连续的glArrayElement(),这两个函数都会对参数做错误检查。
由于我们利用glMulitidraw*()函数需要维护一个数组,加之平时我们总是需要绘制一些循环相同的多边形,因此我们采用重启图元的方法,首先我们要有一个索引序列,在索引序列里有一个值被看作重启索引,每当遇到重启索引时,现在绘制的图形绘制完成,接着这个点从头绘制与这个图形相同的图形,在使用重启图元时,函数为:glPrimitiveRestartIndex()一个参数,为索引值。同时,需要使用glEnable(GL_PRIMITIVE_RESTART).
OpenGL3.1提供了另一个额外的值 glInstanceID,每一组图元一个即每一个多边形一个(方便理解),我们使用函数:
glDrawArraysInstanced()四个参数,第一个参数是模式,第二个是每次调用的首地址,第三个是调用的个数,第四个是调用了多少次的glDrawArrays().
glDrawElementsInstanecd()与上述函数类似。
如果大家跟着看书学习,那么前面有个例子就是数组里面存了颜色和坐标,叫interwind,然后我们使用glVertexPointer()和glColorPointer()这两个函数把其中的数据分别存到顶点数组里面,在这里我们介绍了混合数组,这个函数是glInterLeavedArrays()
书中对这个函数的介绍让人难懂,这个函数只支持特定的数组,有四个数组都是自动关闭的,详情见p56 实例2-16,这个函数本质上和我们调用两次gl*Pointer是一样的,不过他把我们对于顶点数组的前两部操作组合在了一起,我们使用完这个函数,可以直接进行渲染,函数的第一个参数是模式 ,书上的表2-5列举了参数的类型,我给大家解释一下pc,pn,pv,s这四个,p开头的就是对于一个顶点元素(假设源数组有三种数据,顶点,颜色,纹理坐标)假设一组数据里开头就放的是顶点,那么顶点就是0,如果顶点有三个坐标值(xyz),那么接下来的不管是颜色还是纹理坐标,就是3,以此类推,s代表了你从一个元素里的顶点值也好颜色值也好跳转到下一个所需要的跨距值。第二个参数是跨距值,这个东西分析了书上的例子,我觉得没啥大用,这个跨距值要是你自己写就是自己指定,要是写0,他会自动的给你变成上面所讲的s的值,最后一个参数是一个指针,指向源数组,后面还讲到会提高系统性能,我在这里也有点困惑。
在这里,我先解读一下这一小节的引言:
引言的内容就是,由于客户机服务器的模式,我们的数据,就是之前说的什么顶点数组啦,都是在客户机的内存里,如果想让他进行渲染,要发送到服务器,这样第一可能会慢,或者还有什么冗余之类的,反正就是数据储存在客户机是非常不便于处理的,为此,我们引入缓冲区,强制这些数据存到服务器里,在这里还提到了对象一词,有的对象是用来存储成块的信息,有的对象又是用来封装功能,方便我们绘制图像的。
在opengl中,对象的名字都是利用函数glGen*()这类函数来完成的,会返回一个标识符,用来标识一个对象,opengl3.1已经不允许你自己定义对象名字了。
我们使用函数 glGenBuffers()函数,函数有两个参数,第一个是返回的标识符的个数,第二个是一个指针,指针指向数组,数组里存有所有返回的标识符。0这个特殊的数值是不会被返回的。
我们还可以使用glIsBuffer()这个函数来判断某个标识符现在是不是处于被占用的状态,有一个参数,就是标识符,其实标识符可以是任何一个有代表的值。
其实这本红宝书难点就在与翻译,真的都是坑,他们翻译的真的有点晦涩,就是给人模棱两可的感觉。
我们使用glBindBuffer()函数来完成这个功能,第一个参数的值只能是下面的几个
GL_ARRAY_BUFFER表示定点数据
GL_ELEMENT_ARRAY_BUFFER表示索引数据
GL_PIXEL_BACK_BUFFER表示从opengl获取的像素数据
GL_PIXEL_UNBACK_BUFFER表示传递给OpenGL的像素数据
GL_COPY_READ_BUFFER
GL_COPY_WRITE_BUFFER这两个表示在缓冲区之间复制数据
GL_TRANSFORM_FEEDBACK_BUFFER表示执行一个变换反馈着色器的结果
GL_UNIFORM_BUFFER表示统一变量值
第二个参数是一个缓冲区标识符。
我觉得这个函数的功能是大家必须掌握的,我在学习这里的时候我也犯困了,现在我给大家好好解读一下:
如果你的第二个参数是一个非零的 ,且是你之前用glGenBuffers()返回的值的话,而且已经bind过的缓冲区,那么这个缓冲区就被激活,意思就是接下来你就可以对这个缓冲区进行操作了;如果是一个你没有bind的过的缓冲区标识符,且非0,来自glGenBuffers(),那么这个标识符代表的缓冲区就是具有第一个属性的缓冲区;如果第二个参数为0,那么就是禁用缓冲区对象模式。
我们有名字,有属性,就是没有数据,我们使用函数 glBufferData()
这个函数有四个参数,第一个是与上面刚介绍的函数的第一个参数相同的,第二个是我们这个缓冲区储存数据需要的内存数量,第三个是指向客户机内存的一个指针,如果第三个参数不是NULL,那么会把指针的内容按照第二个参数的大小复制到缓冲区,否则,不复制,缓冲区虽然有那么大的空间,但是是空的,最后一个参数是指示数据存到缓冲区之后如何进入读取和写入。有效值参考书p59
第一种更新的方法是替换,就是我们的应用程序缓冲区(我也不懂)里有数据,我们想用这些数据来替换,我们利用函数
glBufferSubData()
第一个参数是与上述两个参数的第一个参数相同的,第二个是要修改缓冲区中要要做替换数据的位置,值为第几个字节,第三个为我们替换的数据的大小,第四个是应用程序缓冲区的地址。
更为灵活的方法是glMapBuffer()
参数为两个,第一个与上述的第一个函数相同,第二个是对更改高缓冲区的访问权限,值只能是:GL_READ_ONLY,GL_WRITE_ONLY,GL_READ_WRITE.
返回一个指向具有第一个参数这个属性的缓冲区的指针。紧接着你可以对这个缓冲区直接进行修改,修改完成后调用glUnmapBuffer(),参数还是和上面的第一个参数相同,表示对当前缓冲区更新完成。这种方法对于一个大缓冲区更新较少的值来说,效率很低
如果需要更新很少的值,或者是小部分连续的值,可以用glMapBufferRange()参数有四个,第一个参数还是和上面相同,第二个是缓冲区要修改的起始位置,就是这个位置距离开始有多少个字节,第三个是要修改多长,第四个是对缓冲区的访问权限,这个参数的值详情见书p61。
对于函数glFlushMappedBufferRange()
这个函数的使用是和上面这个函数的最后一个参数的值有关,它用来让OpenGL服务器刷新,让修改的数据在服务器端是定义好的。
大家如果认真看书的话,我想大家应该也看到了对于glMapBufferRange()最后一个参数的值,书中特别的列举出了GL_MAP_FLUSH_EXPLICIT_BIT这个参数有什么特别的呢,当你选择用其他的参数选择更新缓冲区之后,我们都可以调用函数glUnmapBuffer(),自动对你进行的所有更新刷新一遍,当你用了这个参数,这个刷新函数就失效了,必须的使用glFlushMappedBufferRange(),而且使用这个参数的理由很简单,对于你映射的那块区域,你更新的可能是不连续的,这时候需要借助这个参数和这个特殊的刷新函数。然后书上有一处和我在网上查阅的资料不符,就是 GL_MAP_FLUSH_EXPLICIT_BIT和GL_MAP_WRITE_BIT不能同时使用,关于这点我希望读到这里有明白的人可以在评论区和我讨论一下。
在opengl3.1中,我们使用glCopyBufferSubData()
这个函数有五个参数,第一个是我们读取的缓冲区,第二个是我们目的地缓冲区(就是缓冲区的属性,例如 GL_ARRAY_BUFFER),第三个是读缓冲区的起始位置,下一个是目的地缓冲区的起始位置,最后一个参数是复制的数据块的大小。
同时读缓冲区和写缓冲区必须是同类型的。
释放资源,标识符空闲,我们用到的函数是glDeleteBuffers()
两个参数,第一个参数是清除的缓冲区的数量,第二个是一个指针,指向存缓冲区标识符的数组。
顶点数组的引入,就是为了方便我们在编写程序时,复杂的在不同的顶点数组之间进行切换,我们只需要使用一个名字,就可以调用一个顶点数组,就好像c语言中变量的名字一样。
首先调用glGenVertexArrays()
函数有两个参数,第一个是返回的顶点数组对象的名字,第二个是存名字的数组(指针)。
接下来我们使用函数glBindVertexArray()这个函数来进行绑定,只有一个参数,就是上面的函数返回的标识符,这个函数有三个用法,如果参数是之前从没用的同时也是glgen返回的名字,那么就创建一个新的对象,并分配这个名字,如果是之前glgen返回的名字而且已经bind过,那么就使得这个顶点数组的状态为活动的,如果是0,那么opengl就停止使用顶点数组对象,并返回顶点数组的默认状态。
这里我也是研究了很久很久,这里我想和大家分享一下关于缓冲区还有顶点数组对象的一些看法,如果不对,希望大家指正:我们如果想利用顶点数组对象,首先要有顶点数组对吧,而且高版本的OpenGL要求顶点数组要存放在缓冲区里,那么我们就可以看到,我们需要三个要素,顶点数组对象,顶点数组,还有缓冲区,我看大部分的例子都是先glgen一个顶点数组对象名,然后紧接着就是对这个对象的描述了,先glgen一些缓冲区,这都属于这个顶点数组,然后,对缓冲区bind之后填充数据,然后在缓冲区的数据里面glpointer一些数据出来当作顶点数组,最后启用顶点数组,对于每一个缓冲区,都要有这个流程,那么这个做完以后,这个顶点数组对象就构造好了,之后调用的时候,只要再次bind一次顶点数组对象标识符就好了,然后渲染的时候,他会一次性的让这个对象下面所有的顶点数组们都处于活跃状态,(在这里我有一个问题,如果说数组里的数据存在于缓冲区之中,那么我更改缓冲区的数据,会对顶点数组有影响吗)
其实有没有影响无所谓,因为渲染的时候我们调用的是缓冲区里的数据
但是我觉得还是会影响。之前的话应该是说错了,因为渲染的函数都是对于顶点数组的。
删除顶点数组对象要利用函数glDeleteVertexArrays()第一个参数要删除的数量,第二个是存对象标识符的数组
删除顶点数组对象并不能删除他们绑定的缓冲区对象!
利用函数glIsVertexArray()来判断某个标识符是否已经分配 ,参数为对象标识符。
OpenGL把相关的状态归组,比如说五个有联系的状态,我们就可以把他们归为一组,在OpenGL中,有两个属性堆栈,一个是服务器的,服务器呢有大约20个不同的属性组,客户端就两个,我们如果把属性组放到堆栈里面,访问会快,因为有的状态是由硬件维护的。
相关函数,大家看书即可,比较简单。客户端的比服务器端的多了一个Client