понедельник, 14 января 2008 г.

Растительность. Часть 2. Отрисовка

46,91 КБ

После того как мы сгенерировали матрицы [link] пришло время заняться непосредственно рендером.

Рисовать мы будет пучками. Геометрия - знакомая многим "звёздочка". 4 плоскости или 8 полигонов.


Она же после альфа теста:


Увеличим плотность засейки. Вблизи смотриться неплохо:


Но если отодвинуть камеру то вылезут артефакты.

Артефакт 1. wrap


На самой верхушки пучков повылазили артефакты. Их природа довольно проста и лечится всё это дело выставлением clamp'а, вместо wrap в самплере.

Артефакт 2. mipmaps и алиасинг
Если отодвинуть камеру ещё дальше, то можно заметить что картинка поменялась в худшую сторону.


Что произошло? Трава стала менее прозрачной. Почему? Кто работал с альфатестом - знают в чём проблема.
Всё дело в mipmap уровнях( если кто не знает что это - [link] )

Рассмотрим мип-уровни куста( альфа канал ).


Как видно, уже на 5-ом уровне альфа канал теряет все детали и превращается в пятно. В результате дальние кусты травы( использующие 4+ мип уровни ) теряют прозрачность и становятся более "монолитными".

Что первое приходит на ум - отключить сам mipmaping, в результате чего будет использоваться текстура с максимальным разрешением. Но сделав это, мы получим очень неприятный алиасинг.

( на превьюшке плохо видно, лучше смотреть полноразмерный скрин )

Очевидно, что отказаться от мип-уровней мы не можем. Что делать? Можно исхитриться и разбить текстуру 2 части. Первая - диффузная, вторая - альфа маска. Поместив их в разные самплеры и отключив мип уровни только для альфы мы можем побороть алиасинг.

Но есть способ проще - достаточно грузить только первые 3-4 мип-уровня. Для загрузки текстур я использую функцию D3DXCreateTextureFromFileEx, которая позволяет задавать количество загружаемых мип-уровней.

Как видно, алиасинг исчез, проблемы с альфа тестом тоже.


Instancing
Очевидно что рисовать один куст за вызов слишком "дорого". К примеру, у меня в кадре может быть более 4000 кустов. Делать 4000 вызовов на отрисовку( dip ) будет слишком накладно. Посему будем использовать shader constant geometry instancing.

Как оно работает. Известно, что vertex shader 2_0 и выше может работать как минимум с 256 регистрами( каждый регистр - это 4 float ), в которые можно затолкать 64 матрицы 4х4. Оставим 16 регистров для других нужд( видовая матрица, освещение и т.д ) и "забёрём" остальные. Итого 60 матриц в нашем распоряжении.

Чтобы использовать instancing нужна "особая" геометрия. Если кратко - батчинг с индексом на каждый элемент. Если подробно - каждый куст мы описывает 16-ю вершинами и 24-мя индексами. Нам нужно увеличить вершинный и индексный буффер в 60 раз и заполнить их копиями оригинала. Примерно так:

for( unsigned int j = 1 ; j < 60 ; j++ ) {
for( unsigned int i = 0 ; i < 16 ; i++ ) {
vertex[ j * 16 + i ] = vertex[ i ];
}
}
_Winnie C++ Colorizer


С индексами нужно поступить аналогично. В дополнение к сделанному нужно расширить формат вершины. Если раньше она содержала только позицию и текстурные координаты, то теперь она дополнилась индексом батча. Т.е. каждая вершина в "пучке" знает к какому индексу она относится.

for( unsigned int j = 0 ; j < 60 ; j++ ) {
for( unsigned int i = 0 ; i < 16 ; i++ ) {
vertex[ j * 16 + i ].index = j;
}
}
_Winnie C++ Colorizer


Буфера "забатчены" и проиндексированы. Осталось отрисовать.
float4x4 g_vp          : register( c0  );
float4x4 g_world[ 60 ] : register( c16 );

void vs_main( inout float4 pos : POSITION ,
inout float2 tex : TEXCOORD0 ,
in float index : TEXCOORD1 )
{
float4 world_pos = mul( pos, g_world[ index ] );
pos = mul( world_pos, g_vp );
}
_Winnie C++ Colorizer


В итоге количество dip calls сократилось в 60 раз. 100 вызовов вместо 6000. Думаю, разница очевидна. FPS радостно подпрыгнул, что и требовалось.

BTW: в DirectX 9 SDK есть очень хороший пример с instancing.

Итого
Комбинируя разные типы растительность можно получить очень приятную картинку




По моему, вышло неплохо =)

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

Анонимно комментирует...

С удовольствием читаю твои статьи
Продолжай писать!

Timai комментирует...

zabivator

С удовольствием читаю твои статьи
Продолжай писать!


С удовольствием пишу свои статьи. Продолжайте читать!

спасиба, да =)