目录

一、创建交换链
二、创建DepthBuffer和FrameBuffer
三、基于交换链对Buffer的使用

一、创建交换链

首先我们关注一下ovrFramebuffer这个结构体:

typedef struct {int Width;int Height;int Multisamples;int TextureSwapChainLength;int TextureSwapChainIndex;ovrTextureSwapChain * ColorTextureSwapChain;GLuint * DepthBuffers;GLuint * FrameBuffers;
} ovrFramebuffer;
这个结构体描述了oculus的Framebuffer包含的信息:
framebuffer的宽、高、采样率,交换链长度,当前正在使用的交换链索引,交换链地址指针。以及一个DepthBuffer和FrameBuffer的指针。
然后看下创建交换链的代码实现:
 //创建一个Texture,类型是2D,格式是colorFormatframeBuffer->ColorTextureSwapChain = vrapi_CreateTextureSwapChain(VRAPI_TEXTURE_TYPE_2D, colorFormat, width, height, 1, true);
这个接口从参数上来看是创建了一个交换链,并将交换链句柄传递出来,交给frameBuffer->ColorTextureSwapChain.
根据汇编代码,glTexStorage2D和glTexStorage3D推断这里使用的是不可变纹理
即在加载数据之前指定纹理的格式和大小。这样可以预先进行所有一致性和内存检查
一旦纹理不可变,它的格式和大小就不会再变化,但应用仍然可以通过使用glTexSubImage2D
,glTexSubImage3D,glGennerateMipMap或者渲染到纹理加载图像数据
即创建了两个不可变纹理,并把存储这两个纹理地址的数组转换成ovrTextureSwapChain传递出去。
用图形表示,就是如下:

至于这些texture的格式和大小:
由http://blog.csdn.net/d_uanrock/article/details/48209533中对SuggestedEyeResolutionWidth以及SuggestedEyeResolutionHeight
的分析:

其中的ovrHmdInfo是VR 头盔(眼镜)相关的一个结构体,包含,分辨率,刷新率,默认分辨率,和水平垂直视野角度。

显示一幅分辨率为2560*2440的图像的时候,每个像素在眼睛中所占用的视角约为0.06度。展现完整360度就需要6000个像素(360/0.06=6000),展现90度的视角就需要占用其中的1/4。分辨率为1536*1536的图像能够产生1:1的图像。但是对于偏离中心的像素,需要提供大密度投影。考虑到不需要大密度投影的情况和更好的表现,这里提供了默认值1024*1024.
可以知道程序中默认的texture 对应Buffer的大小为1024x1024

接口vrapi_GetTextureSwapChainLength就是用于获取交换链长度,也就是交换链个数,这里是2

二、创建DepthBuffer和FrameBuffer

后面创建DepthBuffer和FrameBuffer的时候都用到了这个长度。
      frameBuffer->DepthBuffers = (GLuint *) malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint));frameBuffer->FrameBuffers = (GLuint *) malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint));
可以看到,这里只分配了2*sizeof(GLuint)的很小的空间,因为和前面的textureSwapChain一样,这里的的指针指向的也只是一个存储地址的数组。
分配了存储地址的数组之后,就开始通过glGenRenderbuffers实际为DepthBuffer和FrameBuffer分配空间:
                 //创建渲染缓冲区buffer,12章,P233,glGenRenderbuffers作用是分配n个渲染缓冲区对象名称//这里分配了1个渲染缓冲区名称存储到frameBuffer->DepthBuffers[i]中,名称是不为0的无符号整数// Create multisampled depth buffer.GL(glGenRenderbuffers(1, &frameBuffer->DepthBuffers[i]));//指定渲染缓冲区,第一个值必须指定为GL_RENDERBUFFERGL(glBindRenderbuffer(GL_RENDERBUFFER,frameBuffer->DepthBuffers[i]));GL(glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER,multisamples, GL_DEPTH_COMPONENT24, width, height));GL(glBindRenderbuffer(GL_RENDERBUFFER, 0));//创建帧缓冲区buffer// Create the frame buffer.//创建framebufferGL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i]));GL(glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer->FrameBuffers[i]));GL(glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture,0, multisamples));GL(glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,frameBuffer->DepthBuffers[i]));GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus( GL_FRAMEBUFFER ));GL(glBindFramebuffer(GL_FRAMEBUFFER, 0));if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) {ALOGE("Incomplete frame buffer object: %s",EglFrameBufferStatusString(renderFramebufferStatus));return false;}
