opengles顶点属性顶点数组缓存对象

名词解释:
 
 顶点属性就是顶点shader里面声明的一个attribute变量,有两种类型的attribute变量,一种是const属性,一种是顶点属性数组

 在顶点shader中如何声明一个顶点属性:
 
 attribute vec4 a_position;
 attribute vec4 a_color;

 顶点属性的类型只能为:float vec2 vec3 vec4 mat2 mat3 mat4
 不可以为结构体,或者是这些类型的数组都不可以。
 opengl es 2.0支持GL_MAX_VERTEX_ATTRIBS个vec4类型的顶点属性,
 不够vec4的也会被看做vec4
 内部以32为单精度浮点数存储。属性不会被get packed

 顶点属性在顶点shader里面是只读的,不能写入。

 将顶点shader,片段shader都编译好,并且添加到一个program

      里面之后,就可以将顶点属性 绑定到属性位置0,1,2,3,4,5,6,7中的一个上,

 之后这个属性就有地址了,给每个program里面的

      每个属性都绑定一个位置,然后就可以连接program了,

 也可以在linkprogram之后再获取顶点属性的location。

     怎么为顶点属性上传数据呢,要分情况,如果是顶点属性数组:
      调用函数glVertexAttribPointer ();这个函数里面指定顶点属性的位置,和数据指针。然后
 glEnableVertexAttribArray ( 顶点属性的location );

 各种顶点属性数组都上传并且enable之后,就可以调用glDrawElements函数进行绘制了。
 
 如果是const 顶点属性,则使用函数:
  glVertexAttribnf(GLuint index,GLfloat x,GLfloat y,...);
  glVertexAttribnfv(GLuint index,const GLfloat* data);

 

const顶点属性:

 这个样的属性,表示所有的顶点的这个属性都是一样的
 
 使用下面这些函数来给const顶点属性赋值:
 glVertexAttribnf(GLuint index,GLfloat x,GLfloat y,...);
 glVertexAttribnfv(GLuint index,const GLfloat* data);
 
 只支持float类型vec4类型。

 

顶点数组:

 这个顶点数据在主内存中,每一帧渲染都要上传到显卡上去,比较慢,要结合vbo使用。

 使用下面这个函数来给顶点数组赋值:
  glVertexAttribPointer(index,属性在shader中的位置索引(最少0-7),
          这个属性的大小,例如position为3,因为有x,y,z,
     属性中单个成分的数据类型,比如float x,GL_FLOAT。
     bool,表示非浮点数转换为浮点数的时候是否normalized。
     stride,在主内存中前后顶点属性之间间隔多少个字节。

     数据指针);

  这里面的normalized是什么意思呢?是用来控制数据类型转换的,为false的时候,
  把非浮点数直接转换为浮点数,当他为true的时候,有符号的要映射到-1.0--1.0
  无符号数映射到0.0--1.0
  当为true的时候,关于数据类型如何转换的,有下面这个表:
    
  GL_BYTE   (2c+1)/(2^8-1)
  GL_UNSIGNED_BYTE c/(2^8-1)
  GL_SHORT  (2c+1)/(2^16-1)
  GL_UNSIGNED_SHORT c/(2^16-1)
  GL_FIXED  c/2^16
  GL_FLOAT  c
  GL_HALF_FLOAT_OES c
 
  无论是true和false都要做转换,所以顶点属性为最下面两种比较好类型

 顶点属性在主内存中的存储方式有两种:
  
  1 所有顶点属性存在一起,Pos,normal,texcoord等。
  
  
  2 分开存储pos,normal,texcoord各自存着。

   注意这种方式的stride可不是0啊。 

  两种方式数据的指定,区别不大,主要是stride和数据指针不同。
  使用第二种方式存储比较好一点。

 允许和禁止顶点属性数组:
 
  void glEnableVertexAttribArray(GLuint index);
  void glDisableVertexAttribArray(GLuint index);

 指定属性的index:
 
  glBindAttribLocation(programObj,0,"a_color");
  glBindAttribLocation(programObj,1,"a_position");

  这个函数只能在link program程序之前调用。

怎么区分const顶点属性和顶点数组呢?

 假设在顶点shader中定义了一下如下的顶点属性:

  attribute vec4 a_position;

  既可以把这个a_position当做const顶点属性,也可以当做顶点数组。

  关键是看你调用什么函数来设置他的值,如果你调用了glDisableVertexAttribArray(a_position的location);

  那么这个a_position就是const顶点属性,需要使用函数
   glVertexAttribnf(GLuint index,GLfloat x,GLfloat y,...);
   或glVertexAttribnfv(GLuint index,const GLfloat* data);来设置他的值。

  否则就当做顶点数组来设置他的值。

 

 

获得顶点shader最多支持多少个顶点属性:

 GLint maxVertexAttribs; //大于等于8
 glGetIntgerv(GL_MAX_VERTEX_ATTRIBS,&maxVertexAttribs);
 
   1 获取program中有几个活动的顶点属性:
 glGetProgramiv(program,GL_ACTIVE_ATTRUBUTES,&numActiveAttribs);

   2 获取program中顶点shader里的某个顶点属性的详细信息:
 void glGetActiveAttrib(GLuint program,
   GLuint index,  //属性的index
   GLsizei bufsize, //后面那个name数组的大小
   GLsizei* length, //写入name数组多少个字符,不包括NULL字符
   GLint* size,  //如果属性不是数组返回1,如果是数组
   GLenum* type,
   GLchar* name);

 上面的两个函数是在link program成功之后调用
 通过上面的两个函数就可以知道顶点shader中有多少个顶点属性,以及他们的名字和类型等详细信息。

   3 绑定和获取一个顶点属性的location(也就是那个index):
 void glBindAttribLocation(GLuint program,GLuint index,const GLchar* name);(这个函数在link program之前调用)
 GLint glGetAttribLocation(GLuint program,const GLchar* char);


