OpenGL для видео на iPhone

В настоящее время я работаю над проектом, чтобы преобразовать физическое моделирование в видео на самом iPhone.

Для этого в настоящее время я использую два разных цикла. Первый цикл выполняется в блоке, где объект AVAssetWriterInput опроса EAGLView для большего количества изображений. EAGLView предоставляет изображения из массива, в котором они хранятся.

Другой цикл - это фактическое моделирование. Я отключил таймер моделирования и каждый раз вызываю галочку с заранее заданной разницей во времени. Каждый раз, когда вызывается тик, я создаю новое изображение в методе swap-буферов EAGLView после того, как буферы были заменены. Затем это изображение помещается в массив, который опроса AVAssetWriter.

Существует также некоторый код, чтобы убедиться, что массив не слишком большой

Все это прекрасно работает, но очень медленно.

Есть ли что-то, что я делаю, концептуально, заставляя весь процесс быть медленнее, чем это могло бы быть? Кроме того, знает ли кто-нибудь более быстрый способ получить изображение из Open GL, чем glReadPixels?

Ответ 1

Видеопамять спроектирована так, что она быстро записывается и замедляется для чтения. Вот почему я выполняю рендеринг текстуры. Вот весь метод, который я создал для рендеринга сцены в текстуру (есть некоторые пользовательские контейнеры, но я думаю, что довольно просто заменить их своими):

-(TextureInf*) makeSceneSnapshot {
    // create texture frame buffer
    GLuint textureFrameBuffer, sceneRenderTexture;

    glGenFramebuffersOES(1, &textureFrameBuffer);
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, textureFrameBuffer);

    // create texture to render scene to
    glGenTextures(1, &sceneRenderTexture);
    glBindTexture(GL_TEXTURE_2D, sceneRenderTexture);

    // create TextureInf object
    TextureInf* new_texture = new TextureInf();
    new_texture->setTextureID(sceneRenderTexture);
    new_texture->real_width = [self viewportWidth];
    new_texture->real_height = [self viewportHeight];

    //make sure the texture dimensions are power of 2
    new_texture->width = cast_to_power(new_texture->real_width, 2);
    new_texture->height = cast_to_power(new_texture->real_height, 2);

    //AABB2 = axis aligned bounding box (2D)
    AABB2 tex_box;

    tex_box.p1.x = 1 - (GLfloat)new_texture->real_width / (GLfloat)new_texture->width;
    tex_box.p1.y = 0;
    tex_box.p2.x = 1;
    tex_box.p2.y = (GLfloat)new_texture->real_height / (GLfloat)new_texture->height;
    new_texture->setTextureBox(tex_box);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,  new_texture->width, new_texture->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, sceneRenderTexture, 0);

    // check for completness
    if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
        new_texture->release();
        @throw [NSException exceptionWithName: EXCEPTION_NAME
                                       reason: [NSString stringWithFormat: @"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES)]
                                     userInfo: nil];
        new_texture = nil;
    } else {
        // render to texture
        [self renderOneFrame];
    }

    glDeleteFramebuffersOES(1, &textureFrameBuffer);

    //restore default frame and render buffers
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, _defaultFramebuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, _colorRenderbuffer);
    glEnable(GL_BLEND);         
    [self updateViewport];      
    glMatrixMode(GL_MODELVIEW);


    return new_texture;
}

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

Ответ 2

Следует помнить, что графический процессор работает асинхронно с CPU, поэтому, если вы попытаетесь сделать glReadPixels сразу после завершения рендеринга, вам придется ждать, пока команды будут очищены до графического процессора и отображены до того, как вы сможете прочитайте их.

Вместо того, чтобы ждать синхронно, сделайте снимки в очередь текстур (используя такие FBOs, как Max). Подождите, пока вы не отложите еще несколько кадров, прежде чем сбросить один из предыдущих кадров. Я не знаю, поддерживает ли iPhone заборы или объекты синхронизации, но если это так, вы можете проверить их, чтобы убедиться, что рендеринг закончен перед чтением пикселей.

Ответ 3

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

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

Ответ 4

Пока вопрос не новый, он еще не ответил, поэтому я подумал, что смогу войти.

glReadPixels действительно очень медленный и поэтому не может использоваться для записи видео из приложения OpenGL без неблагоприятного воздействия на производительность.

Мы нашли обходное решение и создали бесплатный SDK под названием Everyplay, который может записывать графику на основе OpenGL в видеофайл без потери производительности. Вы можете проверить это на https://developers.everyplay.com/