Особенности программирования DirectX графики
Особенности программирования DirectX графики для
видео карт nVidia (по материалам nVidia)
Интро
Сегодня в эру Multimedia трудно создать качественный продукт без применения
современных технологий. Если для обычных офисных приложений достаточно
стандартного интерфейса, а навороченный даже мешает, то для всякого рода плееров
скины стали уже неписаным стандартом. Так же и программы трёхмерной графики -
если ранее вполне достаточно было создать "впечатление" трёхмерности, то сейчас
почти все претендуют на "фотографическое качество". На фоне всего этого
необходимо отметить постоянно повышающуюся производительность видео карт и
аппаратную поддержку всё большего количества операций. На сегодняшний день
существует две "стандартные" библиотеки работы с 3D графикой. Microsoft DirectX
и Silicon Graphics OpenGL. Это высокоуровневые, аппаратно независимые средства.
С одной стороны они предоставляют доступ к 3D ускорению, а с другой не
привязывают к конкретной железке. Конечно же жалко неправильно их использовать,
искусственно понижая производительность видео карты. В связи с этим хочу
представить перевод (немного вольный и дополненный собственным опытом)
официального руководства nVidia (мамы/папы знаменитых Riva TNT 1/2, GeForce
1/2/3) по программированию графики с использованием графики DirectX.
Итак, ваша программа работает медленно... Что значит медленно? Если у вас
прорисовывается меньше 3000-10000 полигонов в секунду, то это медленно. Либо у
вас может быть видео карта без 3D ускорения, но это не предмет данной статьи.
Поэтому, далее предполагается, что у вас приличный 3D ускоритель есть!
Возможные проблемы
Profiling показал, что это ваша программа.
Скорее всего, ваша программа что-то делает плохо... Или вы пытаетесь рисовать
слишком часто... Или вы делаете то, что за вас и так делает драйвер, например
отсечение или трансформации... Никогда не трансформируйте вертексы сами.
Желательно, так же, рисовать индексированные полигоны, так как при этом
количество трансформируемых вертексов может быть в несколько раз меньше.
Profiling показал, что это драйвер.
Значит, у вас хорошая программа :-). Либо вы неправильно используете методы
Lock, неправильно используете VertexBuffer-ы, у вас очень простая программа, где
нет ничего существенного кроме рисования (как многие примеры из DirectX SDK).
Возможно так же что это особенности драйвера. Неплохо было бы (если все
остальные рекомендации соблюдены) проверить программу на другой видео карте и
других драйверах. Не используйте стандартные драйвера от Microsoft. Нередко они
имеют ту же версию что и драйвера nVidia, но не поддерживают аппаратное
ускорение.
Возможно это код инициализации рисования кадра.
Следующие методы должны вызываться как можно реже: ValidateDevice,
CreateVertexBuffer, Optimize, CreateStateBlock. Рисование кадра выполняется
минимум 20-25 раз в секунду, а это - методы распределения видео памяти и,
следовательно, их применение будет по времени накладным. Нельзя однозначно
сказать что лучше сразу загрузить всё в видео память или загружать помаленьку.
Можно поступить так: Не загружать ничего во время рисования кадра, загружать
между кадрами. Неплохо также разбить одну большую 3D сцену на несколько
поменьше. В Quake например так ходишь между уровнями.
Не используйте Stencil буфер, если он вам не нужен.
Если вы очищаете только Z-буфер, то DirectX вынужден сохранять содержимое
Stencil буфера, даже если он вам не нужен и вы его не используете. Вместо
"тупой" записи значений, для каждого пикселя выполнятся цикл
чтение/изменение/запись (mov mem,reg/or reg,reg/mov reg,mem). Причём это, в
отличие от полной очистки, как правило, реализуется программно. Никогда не
очищайте экран рисованием полигонов! Это не лучший способ, так как вы не сможете
корректно очистить z-буфер.
Не рисуйте никогда то, что не должно быть видно!
Попытайтесь по возможности сократить количество полигонов отсылаемых вами для
прорисовки. Отсекайте (просто не рисуйте) то, что сзади, то, что к вам задней
(то есть невидимой) стороной, то, что слишком далеко. Если текстуры
высококачественные, то создавайте для них MipMap. Есть много алгоритмов
отсечения невидимых частей. Для начала, было бы неплохо включить какой-нибудь
простой алгоритм отсечения того, что сзади. Затем неплохо было бы использовать
какой-нибудь алгоритм умной прорисовки сцены такой, как например BSP деревья.
Не используйте большое количество Pixel/Vertex Shader-ов (от 4-х).
Это может быть гораздо медленнее, чем простая смена текстуры. И вообще не
факт, что это поддерживается аппаратно вашей видео картой.
Не рисуйте полигоны из системной памяти, используйте VertexBuffer для
всего.
Нет практически ни одного случая, когда рисование без использования
VertexBuffer могло привести к хорошим результатам. Изменение производительности,
наблюдавшееся мной, достигало 250 раз! Используйте также IndexBuffer и вообще
чем более "видео", та память в которой у вас данные, тем лучше.
Вы используете D3DLOCK_DISCARD и D3DLOCK_NOOVERWRITE неправильно.
Не используйте оба флага одновременно - будет использован
D3DLOCK_NOOVERWRITE. Как правило, вам нужен D3DLOCK_NOOVERWRITE. На самом деле,
вам это не нужно, поскольку модифицировать VertexBuffer во время рисования
очень, очень плохо.
Заполняйте VertexBuffer с нулевого вектора и далее, пока он не будет
заполнен.
Не оставляйте промежутков! Не используйте маленькие (меньше 100 вертексов) и
большие (больше 25000 вертексов) буферы. В маленький всё равно ничего серьёзного
не влезет, да и рисовать его отдельно не очень хорошо. А большой трудно
полностью, без промежутков, заполнить.
Если вы используете z-буфер,
То у него должна быть та же глубина что и у цвета, тот же размер что и у
Render Target Surface. Для оконных приложений размер Render Target это, как
правило, размер клиентской части окна, для полноэкранных - разрешение. То есть
если у вас 32-х битный цвет, то и z-буфер желательно иметь тоже 32-х битный.
Если у вас окно 546 на 234 то и z-буфер надо иметь такой же. Естественно, что
для оконного режима это не столь существенно.
Не рисуйте слишком сложные моделиНе пытайтесь рисовать в реальном
времени модели 3D редакторов, которые хорошо смотрятся только после 5-10
минутного рендеринга. Всегда помните - то, что вы пишите, хоть в минимальной
конфигурации, должно запускаться на практически любой машине. Неплохо
использовать несколько уровней качества прорисовки для аппаратуры разной
мощности. Очень неплохо использовать в тех же целях модели разной детализации.
Не используйте GDI.
Никогда не используйте GDI, особенно для рисования текста и Bitmap-ов. Не
рисуйте текст по буквам. Если уж очень приспичило рисуйте заранее (до основного
цикла) в буферный surface, а потом копируйте. Или вообще храните шрифт в виде
растра в файле. Несколько забавно когда с помощью GDI пытаются выводить
количество кадров в секунду. От того что вы начали писать на экране с помощью
GDI нечто наподобие 12.8fps, этот самый fps падает в несколько раз...
Не используйте W-буфер.
Он, как правило, программный, и не может быть использован вместе с
кубическими текстурами. Кстати о них, все не плоские текстуры лучше тоже не
использовать.
Не используйте Texture BumpMap.
Он аппаратно не поддерживается даже GeForce3, не говоря уже об S3. И вообще
не очень реалистично выглядит, как будто поверхность пластиковая. Даже не
представляю где он может понадобится. Добиться выпуклости за счёт увеличения
числа полигонов гораздо быстрее и красивее.
Если хотите хитро что-либо с чем-либо смешать.
Используйте альфа-канал и не используйте COLORKEY. Альфа-канал очень хорошая
вещь, если ею умело пользоваться. Например можно заполнять
значение альфа-канала исходя из RGB значений того же пикселя. Это для многих
изображений можно сделать заранее, например для всех спрайтов. А блиттинг с
учётом альфа-канала на многих картах уже аппаратно поддерживается.
Рисуйте минимум по 50-60 полигонов.
Лучше по 200-10000. Никогда не посылайте на отрисовку 1-10 полигонов. Лучше
их не нарисовать. Никогда не посылайте рисоваться столь малое количество
полигонов.
Освобождайте всё что только можно.
Чем больше видео памяти свободно, тем лучше. Особенно если используется много
текстур. В принципе DirectX сам распределяет всю память, за исключением
surface-ов, а они-то и забирают львиную долю. Поэтому нещадно выгружайте и
удаляйте всё что только можете. Метод Release должен стать вашим другом.
Работайте на максимальной частоте обновления монитора.
Помните, она теперь зачастую превышает 60 герц. Для современных мониторов она
доходит и до 120/140 герц и выше. Это не только улучшит качество изображения но
и позволит сделать более незаметными артефакты возникающие если вы не ждёте
обратного хода луча.
Редко меняйте текстуры и VertexBuffer.
Когда вы устанавливаете текущую текстуру или VertexBuffer, то они, как
правило, загружаются в видео память, а эта загрузка может занять много времени.
Менять текстуры хуже, чем VertexBuffer, поэтому лучше все полигоны отсортировать
по текстурам а потом по VertexBuffer-ам или даже не хранить в одном
VertexBuffer-е вертексы полигонов с разными текстурами.
Старайтесь не рисовать подряд полигоны с разным форматом вертекса.
На большинстве карт не очень критично, но всё же заметно. Рекомендуется также
не создавать в вертексе неиспользуемые поля.
Не используйте LOCALVIEWER.
Впрочем, это уже дело хозяйское, так сказать... За красоту надо платить.
Кстати говоря постарайтесь не просчитывать в каждом кадре те части сцены,
которые можно просчитать заранее. Это, например, освещение от статических
источников света.
Всегда используйте WRITEONLY/READONLY флаги.
Это сократит временные расходы на компрессию/декомпрессию при записи/чтении
VertexBuffer-ов и surface-ов если они не в заявленном формате (специфический для
данной видеокарты). Так как отследить подобные случаи нельзя, лучше использовать
Эти флаги всегда.
Обычно основной цикл пишется так:
while (continueMainLoop)
{
Physics_Of_The_Scene();
Some_AI_Or_Another_Scene_Changing();
Render_Triangles();
Copy_Back_Buffer();
Wait_Till_Time_For_One_Frame_Expired();
}
|
А должен так:
while (continueMainLoop)
{
Physics_Of_The_Scene();
Some_AI_Or_Another_Scene_Changing();
Wait_Till_Time_For_One_Frame_Expired();
Render_Triangles();
Copy_Back_Buffer();
}
|
Не читайте из видео памяти.
Это очень медленно даже на AGP видео картах. Процесс чтения из видеопамяти
может занимать в 2-10 раз больше времени чем запись в неё.
В заключение хотелось бы сказать что Direct3D это в основном средство
рендеринга в играх и подобных им программах. Его использование в них весьма
удобно так как в состав DirectX входят также DirectSound, DirectMusic,
DirectInput, а начиная с восьмой версии DirectShow. Подобное сочетание позволяет
полностью построить работоспособное приложения на основе одной группы библиотек.
Безусловно что в системах научной, высокоточной графики первенство остаётся за
мультиплатформенным OpenGL.
Автор: Роман Акопов
Источник: www.rsdn.ru
|