我正在尝试制作一个非常基本的示例,使用他们的SDK v0.8渲染到Oculus。我要做的就是为两只眼睛渲染纯色。当我运行它时,一切似乎都正确初始化。Oculus显示健康警告消息,但一旦健康警告消息消失,我看到的只是一个黑屏。我在这里做错了什么?
#define GLEW_STATIC
#include <GL/glew.h>
#define OVR_OS_WIN32
#include <OVR_CAPI_GL.h>
#include <SDL.h>
#include <iostream>
int main(int argc, char *argv[])
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window = SDL_CreateWindow("OpenGL", 100, 100, 800, 600, SDL_WINDOW_OPENGL);
SDL_GLContext context = SDL_GL_CreateContext(window);
//Initialize GLEW
glewExperimental = GL_TRUE;
glewInit();
// Initialize Oculus context
ovrResult result = ovr_Initialize(nullptr);
if (OVR_FAILURE(result))
{
std::cout << "ERROR: Failed to initialize libOVR" << std::endl;
SDL_Quit();
return -1;
}
// Connect to the Oculus headset
ovrSession hmd;
ovrGraphicsLuid luid;
result = ovr_Create(&hmd, &luid);
if (OVR_FAILURE(result))
{
std::cout << "ERROR: Oculus Rift not detected" << std::endl;
SDL_Quit();
return 0;
}
ovrHmdDesc desc = ovr_GetHmdDesc(hmd);
std::cout << "Found " << desc.ProductName << "connected Rift device" << std::endl;
ovrSizei recommenedTex0Size = ovr_GetFovTextureSize(hmd, ovrEyeType(0), desc.DefaultEyeFov[0], 1.0f);
ovrSizei bufferSize;
bufferSize.w = recommenedTex0Size.w;
bufferSize.h = recommenedTex0Size.h;
std::cout << "Buffer Size: " << bufferSize.w << ", " << bufferSize.h << std::endl;
// Generate FBO for oculus
GLuint oculusFbo = 0;
glGenFramebuffers(1, &oculusFbo);
// Create swap texture
ovrSwapTextureSet* pTextureSet = nullptr;
if (ovr_CreateSwapTextureSetGL(hmd, GL_SRGB8_ALPHA8, bufferSize.w, bufferSize.h,&pTextureSet) == ovrSuccess)
{
ovrGLTexture* tex = (ovrGLTexture*)&pTextureSet->Textures[0];
glBindTexture(GL_TEXTURE_2D, tex->OGL.TexId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
// Create ovrLayerHeader
ovrEyeRenderDesc eyeRenderDesc[2];
eyeRenderDesc[0] = ovr_GetRenderDesc(hmd, ovrEye_Left, desc.DefaultEyeFov[0]);
eyeRenderDesc[1] = ovr_GetRenderDesc(hmd, ovrEye_Right, desc.DefaultEyeFov[1]);
ovrLayerEyeFov layer;
layer.Header.Type = ovrLayerType_EyeFov;
layer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft | ovrLayerFlag_HeadLocked;
layer.ColorTexture[0] = pTextureSet;
layer.ColorTexture[1] = pTextureSet;
layer.Fov[0] = eyeRenderDesc[0].Fov;
layer.Fov[1] = eyeRenderDesc[1].Fov;
ovrVector2i posVec;
posVec.x = 0;
posVec.y = 0;
ovrSizei sizeVec;
sizeVec.w = bufferSize.w;
sizeVec.h = bufferSize.h;
ovrRecti rec;
rec.Pos = posVec;
rec.Size = sizeVec;
layer.Viewport[0] = rec;
layer.Viewport[1] = rec;
ovrLayerHeader* layers = &layer.Header;
SDL_Event windowEvent;
while (true)
{
if (SDL_PollEvent(&windowEvent))
{
if (windowEvent.type == SDL_QUIT) break;
}
ovrGLTexture* tex = (ovrGLTexture*)&pTextureSet->Textures[0];
glBindFramebuffer(GL_FRAMEBUFFER, oculusFbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex->OGL.TexId, 0);
glViewport(0, 0, bufferSize.w, bufferSize.h);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
ovr_SubmitFrame(hmd, 0, nullptr, &layers, 1);
SDL_GL_SwapWindow(window);
}
SDL_GL_DeleteContext(context);
SDL_Quit();
return 0;
}
这里有许多问题
- 不初始化
ovrLayerEyeFov.RenderPose
- 未正确使用
ovrSwapTextureSet
- 无用的呼叫
SDL_GL_SwapWindow
会导致口吃
在纹理 - 仍绑定进行绘制时读取纹理时可能出现的未定义行为
不初始化ovrLayerEyeFov.RenderPose
您的主要问题是您没有设置ovrLayerEyeFov
结构的RenderPose
成员。 此成员告诉 SDK 您渲染的姿势,因此它应该如何根据当前的头部姿势(自渲染以来可能已更改(应用时间扭曲。 通过不设置此值,您基本上会给 SDK 一个随机的头部姿势,这几乎肯定不是一个有效的头部姿势。
此外,图层类型不需要ovrLayerFlag_HeadLocked
。 它会导致 Oculus 在相对于头部的固定位置显示生成的图像。 它可能会做你想要的,但前提是你用正确的值正确初始化layer.RenderPose
成员(我不确定在ovrLayerEyeFov
的情况下会是什么,因为我只将标志与ovrLayerQuad
结合使用(。
您应该做的是在层声明之后添加以下内容以正确初始化它:
memset(&layer, 0, sizeof(ovrLayerEyeFov));
然后,在渲染循环中,您应该在检查退出事件后立即添加以下内容:
ovrTrackingState tracking = ovr_GetTrackingState(hmd, 0, true);
layer.RenderPose[0] = tracking.HeadPose.ThePose;
layer.RenderPose[1] = tracking.HeadPose.ThePose;
这会告诉 SDK 此图像是从头部当前所在的角度渲染的。
未正确使用ovrSwapTextureSet
代码中的另一个问题是您错误地使用了纹理集。 文档指定在使用纹理集时,需要使用 ovrSwapTextureSet.CurrentIndex
指向的纹理:
ovrGLTexture* tex = (ovrGLTexture*)(&(pTextureSet->Textures[pTextureSet->CurrentIndex]));
。然后在每次调用ovr_SubmitFrame
之后,您需要递增ovrSwapTextureSet.CurrentIndex
然后像这样ovrSwapTextureSet.TextureCount
修改值
pTextureSet->CurrentIndex = (pTextureSet->CurrentIndex + 1) % pTextureSet->TextureCount;
无用的呼叫SDL_GL_SwapWindow
会导致口吃
SDL_GL_SwapWindow(window);
调用是不必要的,毫无意义,因为您没有向默认帧缓冲区绘制任何内容。 一旦你不再绘制纯色,这个调用最终会导致抖动,因为它会阻塞,直到垂直同步(通常在60hz(,导致你有时会错过Oculus显示屏的参考。 现在这是不可见的,因为你的场景只是纯色,但稍后当你在3D中渲染对象时,它会导致无法忍受的抖动。
您可以使用SDL_GL_SwapWindow
,如果您
- 确保已禁用垂直同步
- 具有可用于绘制到窗口的镜像纹理。 (请参阅文档以了解
ovr_CreateMirrorTextureGL
(
可能的帧缓冲问题
我不太确定这是一个严重的问题,但我也建议在将帧缓冲器发送到ovr_SubmitFrame()
之前解绑帧缓冲区并分离 Oculus 提供的纹理,因为我不确定从附加到当前绑定的帧缓冲的纹理读取时行为是否定义良好绘制。它似乎对我的本地系统没有影响,但未定义并不意味着不起作用,它只是意味着你不能依赖它来工作。
我已经更新了示例代码并将其放在这里。 作为奖励,我对其进行了修改,使其在左眼上绘制一种颜色,在右眼上绘制另一种颜色,并设置缓冲区以为每只眼睛渲染一半的缓冲区。