Oculus VR SDK实现-Oculus针对大量显示数据的buffer设计
来源:互联网 发布:最出名的网络作家 编辑:程序博客网 时间:2024/06/11 19:38
- 目录:
第一部分讲如何构建场景中要显示的物体的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
- Oculus VR SDK实现-Oculus针对大量显示数据的buffer设计
- Oculus VR SDK实现-Oculus针对双眼显示的交换链设计
- Oculus VR SDK实现-Oculus左右眼视角的偏移实现
- VR系列——Oculus Audio sdk文档:二、Oculus音频SDK指南(3)——Oculus Audio SDK的特性
- VR系列——Oculus Mobile SDK文档:一、Mobile SDK的介绍
- VR系列——Oculus Audio sdk文档:一、虚拟现实音频技术简介(6)——空间化的音响设计
- VR系列——Oculus Audio sdk文档:四、传统Oculus声场定位技术的统一集成指南(1)——概述
- VR系列——Oculus Audio sdk文档:四、传统Oculus声场定位技术的统一集成指南(3)——安装到Unity
- Oculus Rift SDK 安装
- VR系列——Oculus Audio sdk文档:四、传统Oculus声场定位技术的统一集成指南(4)——如何在Unity中使用Oculus声场定位技术
- VR系列——Oculus Audio sdk文档:五、Wwise集成Oculus声场定位指南(3)——声音的特性、整合Oculus Spatializer及OSP版本在Wwise中的迁移
- VR系列——Oculus Rift 介绍指南:四、Oculus Rift的硬件设置
- VR系列——Oculus Rift 开发者指南:三、Oculus Rift的渲染(一)
- VR系列——Oculus Rift 开发者指南:三、Oculus Rift的渲染(二)
- VR系列——Oculus Rift 开发者指南:三、Oculus Rift的渲染(三)
- VR系列——Oculus Rift 开发者指南:三、Oculus Rift的渲染(四)
- VR系列——Oculus Rift 开发者指南:三、Oculus Rift的渲染(五)
- VR系列——Oculus Rift 开发者指南:三、Oculus Rift的渲染(六)
- 前端 后端的基本 交互(后端 scala)(一)
- Joomla如何发送一封 简单的&&复杂的 邮件
- 逻辑运算符,位运算符以及三目运算符总结
- Java学习笔记之IO流
- 我 对 数论的学习
- Oculus VR SDK实现-Oculus针对大量显示数据的buffer设计
- Linux 下的 FFmpeg 安装编译环境配置总结
- 微信小程序之你不可不知的调试技巧
- ARKit & OpenGL ES
- 怎样从0开始搭建一个测试框架_3——参数化
- swift3
- js new 发生了什么
- 2017 Multi-University Training Contest
- 百练3421,noi 25:螺旋加密