Oculus VR SDK实现-Oculus针对大量显示数据的buffer设计

来源:互联网 发布:最出名的网络作家 编辑:程序博客网 时间:2024/06/11 19:38

  1. 目录:

第一部分讲如何构建场景中要显示的物体的buffer,比如一个立方体Cube
第二部分主要讲配合显示用到的一些buffer。
第三部分讲这些buffer的使用

首先参考文章:Vertex Array的性能表现:
http://www.openglsuperbible.com/2013/12/09/vertex-array-performance/
关于Vertex Array的官方解释:
https://www.khronos.org/opengl/wiki/Vertex_Specification#Vertex_Array_Object
这里说明vertex attributes也有编号,从0到 GL_MAX_VERTEX_ATTRIBS - 1,并且vertex shader会来读其中的属性,如果读不到就提供一个常量值。
因此要理解vertex attributes还要结合shader来理解。




一、立方体Cube的创建

1.创建立方体

首先构造结构体来存储立方体的8个顶点位置及颜色属性,并初始化
      typedef struct {            char positions[8][4];            unsigned char colors[8][4];      } ovrCubeVertices;

2.创建索引

      static const unsigned short cubeIndices[36] = { 0, 1, 2, 2, 3, 0,      // top                  4, 5, 6, 6, 7, 4, // bottom                  2, 6, 7, 7, 1, 2, // right                  0, 4, 5, 5, 3, 0, // left                  3, 5, 6, 6, 2, 3, // front                  0, 1, 7, 7, 4, 0  // back                  };

3.创建结构体来存储一个几何体对应的信息

typedef struct {      GLuint VertexBuffer;      GLuint IndexBuffer;      GLuint VertexArrayObject;      int VertexCount;      int IndexCount;      ovrVertexAttribPointer VertexAttribs[MAX_VERTEX_ATTRIB_POINTERS];} ovrGeometry;
这里一个几何体的信息,包含一个顶点buffer,一个索引buffer,一个顶点数组对象,一个顶点数,一个索引数,以及一个
存储属性的数组,这个存储属性的数组

4.设计结构体存储顶点属性

typedef struct {      GLuint Index;      GLint Size;      GLenum Type;      GLboolean Normalized;      GLsizei Stride;      const GLvoid * Pointer;} ovrVertexAttribPointer;

每个顶点属性都包含以下几部分:
Index:标识顶点属性类型的一个枚举变量值,通过这个枚举变量值可以知道这是一个什么属性
Size:属性的数据大小
Type:存储属性的数据类型
Normalized:
Stride:步长
Pointer:偏移量
这里讲解一下步长的概念:
比如在上面的ovrCubeVertices结构体,初始化后如下:

      static const ovrCubeVertices cubeVertices = {      // positions                  { { -127, +127, -127, +127 }, { +127, +127, -127, +127 }, { +127,                              +127, +127, +127 }, { -127, +127, +127, +127 },     // top                              { -127, -127, -127, +127 }, { -127, -127, +127, +127 }, {                                          +127, -127, +127, +127 }, { +127, -127, -127, +127 }// bottom                  },                  // colors                  { { 255, 0, 255, 255 }, { 0, 255, 0, 255 }, { 0, 0, 255, 255 }, {                              255, 0, 0, 255 }, { 0, 0, 255, 255 }, { 0, 255, 0, 255 }, {                              255, 0, 255, 255 }, { 255, 0, 0, 255 } }, };      static const unsigned short cubeIndices[36] = { 0, 1, 2, 2, 3, 0,      // top                  4, 5, 6, 6, 7, 4, // bottom                  2, 6, 7, 7, 1, 2, // right                  0, 4, 5, 5, 3, 0, // left                  3, 5, 6, 6, 2, 3, // front                  0, 1, 7, 7, 4, 0  // back                  };

那么这里的Stride 的值为:
geometry->VertexAttribs[0].Stride = sizeof(cubeVertices.positions[0]);
也就是说,步长就是一个属性对应的数据总长度。
而上面的偏移量就是指存储这个属性的数据段的起始位置:
比如,位置属性的偏移量为:
geometry->VertexAttribs[0].Pointer = (const GLvoid *) offsetof(                  ovrCubeVertices, positions);
颜色属性的偏移量为:
geometry->VertexAttribs[1].Pointer = (const GLvoid *) offsetof(                  ovrCubeVertices, colors);

5.创建顶点buffer,并将上面的cubeVertices的内容放到顶点buffer中:


所以顶点buffer的前一半是顶点位置属性数据,后一半是顶点颜色属性的数据

到这里,创建顶点的工作就算做完了,其实就是构造顶点的位置及其属性数据,并将其放到顶点buffer中。

二、辅助显示的相关buffer创建


上面创建好了立方体的数据以及buffer,这里如果要创建1500个立方体,那么应该如何处理呢?
就可以为这1500个buffer开辟一个buffer空间:InstanceTransformBuffer。
注意,这个buffer里存储的都是transform矩阵,因为通过一个标准的矩阵的位置和这个buffer中存储的transform矩阵,就可以算出每一个矩阵的具体位置。(但为什么不直接存空间位置呢?
还只需要x,y,z三个值?),这里大小为NUM_INSTANCES * 4 * 4 * sizeof( float )

glBufferData( GL_ARRAY_BUFFER, NUM_INSTANCES * 4 * 4 * sizeof( float ), NULL, GL_DYNAMIC_DRAW ));
这里有创建两个VAO,一个是为cube创建的,首先分析下这段代码:
static void ovrGeometry_CreateVAO(ovrGeometry * geometry) {      GL(glGenVertexArrays(1, &geometry->VertexArrayObject));      GL(glBindVertexArray(geometry->VertexArrayObject));      GL(glBindBuffer(GL_ARRAY_BUFFER, geometry->VertexBuffer));      for (int i = 0; i < MAX_VERTEX_ATTRIB_POINTERS; i++) {            if (geometry->VertexAttribs[i].Index != -1) {                  GL(glEnableVertexAttribArray(geometry->VertexAttribs[i].Index));                  GL(glVertexAttribPointer(geometry->VertexAttribs[i].Index,                                          geometry->VertexAttribs[i].Size,                                          geometry->VertexAttribs[i].Type,                                          geometry->VertexAttribs[i].Normalized,                                          geometry->VertexAttribs[i].Stride,                                          geometry->VertexAttribs[i].Pointer));            }      }      GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry->IndexBuffer));      GL(glBindVertexArray(0));}

