воскресенье, 31 января 2010 г.

Fast Prefiltered Lines

Наткнулся на эту технику в GPU Gems 2. Суть её в том, что она позволяет рисовать линии с лучшим сглаживанием, чем это позволяет стандартный антиализинг.

Chapter 22. Fast Prefiltered Lines
Fast Antialiasing Using Prefiltered Lines on Graphics Hardware

В основе лежит уравнение расстояния от точки до отрезка (луча). Растеризатор генерирует фрагмент "широкой" линии, рассчитывается расстояние фрагмента до бесконечно тонкого отрезка, затем делается выборка из предрассчитанной текстуры. Что побудило меня остановиться и реализовать этот алгоритм:

1) В оригинальном варианте вершины линии-квада и уравнения для его рёбер предлагается рассчитывать на CPU. Сегодня их можно перенести в geometry shader.
2) В Direct3D 10/11 нет прямой возможности рисовать сглаженные линии произвольной толщины. Единственное, что доступно - это флажок AntialiasedLineEnable структуры D3D1x_RASTERIZER_DESC.

Я думал по-быстрому реализовать алгоритм и двинуться дальше, но не тут-то было. Дело в том, что в GPU Gems не объясняется, почему уравнения рёбер рассчитываются именно таким образом. Например, неясно, откуда выводится 1 + ..., почему в числителе двойка, а в выражении под корнем, которое, как нетрудно догадаться, вычисляет длину линии, стоит минус вместо плюса. Особенно странно выглядит единица, которой нет в уравнениях, приведённых на странице MIT (вторая ссылка).

Я решил более детально разобраться в этих вещах, а пока реализовал простейший вариант, где рассчитывается расстояние до идеальной линии и используется его обратное значение в виде альфы фрагмента. Линии подаются на вход как LINELIST, копируются вершинным шейдером в геометрический, там линия "раздувается" в стрип из двух треугольников, рассчитывается уравнение прямой (в window space) и всё это передаётся в фрагментный шейдер. Ну а там уже рассчитывается расстояние до сгенерированного фрагмента. Смысл выборки из текстуры в том, что расстояние (и альфа) изменяются линейно, а с выборкой можно заставить их лечь на некую плавную кривую, сделать границы резче или размытее. Подготовка различных фильтров - это отдельная тема.

Скриншоты:


Верхний треугольник: антиалиазинг средствами Direct3D (AntialiasedLineEnable = true + blending).
Первый ряд: линии с шириной 1.3, 1.6, 2.0.
Второй ряд: линии с шириной 4.0, 6.0, 10.0.

Вариант с радиусом 1.0 практически неотличим от того, что рисует Direct3D, т. е. механизмы рисования схожие. Единственное, что нужно дополнительно реализовать - это аккуратные "колпачки" на кончиках линий, при которых они могли бы более-менее плавно перетекать одна в другую.

Комментариев нет:

Отправить комментарий