使用顶点数组的物体的渲染流程:
 
 1 写好shader,

 2 编译shader,
 
 3 创建program

 4 给program添加shader
 
 5 绑定好program里面各个顶点属性的location,这一步可选,并不是必须的。

 6 如果是顶点数组的话enable它。
   给顶点属性好赋值,
  

 7 链接program

  链接之后,可以调用函数获得各个顶点属性的location以及uniform属性的location

 8 指定使用program。

 9 调用函数渲染:
  glVertexAttribPointer(...);
  glEnableVertexAttribArray();

  glDrawElements(GL_TRIANGLES,numIndices,GL_UNSIGNED_SHORT,indices);

使用缓存对象结合顶点数组渲染物体流程:

 1 创建vbo,两个vbo,一个保存各种顶点属性,一个保存索引。
 2 给vbo上传好数据,数据分为顶点数据和索引数据

 3 enable各个顶点属性数组,
  绑定vbo,
  调用glVertexAttribPointer 函数给顶点属性上传数据:
  上传数据的时候,数据指针pos为0
  
  

 4 绑定顶点属性

 5 渲染:
  glDrawElements(GL_TRIANGLES,numIndices,GL_UNSIGNED_SHORT,0);

 这个是将各个顶点属性存储在一起的方式渲染的,还有分开存储的渲染方式。
 分开存储的渲染方式比较好。

 1 ,创建vbo,给各个vbo上传好数据,三个GL_ARRAY_BUFFER,一个GL_ELEMENT_ARRAY_BUFFER

 2 关键的一点,因为这里有三个GL_ARRAY_BUFFER:
  glBindBuffer(GL_ARRAY_BUFFER,posvbo);
  glEnableVertexAttribArray(POS_INDEX);

  glBindBuffer(GL_ARRAY_BUFFER,normalvbo);
  glEnableVertexAttribArray(NORMAL_INDEX);

  glBindBuffer(GL_ARRAY_BUFFER,texcoordvbo);
  glEnableVertexAttribArray(TEXCOORD_INDEX);

 3 glVertexAttribPointer(POS_INDEX,possize,GL_FLOAT,GL_FALSE,posStride,0);
   glVertexAttribPointer(NORMAL_INDEX,normalsize,GL_FLOAT,GL_FALSE,normalStride,0);
   glVertexAttribPointer(TEXCOORD_INDEX,texcoordsize,GL_FLOAT,GL_FALSE,texcoordStride,0); 

 4 绑定好顶点属性location:

 5 glDrawElements(GL_TRIANGLES,numIndices,GL_UNSIGNED_SHORT,0);

 

顶点缓存对象(VB0);
 
   opengles2.0支持两种类型的缓存对象:
 
 一种是array buffer object,就是GL_ARRAY_BUFFER, 用来存储顶点属性数据。
 另一种:element array buffer object:就是GL_ELEMENT_ARRAY_BUFFER,用来存储索引数据。

 在绑定了GL_ARRAY_BUFFER类型的缓存对象之后,如果后面调用glVertexAttribPointer函数指定

 顶点数据的话,这个函数最后面的那个参数是当前绑定的GL_ARRAY_BUFFER缓存对象显存地址从
 开始处的偏移值。

 同理,在绑定了GL_ELEMENT_ARRAY_BUFFER类型的vbo后,后面调用glDrawElement函数绘制物体的时候

 这个函数最后面的那个参数也是偏移值。

 void glGenBuffers(GLsizei n,GLuint* buffers);

 void glBindBuffer(GLenum target, GLuint buffer);//buffer为0的话表示不绑定

  opengl中的那些glBindxxx函数如果后面的那个参数是0的话,就表示解除绑定。

 void glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
  usage有三种:
  GL_STATIC_DRAW: 一次指定多次使用 
  GL_DYNAMIC_DARW,多次指定,多次使用
  GL_STREAM_DRAW, 一次指定,比较少使用。

  如果知道了GL_STATIC_DRAW,那么顶点数据上传到显卡之后,就可以释放主内存

  中的顶点数据了,如果是GL_DYNAMIC_DARW的话,就还不能释放主内存中的数据。

 

更新缓存对象中的数据:
 
 一种方法是使用:
  
  void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size,
   const void* data);

 第二种方法:
  如果支持OES_map_buffer扩展,可以map和unmap vbo的存储空间到内存。


  void* glMapBufferOES(GLenum target, GLenum access);
  
   target必须为GL_ARRAY_BUFFER
  
   access必须为GL_WRITE_ONLY_OES

  返回一个指针,不能读取这个指针指向的内容,读取是undefined的。

  更新完了之后调用:
  
  GLboolean glUnmapBufferOES(GLenum target);
 
   target必须为GL_ARRAY_BUFFER。
  
   如果屏幕分辨率改变了,context使用了多个屏幕,或者内存访问
   越界了,就可能会导致mapped出来的内存被丢弃。返回false

  注意这个map的方法应该只用来更新vbo的全部内容,不推荐更新部分内容。
  即使更新全部内容,性能也比glBufferData差。

  调用这个map函数钱最好先调用一下glBufferData()指定数据指针为0,表示废弃
  整个内容,因为,显卡当前可能真正使用这个vbo。要等他使用完,才能map,
  既然要整个内容更新,就没有必要等他使用完。

 

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值