这里,为DepthBuffer和FrameBuffer分配了空间,并进行了相应的初始化。
这里要理解一下:glFramebufferTexture2D和glFramebufferRenderbuffer
根据
http://www.songho.ca/opengl/gl_fbo.html
中的解释:
 FBO provides glFramebufferTexture2D() to switch 2D texture objects, and glFramebufferRenderbuffer() to switch renderbuffer objects
说明,这里是将一个2Dtexture 对象和renderbuffer对象绑定到FBO上。
好了,到这里。再次梳理一下现在的数据结构:

注意,这里的FrameBuffer其实就是一个FBO了。其中包含的是一系列的附着点。
因此,这里在每一个FrameBuffer的GL_COLOR_ATTACHMENT0上绑定了一个texture,GL_DEPTH_ATTACHMENT上绑定了一个DepthBuffer
(可以再回头分析一下链接里的那篇文章)
因此现在的数据结构图就是下面这样的了:

三、基于交换链对Buffer的使用

了解完这一部分,我们现在来看下,程序是如何基于交换链对这些数据结构进行使用的:

            ovrFramebuffer_Resolve(frameBuffer);parms.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].ColorTextureSwapChain =frameBuffer->ColorTextureSwapChain;parms.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].TextureSwapChainIndex =frameBuffer->TextureSwapChainIndex;parms.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].TexCoordsFromTanAngles =renderer->TexCoordsTanAnglesMatrix;       //纹理坐标parms.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].HeadPose =updatedTracking.HeadPose;ovrFramebuffer_Advance(frameBuffer);

因为要画的是左眼和右眼的画面。所以这里有左右眼对应的数据结构所在的对象params的类型ovrFrameParms:

