Как избежать моментального растяжения при авторотации приложений iOS OpenGL ES

В последнее время меня это беспокоит. Достаточно просто собрать приложение OpenGL ES, которое поддерживает как портретную, так и альбомную ориентацию. Но во время автоповорота система, кажется, просто принудительно растягивает буфер рендеринга до новых размеров один раз, автоповорачивает, а затем вызывает обычный -layoutSubviews -> -resizeFromLayer: и т. Д., Чтобы чертежи можно было настроить под новые размеры области просмотра.

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

Возможно, мне следует перехватить авторотацию до того, как это произойдет (используя обычные методы UIViewController), "развернуть" буфер рендеринга до идеального квадрата самого длинного размера экрана (например, 1136px x 1136px на iPhone 5), чтобы он "истекал кровью" вне экрана, выполнить автоповорот (без изменения размера буфера рендеринга и, следовательно, без растяжения, как при переключении, например, между двумя альбомными ориентациями), и, наконец, снова настроить буфер кадра, чтобы избавиться от невидимых, «потраченных впустую» полей за пределами экрана? (конечно, у меня всегда мог быть квадратный буфер кадра, но это было бы неэффективно с точки зрения скорости заполнения)

Какие-либо предложения? Есть ли лучший способ добиться этого, о котором я не думал?

ИЗМЕНИТЬ

Я изменил свой -resizeFromLayer: код следующим образом:

CGRect bounds = [layer bounds];

// Enlarge layer to square of the longest side:
if (bounds.size.width < bounds.size.height) {
    bounds.size.width = bounds.size.height;
}
else{
    bounds.size.height = bounds.size.width;
}

[layer setBounds:bounds];


// Adjust size to match view's layer:
[_mainContext renderbufferStorage:GL_RENDERBUFFER
                         fromDrawable:(CAEAGLLayer*) [_view layer]];

// Query the new size:
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH,  &_backingWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_backingHeight);

// (etc., you know the drill...)

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


person Nicolas Miari    schedule 09.09.2013    source источник
comment
У меня еще не было времени попробовать упомянутый выше подход. Как только смогу протестировать, опубликую здесь результаты.   -  person Nicolas Miari    schedule 11.09.2013


Ответы (1)


Мне удалось довольно легко исправить это в моем 3D-приложении, получив размер представления из GLKView presentationLayer. Это даст текущий размер вида во время анимации вращения, и правильная матрица проекции может быть рассчитана на основе этого размера на этапе обновления. Это предотвращает искажение неправильного формата изображения.

Если вы хотите попробовать это, вы можете добавить следующие строки в шаблон OpenGL Game в Xcode:

- (void)update
{
    // get the size from the presentation layer, so that animating does not squish
    CALayer *presentationLayer = [self.view.layer presentationLayer];
    CGSize layerSize = presentationLayer.bounds.size;
    float aspect = fabsf(layerSize.width / layerSize.height);
    GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, 0.1f, 100.0f);

    ...
}
person Tark    schedule 11.09.2013
comment
Я не использую GLKit и не знаком с этим -update методом; он вызывается каждый кадр? - person Nicolas Miari; 11.09.2013
comment
да. Я предполагаю, что у вас есть цикл обновления и рендеринга для этого приложения, поэтому все, что вам нужно сделать, это получить размер уровня представления вашего CAEAGLLayer в цикле обновления и использовать его для расчета матрицы проекции. - person Tark; 11.09.2013
comment
Спасибо. Хотя я не использую GLKit прямо сейчас, и мое собственное решение, похоже, помогает, я принимаю ваш ответ, потому что он выглядит чистым, соответствует фреймворкам и может пригодиться в ближайшем будущем! - person Nicolas Miari; 11.09.2013
comment
Хороший трюк. И основная идея остается неизменной независимо от того, используете ли вы GLKit: каждый слой имеет presentationLayer, поэтому, что бы ни делал ваш код рисования, он может использовать размер этого слоя, чтобы определить соотношение сторон, в котором он должен рисовать. Однако вычисление матрицы проекции для каждого кадра является ненужным - если не убивающим производительность - вычислением. Вы можете подумать об условном обозначении этого, чтобы это происходило только во время вращения (начало и конец которого вы можете поймать в своем контроллере представления). - person rickster; 14.09.2013
comment
@rickster Я согласен с тем, что нет необходимости вычислять матрицу проекции на каждом этапе обновления, это взято непосредственно из шаблона Xcode в качестве простого примера. Я спорю, что это убийца производительности, это всего лишь один дубль и несколько делений и умножений. Если вам нужен простой способ проверить наличие анимации перед настройкой матрицы, вы можете проверить, не имеет ли свойство layer.animationKeys значение nil. - person Tark; 14.09.2013
comment
@Tark Я также согласен с тем, что вычисление матрицы проекции само по себе не должно снижать производительность; Однако я бы предпочел не загружать дополнительную униформу во все мои шейдеры в каждом кадре (не то чтобы я тестировал это или что-то в этом роде ...). - person Nicolas Miari; 16.09.2013
comment
Во всяком случае, я пробую этот подход. Я не использую GLKit, но я все еще использую EAGLLayer, и у меня есть метод -update :, так что я думаю, он должен работать. - person Nicolas Miari; 16.09.2013
comment
@Tark Итак, я попробовал. Если я создаю свои текстуры с помощью GL_LINEAR, швы (= темные линии сетки) между каждым квадратом моего слоя 2D-карты тайлов (обычная сетка с четырьмя ячейками, размер плитки = 32 точки) некоторое время отображаются во время вращения (как мерцающая сетка). Но если я установлю фильтр на GL_NEAREST, артефакт исчезнет. Поскольку вы упомянули, что ваша игра трехмерная, возможно, вы не сможете мне здесь помочь; Думаю, весь ваш UI - это одно-текстурированные квадроциклы ... - person Nicolas Miari; 17.09.2013
comment
Хорошо, это было на симуляторах с низким разрешением. На устройствах Retina швы до сих пор получаются даже с GL_LINEAR ... - person Nicolas Miari; 17.09.2013
comment
@NicolasMiari Похоже на проблему округления с плавающей запятой, вызванную матрицей проекции. Есть множество других сообщений с похожими проблемами, они, вероятно, могут помочь, например, stackoverflow.com/questions/1912836/ - person Tark; 18.09.2013
comment
Спасибо, проверю! - person Nicolas Miari; 18.09.2013