среда, 9 февраля 2011 г.

MD2 Converter

Перенёс наконец-то код загрузки MD2 на Direct3D 11.


Т. к. формат очень старый и в своём первоначальном виде плохо подходит для Direct3D 11, было решено написать конвертер, который брал бы исходные данные и перерабатывал их таким образом, чтобы можно было легко загружать их и оперировать ими на GPU. Для простоты формат я назвал U(nified)MD2.

Во-первых, вершины всех фреймов в формате ubyte собраны вместе и заливаются в один буфер вершин. Для расчёта промежуточного кадра буфер вершин подключается к input slot 0 и 1, для первого слота указывается смещение для текущего кадра, а для второго - смещение для следующего за текущим кадра. Позиция вершины рассчитывается в вершинном шейдере простой интерполяцией двух атрибутов.

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

Данные об анимациях и их длинах также были переработаны для более удобного чтения (нет необходимости в парсинге).

Сложнее всего было решить, как поступить с текстурными координатами. Из-за швов (seams) в MD2 моделях существуют вершины, которые имеют одинаковую позицию, но разные текстурные координаты, и использовать один индексный буфер в таком случае невозможно. Графические ускорители никогда не поддерживали несколько индексных буферов (по очевидным причинам - сложность pre- и post-TnL кэшей увеличивается), поэтому простых решений два: индексировать на CPU или размножать вершины с разными текстурными координатами. Т. к. модели в Quake II геометрически очень просты, формировать буфер на CPU легко, но это не интересно. Размножение вершин возможно и обычно так и поступают в стандартных моделях, но в данном случае используется key frame анимация с множеством копий одних и тех же вершин, и дублирование станет многократным - решение получается некрасивым.

Наиболее правильный подход в данном случае - размножить uv координаты так, чтобы на каждый треугольник приходилось по три uv координаты (т. к. текстурные координаты общие для всех кадров, их увеличение несущественно). Теперь в геометрическом шейдере можно использовать семантику SV_PrimitiveID для индексации буфера текстурных координат, который подключается как shader resource:

Buffer g_buffer : register(t0);
uint pid: SV_PrimitiveID;
...
float2 tex = g_buffer.Load(pid * 3 + i);

где i - номер вершины треугольника.

Внедрение простого геометрического шейдера в современных GPU не приводит к существенному падению производительности, поэтому можно смело использовать такой подход. Впрочем, в случае с MD2 это скорее просто спортивный приём :)

И последнее: в MD2 текстурные координаты хранятся в формате ushort (значения делятся на высоту и ширину скина, получается float), но конвертер преобразует их в half.

1 комментарий: