Черный экран 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;
}

person angrycrab    schedule 15.02.2016    source источник
comment
После некоторого дополнительного тестирования я думаю, что это связано с тем, что я использую ovrLayerEyeFov. Использование прямого типа слоя (ovrLayerDirect) позволяет мне видеть текстуру на окулусе.   -  person angrycrab    schedule 15.02.2016
comment
ovrLayerDirect в основном просто переносит вашу текстуру на экран без каких-либо искажений. Это может сработать для вас сейчас, но будет практически бесполезно в будущем. Это в первую очередь инструмент для тестирования вывода на дисплей и искажения объектива ... как только вы перейдете к рендерингу сплошного цвета, он вам не поможет.   -  person Jherico    schedule 16.02.2016


Ответы (1)


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

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

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

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

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

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

Возможные проблемы с фреймбуфером

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

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

person Jherico    schedule 15.02.2016
comment
Вау, это было больше, чем я ожидал от кого-то. Огромное спасибо за помощь! - person angrycrab; 16.02.2016
comment
Отсутствие инициализации ovrLayerEyeFov.RenderPose и неправильное использование ovrSwapTextureSet определенно были проблемой. Наконец-то на окулусе появился треугольник hello world opengl :) Еще раз спасибо. - person angrycrab; 16.02.2016