Mipmapping and NPOT

05 июня 2016 22:37
Привет!

Сева привет передал и сказал, что вы знаете способ мипмапить NPOT текстуры. Рассказывайте :)
06 июня 2016 11:33
Приветствую.

У нас текстуры подаются пользователем, а они, как правило, используют NPOT-текстуры. Чтобы избежать некачественного рендеринга при отсутсвии возможности mipmap-текстурирования или использования gl.REPEAT поведения (для нас это важно) можно использовать две описанные схемы. Сразу скажу, что суть этих методов сводится к подгону текстуры к ближайшей большей степени двойки. Избежать искажений, связанных с изменением соотношения сторон у изображения, помогают текстурные координаты, которые являются относительными. Примеры можно потестить, скачав SDK.

- Использовать канвас. Рисуем в канвас, который имеет размеры кратные степени двойки, изображение, потом его передать в texImage2D. Всё просто. Только канвас в различных браузерах может искажать цвет пикселя, особенно когда альфа канал в этом пикселе близок к нулю.

- Использовать чуть более сложную схему, которая описана ниже

1) создаём промежуточный фреймбуфер и промежуточную текстуру
if (!_w_framebuffer_tmp)
        _w_framebuffer_tmp = _gl.createFramebuffer();
if (!_w_texture_tmp)
        _w_texture_tmp = _gl.createTexture();


2) рисуем NPOT-изображение с его оригинальными размерами в эту промежуточную текстуру

3) биндим этот фреймбуфер
_gl.bindFramebuffer(_gl.FRAMEBUFFER, fbo);


4) биндим и инициализируем оригинальную текстуру, при этом передаём при отрисовке следующие параметры:
_gl.texImage2D(w_target, 0, _gl.RGBA, size_x, size_y, 0, _gl.RGBA,
            _gl.UNSIGNED_BYTE, null);

size_x, size_y - ближайшие большие не превышающие лимиты WebGL размеры текстуры, кратные степени двойки

5) аттачим оригинальную текстуру к промежуточному буферу
_gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0,
            w_target, w_texture, 0);

6) далее перекидываем изображение из промежуточной текстуры в оригинальную, используя примерно следующий шейдер (по текстурным координатам):
gl_FragColor = texture2D(u_color, v_texcoord);

В итоге мы получили текстуру, кратную степени двойки, и теперь можем генерировать для неё мипмапу
06 июня 2016 12:28
Ага, понял. Кажется, идейно методы эквивалентны. А как canvas искажает цвета? И как вообще с артефактами интерполяции при увеличении исходной текстуры?

Вообще, я о таком немного думал, но выглядит дороговато. У меня масштаб проблемы ~2000 текстур 256х256 на панораму, которые надо грузить с сервера и стримить на GPU, не тормозя при этом рендер. У вас, кстати, есть какой-то "стриминг" текстур? Вы как-то сталкиваетесь с похожими проблемами?
06 июня 2016 14:17

У вас, кстати, есть какой-то "стриминг" текстур? Вы как-то сталкиваетесь с похожими проблемами?

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

Могу в качестве примера разве что приверсти видео-текстуру. Указанный выше метод применяется и к ней. То есть при каждом обновлении видео, мы рисуем для неё POT-текстуру. Этот функционал активно тестился на мобильных платформах, критичного проседания fps не замечалось. Для статичных текстур достаточно сделать эту операцию один раз сразу после загрузки.

А как canvas искажает цвета?
При низком значении альфа канала для png изображения наблюдалось изменения цвета пикселя по сравнению с исходным (r,g,b каналы) после отрисовки в canvas на Firefox.


И как вообще с артефактами интерполяции при увеличении исходной текстуры?
При чтении из текстуры на этапе texture2D происходит линейная фильтрация, что эквивалентно низкочастотной фильтрации изображения. Артефактов не наблюдалось, используем этот алгоритм уже достаточно давно, тестили на довольно высокочастотных изображениях.

08 июня 2016 17:07
А я сделал для себя сервис пережатия изображений на php, используя библиотеку gd2.
Закидываем туда кучку картинок (до 20 шт. единоразово)
Получаем архив с картинками.
Получаю размер стороны изображения таким алгоритмом
private function getSize($size){
//Массив разрешений
for ($i = 1; $i <= 14; $i++){
$this->stepen_dva[] = pow(2, $i);
}

foreach ($this->stepen_dva as $new_size) {
if($new_size >= $size){
return $new_size;
}
}

return $this->stepen_dva[count($this->stepen_dva)-1];
}

Т.Е. увеличиваю в большую сторону
Корректно работает с png и jpg.

Почитав пост думаю переписать используя js и канвас, дабы не грузить сервер.
Не стой, где попало… Попадет еще раз.
http://naviris.ru/
08 июня 2016 17:32
Можно сделать проще же (js):

function nextPowOfTwo (x) {
    var result = 1;
    while (result < x) {
        result <<= 1;
    }
    return result;
}


14 июня 2016 16:05
Ответ на сообщение пользователя Кирилл Дмитренко
Можно сделать проще же (js):
Здорово! Не знал что в js есть побитовые операции.
Не стой, где попало… Попадет еще раз.
http://naviris.ru/
 
Пожалуйста, зарегистрируйтесь или войдите под своей учетной записью , чтобы оставлять сообщения.