typedef struct
{// Layers composited in the time warp.ovrFrameLayer                 Layers[VRAPI_FRAME_LAYER_TYPE_MAX];int                                 LayerCount;// Combination of ovrFrameOption flags.int                           WarpOptions;// Which program to run with these layers.ovrFrameProgram               WarpProgram;// Program-specific tuning values.float                         ProgramParms[4];// Application controlled frame index that uniquely identifies this particular frame.// This must be the same frame index that was passed to vrapi_GetPredictedDisplayTime()// when synthesis of this frame started.long long                     FrameIndex;// WarpSwap will not return until at least this many V-syncs have// passed since the previous WarpSwap returned.// Setting to 2 will reduce power consumption and may make animation// more regular for applications that can't hold full frame rate.int                                 MinimumVsyncs;// Latency Mode.ovrExtraLatencyMode           ExtraLatencyMode;// Rotation from a joypad can be added on generated frames to reduce// judder in FPS style experiences when the application framerate is// lower than the V-sync rate.// This will be applied to the view space distorted// eye vectors before applying the rest of the time warp.// This will only be added when the same ovrFrameParms is used for// more than one V-sync.ovrMatrix4f                   ExternalVelocity;// jobject that will be updated before each eye for minimal// latency with VRAPI_FRAME_PROGRAM_MASKED_PLANE_EXTERNAL.// IMPORTANT: This should be a JNI weak reference to the object.// The system will try to convert it into a global reference before// calling SurfaceTexture->Update, which allows it to be safely// freed by the application.jobject                             SurfaceTextureObject;// CPU/GPU performance parameters.ovrPerformanceParms           PerformanceParms;// For handling HMD events and power level state changes.ovrJava                             Java;
} ovrFrameParms;
从http://blog.csdn.net/d_uanrock/article/details/48209533中对这一部分的分析结合实现代码。可以知道
左右眼都对应着有一组数据结构ovrFrameLayerTexture
typedef struct
{// Because OpenGL ES does not support clampToBorder, it is the// application's responsibility to make sure that all mip levels// of the primary eye texture have a black border that will show// up when time warp pushes the texture partially off screen.//CLAMP_TO_BORDER causes OpenGL to only take the border color at the edge of the texture//rather than the average of the border color and texture edge texels.//This allows for a perfect border around the texture.ovrTextureSwapChain *   ColorTextureSwapChain;// The depth texture is optional for positional time warp.ovrTextureSwapChain *   DepthTextureSwapChain;// Index to the texture from the set that should be displayed.int                                 TextureSwapChainIndex;// Points on the screen are mapped by a distortion correction// function into ( TanX, TanY, -1, 1 ) vectors that are transformed// by this matrix to get ( S, T, Q, _ ) vectors that are looked// up with texture2dproj() to get texels.ovrMatrix4f                   TexCoordsFromTanAngles;// Only texels within this range should be drawn.// This is a sub-rectangle of the [(0,0)-(1,1)] texture coordinate range.ovrRectf                      TextureRect;// The tracking state for which ModelViewMatrix is correct.// It is ok to update the orientation for each eye, which// can help minimize black edge pull-in, but the position// must remain the same for both eyes, or the position would// seem to judder "backwards in time" if a frame is dropped.ovrRigidBodyPosef       HeadPose;
} ovrFrameLayerTexture;
当处理左眼的时候,就更新左眼的这个数据结构的值,当处理右眼的时候,就处理右眼的这个数据结构的值。
结合上面的数据结构总图,可以知道,程序的设计师左眼用一组FrameBuffer,右眼用一组FrameBuffer。
1.计算要绘制的数据及左右眼视角矩阵
2.绑定交换链中的一个Framebuffer
static void ovrFramebuffer_SetCurrent(ovrFramebuffer * frameBuffer) {GL(glBindFramebuffer(GL_FRAMEBUFFER,frameBuffer->FrameBuffers[frameBuffer->TextureSwapChainIndex]));
}
3.绘制图像到绑定的FrameBuffer
4.解绑FrameBuffer
static void ovrFramebuffer_Resolve(ovrFramebuffer * frameBuffer) {// Discard the depth buffer, so the tiler won't need to write it back out to memory.const GLenum depthAttachment[1] = { GL_DEPTH_ATTACHMENT };glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, depthAttachment);// Flush this frame worth of commands.glFlush();
}

5.更新交换链

   parms.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].ColorTextureSwapChain =frameBuffer->ColorTextureSwapChain;parms.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].TextureSwapChainIndex =frameBuffer->TextureSwapChainIndex;parms.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].TexCoordsFromTanAngles =renderer->TexCoordsTanAnglesMatrix;       //纹理坐标parms.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].HeadPose =updatedTracking.HeadPose;

6.移动到下一帧要使用的交换链索引

static void ovrFramebuffer_Advance(ovrFramebuffer * frameBuffer) {// Advance to the next texture from the set.frameBuffer->TextureSwapChainIndex =(frameBuffer->TextureSwapChainIndex + 1)% frameBuffer->TextureSwapChainLength;
}

每一帧都不停地循环1到6这几个步骤,便实现了,一帧画左眼,一帧画右眼的效果。

本篇主要将了Oculus是在OpenGL层面上对纹理交换链做了哪些数据结构的设计,如何基于交换链实现左右眼画面的显示的。