这段代码可以理解为创建了VertexArrayObject,并绑定之后
(1)在这个VertexArrayObject上的GL_ARRAY_BUFFER附着点绑定了VertexBuffer,
(2)然后开启了位置和颜色这两个顶点属性。
(3)在在这个VertexArrayObject上的GL_ELEMENT_ARRAY_BUFFER 附着点绑定了IndexBuffer
经过上面三个步骤:
1.Cube的顶点数据有了,也就是绑定在VertexBuffer中
2.告诉了OpenGL这个VertexBuffer的位置和颜色属性的操作方式。因为VertexAttribs中存储了位置和颜色属性的步长和偏移量,这样就知道怎么样去操作VertexBuffer。
3.还告诉了OpenGL这个VertexBuffer的索引。
这样就可以知道每一个顶点的位置和颜色,以及画法(索引)

http://www.openglsuperbible.com/2013/12/09/vertex-array-performance/
这篇文章中的用法也是一样。
创建了VAO之后的第二步。

首先绑定以继续操作VAO:
 // Modify the VAO to use the instance transform attributes.            GL(glBindVertexArray(scene->Cube.VertexArrayObject));
前面绑定在GL_ARRAY_BUFFER上的不是geometry->VertexBuffer吗?那里面只有一个Cube的数据,所以这时候,修改这个附着点绑定的buffer,
将包含1500个Cube的buffer绑定上去:

GL(glBindBuffer(GL_ARRAY_BUFFER, scene->InstanceTransformBuffer));
可是前面说过InstanceTransformBuffer中存储的是16个一组的位置数据。

前面定义了一个枚举类型:

  enum {            VERTEX_ATTRIBUTE_LOCATION_POSITION,            VERTEX_ATTRIBUTE_LOCATION_COLOR,            VERTEX_ATTRIBUTE_LOCATION_UV,            VERTEX_ATTRIBUTE_LOCATION_TRANSFORM      } location;

