пятница, 17 февраля 2012 г.

Bicubic Texture Filtering

Всю последнюю неделю я занимался программируемой фильтрацией текстур. А началось с того, что я решил из спортивного интереса не преобразовывать .pcx текстуры из Quake II в RGBA (моветон) и не пережимать их в BC (с потерей деталей, которых и так почти нет) а загружать  в видеопамять таблицу индексов и палитру и делать выборку из палитры прямо в шейдере. Всё бы хорошо, но при таком раскладе нельзя использовать стандартную фильтрацию текстур, т. к. в этом случае будут усредняться индексы палитры, а не сами цветовые значения, что является ошибкой. Возможное решение - сначала делать выборку из палитры, а потом вручную выполнять билинейную фильтрацию.

Решено - пишем собственную билинейную фильтрацию (а заодно и бикубическую). У NVIDIA есть .fx файл с реализацией бикубической фильтрации и пример с реализациями различных фильтраций на CUDA. Взяв их за основу и прочитав кое-какую информацию о бикубическом ресемплинге, я написал собственные реализации.

В кубической фильтрации используется формула Митчелла-Нетравали, сплайном можно управлять с помощью двух параметров - B и C. Наиболее правильная фильтрация - по Митчеллу, с параметрами B=1/3, C=1/3, но параметры можно подбирать и произвольно. Ступенчатость лучше всего сглаживается при параметрах B=1, С=0, ценой "замыливания" изображения. NVIDIA использует иную формулу, которая идентична B=0, C=3/4 при A=-3/4 (см. GPU Gems).


Некоторые заметки о ходе разработки.

Для билинейной фильтрации необходимы четыре выборки, но если использовать Gather4 из sm 5.0, можно обойтись только тремя (для RGB компонент). При сравнении аппаратной билинейной фильтрации с реализованной вручную выявилось интересная особенность: похоже, все видеокарты AMD выполняют билинейную фильтрацию со слегка сниженной точностью, т. к. на экстремально маленьких текстурах заметен бандинг. Оптимизация в принципе оправдана, т. к. на больших по размеру текстурах сниженная точность незаметна. На NVIDIA отличий между аппаратной и программной реализациями нет.

При отладке бикубической фильтрации выявились странные различия в семплировании текстур с точечной выборкой между всеми видеокартами и Reference Rasterizer. Они проявлялись в виде паразитных линий, идущих параллельно границам текселей текстуры. Первоначально отлаженный на GTX 560 шейдер работал неправильно на HD 4330, HD 48xx и HD 68xx и refrast-e. Чтобы исправить работу под старшими моделями AMD и refrast-ом, экспериментальным путём было установлено, что из текстурных координат нужно вычесть смещение в 1/512, а для серии HD 4xxx - смещение в 1/128. Само по себе это странно, и сами числа наводят на мысли - это первый случай, когда я наткнулся на такие различия. С этим надо что-то сделать. Попытка отладки шейдера в PIX-е показала, что PIX показывает финальный output пиксельного шейдера, отличный от того, что записан в render target. Приехали.

Текущая реализация бикубической фильтрации использует 16 выборок на пиксель. В CUDA-примере от NVIDIA показано, что можно уложиться в четыре выборки, но пока неясно, насколько это "честно". Есть куда копать.

И наконец, почему бикубическая фильтрация до сих пор не реализована аппаратно? (Ok, похоже, что реализована, но D3D11 доступ к ней не предоставляет). Ответ прост: она требует значительно больше вычислительных ресурсов, при этом не даёт заметного преимущества в качестве при использовании текстур большого размера. Т. к. все современные видеокарты обладают значительным количеством локальной видеопамяти, большие текстуры используются повсеместно. Впрочем, для RAGE Кармак обещал бикубическую фильтрацию (там некоторые текстуры неоправданно пожаты).

Ссылки на примеры с исходным кодом на Google Code:

cubic-plot.zip
bicubic.zip

Update Feb 25, 2012

Переписал шейдеры таким образом, чтобы семплирование текстуры осуществлялось через метод Texture.Load() вместо point-семплирования. В этом случае GPU работает чётко с целочисленными адресами, что, как и ожидалось, привело к отсутствию ошибок и идентичному результату на всех видеокартах.

bicubic-2.0.zip

3 комментария:

  1. Надо же, ещё в 2008-ом году встретил такую проблему: Интересности текстурной выборки на картах АТИ.
    Только в моём случае результат REF rast соответствовал результату NVidia, а не AMD.

    А по поводу аппаратной реализации бикубической фильтрации, в видеокартах AMD есть интересный регистр: HD 6900 Series Instruction Set Architecture, page 33
    Вероятно, поддержка есть, но не открыта через API.

    ОтветитьУдалить
  2. Ага, почитал, интересно. А как ты тогда решил проблему? Оффсеты какие-нибудь подошли? Я подбирал экспериментально и выкинулись как видишь интересные цифры. Похоже виноват refrast, а в AMD просто подогнали результаты под него. Ещё можно попробовать использовать Load() с ручным клампом вместо nearest фильтрации, может это уберёт проблему.

    Пейпр пролистал, спасибо. Ещё интересно что у них есть фильтрация для Cleartype :)

    ОтветитьУдалить
  3. Для решения тогда тоже подбирал оффсет к координатам.

    ОтветитьУдалить