Oculus VR SDK实现-Oculus针对双眼显示的交换链设计相关推荐

  1. Oculus VR SDK实现 -主要结构体及Api接口设计

     其中的ovrHmdInfo是VR 头盔(眼镜)相关的一个结构体,包含,分辨率,刷新率,默认分辨率,和水平垂直视野角度. 显示一幅分辨率为2560*2440的图像的时候,每个像素在眼睛中所占用的视 ...

  2. Oculus VR设计指南

     Oculus VR 设计指南 本指南由字节跳动的四位同学共同翻译,西瓜视频的设计师们共同校对. 如果你对 XR 设计工作感兴趣,vivo 正在招聘 XR 方向的人机交互设计师,当然如果你有其他的任何 ...

  3. oculus vr开发_如何在Oculus Quest上无线玩Steam VR游戏

    oculus vr开发 Oculus 眼 The Oculus Quest is a fully stand-alone headset. It's free of the wires of PC-o ...

  4. 虚拟现实大潮渐近:Oculus VR、EA和Avegant等多家公司...

    虚拟现实不是新词,上世纪的许多科幻小说中就描述过未来虚拟现实技术高度发达后的世界,但是这两年,虚拟现实真正在商业市场有了新的突破,代表者就是Oculus VR.近日,一众在虚拟现实领域有所建树的公司结 ...

  5. Android Studio 2.0+Gradle 2.12编译Oculus Mobile SDK

    1. 下载与安装: Android Studio 2.0  https://dl.google.com/dl/android/studio/ide-zips/2.0.0.20/android-stud ...

  6. oculus vr开发_Oculus IndieCade VR Jam总结

    oculus vr开发 Since August 2nd, 220 teams of developers around the world have been working on projects ...

  7. Oculus VR眼镜调研

    VR眼镜 oculus 软件安装 最详细的Oculus Rift VR/3D眼镜安装设置教程(多图) 在线安装Oculus rift驱动[非VPN方法] Rift,Go,Gear VR,Quest: ...

  8. Android下Opengl ES实现单屏幕双眼显示

    http://blog.csdn.net/u011371324/article/details/68946779 默认情况下,Opengl ES使用系统提供的帧缓冲区作为绘图表面,一般情况下,如果只在 ...

  9. GOOGLE VR SDK开发VR游戏,VR播放器之一

    最近一年来,VR虚拟现实和AR增强现实技术的宣传甚嚣尘上.其实VR,AR技术很早就有了,一直没有流行开来,不可否认价格是影响技术推广的最大壁垒.谷歌对VR最大的贡献是提供了廉价的谷歌眼镜,按照GOOG ...

最新文章

  1. PL/SQL高级编程
  2. CocoaPods详解之----使用篇
  3. 每日程序C语言3-三个数大小排序
  4. poco c++ 开发指南_掌握这个框架,你将会开发通杀全平台的万能爬虫
  5. php ziparchive(),php的ZipArchive类用法实例
  6. 坎蒂雷赋权法 matlab,干货 | 利用MATLAB实现FMCW雷达中的常用角度估计方法
  7. 中国移动:2016年全力推NFC,以公共交通为突破口
  8. rem在响应式布局中的应用
  9. C语言---数字迷宫
  10. 计算机鼠标双击怎么,讲解电脑鼠标左键单击变双击怎么办
  11. 这个世界上人真的分三六九等,你信吗?
  12. 一站式跨境电商免费开源ERP平台,简洁的操作,助您高效工作
  13. Tecplot 输入数学符号
  14. 腾讯金融云mysql,腾讯云金融级云数据库优势与功能介绍
  15. CUPS学习五:打印机基础
  16. C语言中的找特殊数字问题
  17. mysql 如何存带有特殊符号的微信昵称
  18. html设置ios状态栏颜色,ios 显示html标签,超链接颜色以及下划线的处理
  19. 面对困难,你可以等死,也可以马上动手解决问题,解决完一个,就再解决一个,然后就可以回家了。...
  20. 在线博客系统——最热文章、最新文章

热门文章

  1. linux安装无法删除分区表,删除LINUX分区
  2. 电路仿真软件详谈(24),基于proteus电路仿真软件的电压表印刷电路板设计
  3. 微信Android模块化架构重构实践
  4. java毕业设计校园招聘论坛设计mybatis+源码+调试部署+系统+数据库+lw
  5. 2.1_11 Oralce 执行计划之3_直方图(Histograms)
  6. echarts关系图graph点击折叠
  7. 图的操作与实现——简易城市交通网
  8. 朗驰欣创——嵌入式软件面试
  9. 获取当前日期并转换成day_id:20210810
  10. 310. Minimum Height Trees 【Medium】 树