Сегодня в эру Multimedia трудно создать качественный продукт без применения современных технологий. Если для обычных офисных приложений достаточно стандартного интерфейса, а навороченный даже мешает, то для всякого рода плееров скины стали уже неписаным стандартом. Так же и программы трёхмерной графики - если ранее вполне достаточно было создать "впечатление" трёхмерности, то сейчас почти все претендуют на "фотографическое качество". На фоне всего этого необходимо отметить постоянно повышающуюся производительность видео карт и аппаратную поддержку всё большего количества операций. На сегодняшний день существует две "стандартные" библиотеки работы с 3D графикой. Microsoft DirectX и Silicon Graphics OpenGL. Это высокоуровневые, аппаратно независимые средства. С одной стороны они предоставляют доступ к 3D ускорению, а с другой не привязывают к конкретной железке. Конечно же жалко неправильно их использовать, искусственно понижая производительность видео карты. В связи с этим хочу представить перевод (немного вольный и дополненный собственным опытом) официального руководства nVidia (мамы/папы знаменитых Riva TNT 1/2, GeForce 1/2/3) по программированию графики с использованием графики DirectX.
Итак, ваша программа работает медленно... Что значит медленно? Если у вас прорисовывается меньше 3000-10000 полигонов в секунду, то это медленно. Либо у вас может быть видео карта без 3D ускорения, но это не предмет данной статьи. Поэтому, далее предполагается, что у вас приличный 3D ускоритель есть!
Скорее всего, ваша программа что-то делает плохо... Или вы пытаетесь рисовать слишком часто... Или вы делаете то, что за вас и так делает драйвер, например отсечение или трансформации... Никогда не трансформируйте вертексы сами. Желательно, так же, рисовать индексированные полигоны, так как при этом количество трансформируемых вертексов может быть в несколько раз меньше.
Значит, у вас хорошая программа :-). Либо вы неправильно используете методы Lock, неправильно используете VertexBuffer-ы, у вас очень простая программа, где нет ничего существенного кроме рисования (как многие примеры из DirectX SDK). Возможно так же что это особенности драйвера. Неплохо было бы (если все остальные рекомендации соблюдены) проверить программу на другой видео карте и других драйверах. Не используйте стандартные драйвера от Microsoft. Нередко они имеют ту же версию что и драйвера nVidia, но не поддерживают аппаратное ускорение.
Следующие методы должны вызываться как можно реже: ValidateDevice, CreateVertexBuffer, Optimize, CreateStateBlock. Рисование кадра выполняется минимум 20-25 раз в секунду, а это - методы распределения видео памяти и, следовательно, их применение будет по времени накладным. Нельзя однозначно сказать что лучше сразу загрузить всё в видео память или загружать помаленьку. Можно поступить так: Не загружать ничего во время рисования кадра, загружать между кадрами. Неплохо также разбить одну большую 3D сцену на несколько поменьше. В Quake например так ходишь между уровнями.
Если вы очищаете только Z-буфер, то DirectX вынужден сохранять содержимое Stencil буфера, даже если он вам не нужен и вы его не используете. Вместо "тупой" записи значений, для каждого пикселя выполнятся цикл чтение/изменение/запись (mov mem,reg/or reg,reg/mov reg,mem). Причём это, в отличие от полной очистки, как правило, реализуется программно. Никогда не очищайте экран рисованием полигонов! Это не лучший способ, так как вы не сможете корректно очистить z-буфер.
Попытайтесь по возможности сократить количество полигонов отсылаемых вами для прорисовки. Отсекайте (просто не рисуйте) то, что сзади, то, что к вам задней (то есть невидимой) стороной, то, что слишком далеко. Если текстуры высококачественные, то создавайте для них MipMap. Есть много алгоритмов отсечения невидимых частей. Для начала, было бы неплохо включить какой-нибудь простой алгоритм отсечения того, что сзади. Затем неплохо было бы использовать какой-нибудь алгоритм умной прорисовки сцены такой, как например BSP деревья.
Это может быть гораздо медленнее, чем простая смена текстуры. И вообще не факт, что это поддерживается аппаратно вашей видео картой.
Нет практически ни одного случая, когда рисование без использования VertexBuffer могло привести к хорошим результатам. Изменение производительности, наблюдавшееся мной, достигало 250 раз! Используйте также IndexBuffer и вообще чем более "видео", та память в которой у вас данные, тем лучше.
Не используйте оба флага одновременно - будет использован D3DLOCK_NOOVERWRITE. Как правило, вам нужен D3DLOCK_NOOVERWRITE. На самом деле, вам это не нужно, поскольку модифицировать VertexBuffer во время рисования очень, очень плохо.
Не оставляйте промежутков! Не используйте маленькие (меньше 100 вертексов) и большие (больше 25000 вертексов) буферы. В маленький всё равно ничего серьёзного не влезет, да и рисовать его отдельно не очень хорошо. А большой трудно полностью, без промежутков, заполнить.
То у него должна быть та же глубина что и у цвета, тот же размер что и у Render Target Surface. Для оконных приложений размер Render Target это, как правило, размер клиентской части окна, для полноэкранных - разрешение. То есть если у вас 32-х битный цвет, то и z-буфер желательно иметь тоже 32-х битный. Если у вас окно 546 на 234 то и z-буфер надо иметь такой же. Естественно, что для оконного режима это не столь существенно.
Никогда не используйте GDI, особенно для рисования текста и Bitmap-ов. Не рисуйте текст по буквам. Если уж очень приспичило рисуйте заранее (до основного цикла) в буферный surface, а потом копируйте. Или вообще храните шрифт в виде растра в файле. Несколько забавно когда с помощью GDI пытаются выводить количество кадров в секунду. От того что вы начали писать на экране с помощью GDI нечто наподобие 12.8fps, этот самый fps падает в несколько раз...
Он, как правило, программный, и не может быть использован вместе с кубическими текстурами. Кстати о них, все не плоские текстуры лучше тоже не использовать.
Он аппаратно не поддерживается даже GeForce3, не говоря уже об S3. И вообще не очень реалистично выглядит, как будто поверхность пластиковая. Даже не представляю где он может понадобится. Добиться выпуклости за счёт увеличения числа полигонов гораздо быстрее и красивее.
Используйте альфа-канал и не используйте COLORKEY. Альфа-канал очень хорошая вещь, если ею умело пользоваться. Например можно заполнять значение альфа-канала исходя из RGB значений того же пикселя. Это для многих изображений можно сделать заранее, например для всех спрайтов. А блиттинг с учётом альфа-канала на многих картах уже аппаратно поддерживается.
Лучше по 200-10000. Никогда не посылайте на отрисовку 1-10 полигонов. Лучше их не нарисовать. Никогда не посылайте рисоваться столь малое количество полигонов.
Чем больше видео памяти свободно, тем лучше. Особенно если используется много текстур. В принципе DirectX сам распределяет всю память, за исключением surface-ов, а они-то и забирают львиную долю. Поэтому нещадно выгружайте и удаляйте всё что только можете. Метод Release должен стать вашим другом.
Помните, она теперь зачастую превышает 60 герц. Для современных мониторов она доходит и до 120/140 герц и выше. Это не только улучшит качество изображения но и позволит сделать более незаметными артефакты возникающие если вы не ждёте обратного хода луча.
Когда вы устанавливаете текущую текстуру или VertexBuffer, то они, как правило, загружаются в видео память, а эта загрузка может занять много времени. Менять текстуры хуже, чем VertexBuffer, поэтому лучше все полигоны отсортировать по текстурам а потом по VertexBuffer-ам или даже не хранить в одном VertexBuffer-е вертексы полигонов с разными текстурами.
На большинстве карт не очень критично, но всё же заметно. Рекомендуется также не создавать в вертексе неиспользуемые поля.
Впрочем, это уже дело хозяйское, так сказать... За красоту надо платить. Кстати говоря постарайтесь не просчитывать в каждом кадре те части сцены, которые можно просчитать заранее. Это, например, освещение от статических источников света.
Это сократит временные расходы на компрессию/декомпрессию при записи/чтении 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.