因此这里:
  for (int i = 0; i < 4; i++) {                  GL(glEnableVertexAttribArray(VERTEX_ATTRIBUTE_LOCATION_TRANSFORM + i));                  GL(glVertexAttribPointer(VERTEX_ATTRIBUTE_LOCATION_TRANSFORM + i,4,GL_FLOAT,false,4 * 4 * sizeof(float),(void * )(i * 4 * sizeof(float))));                  GL(glVertexAttribDivisor(VERTEX_ATTRIBUTE_LOCATION_TRANSFORM + i,1));            }
glEnableVertexAttribArray
这个函数的调用,说明开启了四种新的属性。
并且每一种属性的步长都是4 * 4 * sizeof(float),而起始偏移量i * 4 * sizeof(float),注意,这里不同属性的起始偏移量小于步长,则说明了其存储属性的方式为各个属性混合组成一组存在里面:


那么这新增加的四组属性和前面的位置颜色属性是什么关系 呢?
sizeof(float)为4。
位置的偏移量为0,颜色的偏移量为32,而这里的偏移量为0,16,32,48,64

三、Buffer的使用

前面创建了VertexBuffer,InstanceTransformBuffer等,这里就看下如何利用这些buffer来画出图像。
首先是如何修改buffer中的数据,这里是修改buffer中的transform矩阵这一数据
 // Update the instance transform attributes.      GL(glBindBuffer(GL_ARRAY_BUFFER, scene->InstanceTransformBuffer));      GL(ovrMatrix4f * cubeTransforms = (ovrMatrix4f *) glMapBufferRange( GL_ARRAY_BUFFER, 0, NUM_INSTANCES * sizeof( ovrMatrix4f ), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT ));      for (int i = 0; i < NUM_INSTANCES; i++) {            const ovrMatrix4f rotation = ovrMatrix4f_CreateRotation(                        scene->CubeRotations[i].x * simulation->CurrentRotation.x,                        scene->CubeRotations[i].y * simulation->CurrentRotation.y,                        scene->CubeRotations[i].z * simulation->CurrentRotation.z);            const ovrMatrix4f translation = ovrMatrix4f_CreateTranslation(                        scene->CubePositions[i].x, scene->CubePositions[i].y,                        scene->CubePositions[i].z);            const ovrMatrix4f transform = ovrMatrix4f_Multiply(&translation,                        &rotation);            cubeTransforms[i] = ovrMatrix4f_Transpose(&transform);      }      GL(glUnmapBuffer(GL_ARRAY_BUFFER));      GL(glBindBuffer(GL_ARRAY_BUFFER, 0));
首先是将InstanceTransformBuffer通过glMapBufferRange函数映射成一个ovrMatrix4f格式的数组。
然后便可以通过修改数组的方式来修改这个buffer了。

之后
   GL(glUseProgram(scene->Program.Program));            GL(glUniformMatrix4fv(scene->Program.Uniforms[UNIFORM_VIEW_MATRIX],1, GL_TRUE, (const GLfloat * )eyeViewMatrix.M[0]));            GL(glUniformMatrix4fv(scene->Program.Uniforms[UNIFORM_PROJECTION_MATRIX], 1,GL_TRUE,(const GLfloat * )renderer->ProjectionMatrix.M[0]));            GL(glBindVertexArray(scene->Cube.VertexArrayObject));            GL(glDrawElementsInstanced( GL_TRIANGLES, scene->Cube.IndexCount, GL_UNSIGNED_SHORT, NULL, NUM_INSTANCES ));            GL(glBindVertexArray(0));            GL(glUseProgram(0));

直接绑定VertexArrayObject,然后调用glDrawElementsInstanced来进行绘制就可以了
当前,每次绘制之前都会调用交换链:

static void ovrFramebuffer_SetCurrent(ovrFramebuffer * frameBuffer) {      GL(                  glBindFramebuffer(GL_FRAMEBUFFER,                              frameBuffer->FrameBuffers[frameBuffer->TextureSwapChainIndex]));}



阅读全文
0 0
原创粉丝点击