Oculus 0.8 SDK Черный экран

Я пытаюсь сделать очень простой пример рендеринга в Oculus, используя их SDK v0.8. Все, что я пытаюсь сделать, это сделать сплошной цвет для обоих глаз. Когда я запускаю это, кажется, что все инициализируется правильно. 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;
}

1 ответ

Решение

Здесь есть ряд проблем

  • Не инициализируется ovrLayerEyeFov.RenderPose
  • Не используется ovrSwapTextureSet правильно
  • Бесполезные звонки SDL_GL_SwapWindow вызовет заикание
  • Возможно неопределенное поведение при чтении текстуры, пока она еще привязана к рисованию

Не инициализируется ovrLayerEyeFov.RenderPose

Вы главная проблема в том, что вы не устанавливаете RenderPose член ovrLayerEyeFov состав. Этот участник сообщает SDK, в какой позе вы рендерились и, следовательно, как он должен применять timewarp в зависимости от текущей позы головы (которая могла измениться с момента рендеринга). Не устанавливая это значение, вы в основном задаете 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); Вызов не нужен и не имеет смысла, так как вы ничего не рисуете в кадровый буфер по умолчанию. Как только вы отойдете от рисования сплошным цветом, этот вызов в конечном итоге вызовет дрожание, поскольку он будет блокироваться до v-sync (обычно с частотой 60 Гц), что иногда приводит к пропуску референса дисплея Oculus. Прямо сейчас это будет невидимым, потому что ваша сцена - просто сплошной цвет, но позже, когда вы визуализируете объекты в 3D, это вызовет недопустимое дрожание.

Вы можете использовать SDL_GL_SwapWindow если ты

  • Убедитесь, что V-Sync отключен
  • Имейте зеркальную текстуру, доступную для рисования в окне. (См. Документацию для ovr_CreateMirrorTextureGL)

Возможные проблемы с кадровым буфером

Я менее уверен в том, что это серьезная проблема, но я бы также предложил отсоединить кадровый буфер и отсоединить предоставленную текстуру Oculus перед отправкой ovr_SubmitFrame(), поскольку я не уверен, что поведение хорошо определено при чтении из текстуры, прикрепленной к кадровому буферу, который в настоящее время привязан к рисованию. Кажется, это не влияет на мою локальную систему, но undefined не означает, что не работает, это просто означает, что вы не можете полагаться на то, что он работает.

Я обновил пример кода и разместил его здесь. В качестве бонуса я изменил его, чтобы он рисовал один цвет для левого глаза и другой цвет для правого глаза, а также настраивал буфер, чтобы обеспечить рендеринг одной половины буфера для каждого глаза.

Другие вопросы по тегам