Введение в программирование трехмерных игр с DX9

       

Разделенный на сегменты буфер вершин


Рисунок 14.1. Разделенный на сегменты буфер вершин


Теперь создайте глобальную переменную i= 0 для отслеживания того, с каким сегментом мы работаем.

Для каждого кадра:

Обновите все частицы.

Пока не будут визуализированы все живые частицы:

Если буфер вершин не заполнен, то:

Блокируем сегмент i с флагом D3DLOCK_NOOVERWRITE.

Копируем 500 частиц в сегмент i.

Если буфер вершин заполнен, то:

Возвращаемся к началу буфера вершин: i = 0.

Блокируем сегмент i с флагом D3DLOCK_DISCARD.

Копируем 500 частиц в сегмент i.

Визуализируем сегмент i.

Переходим к следующему сегменту: i++

ПРИМЕЧАНИЕ

Это очень упрощенное описание, но оно иллюстрирует идею. Предполагается, что у нас всегда есть 500 частиц, чтобы заполнить целый сегмент, хотя в действительности это условие не выполняется потому что мы постоянно создаем и уничтожаем частицы и их количество меняется от кадра к кадру. Предположим, что у нас осталось только 200 частиц для копирования и визуализации в текущем кадре. Поскольку 200 частиц недостаточно для заполнения буфера вершин, в коде мы обрабатываем такой сценарий как особый случай. Данный сценарий может иметь место только в том случае, когда в текущем кадре заполняется последний сегмент. Если сегмент не последний, это значит что у нас есть по крайней мере 500 частиц для перехода к следующему сегменту.

ПРИМЕЧАНИЕ

Вспомните, что наш буфер вершин динамический, и поэтому мы можем пользоваться преимуществами, предоставляемыми флагами динамической блокировки D3DLOCK_NOOVERWRITE и D3DLOCK_DISCARD. Эти флаги позволяют блокировать часть буфера вершин, которая не визуализируется, в то время как остальные части буфера вершин будут продолжать визуализироваться. Например, предположим, что мы визуализируем сегмент 0; используя флаг D3DLOCK_NOOVERWRITE мы можем заблокировать и заполнить сегмент 1 в то время, когда визуализируется сегмент 0. Это позволяет предотвратить простои визуализации, которые возникали бы в ином случае.
Данный подход более эффективен. Во-первых, мы сокращаем размер необходимого нам буфера вершин. Во-вторых, теперь центральный процессор и видеокарта работают в унисон; то есть мы копируем небольшую партию частиц в буфер вершин (работа центрального процессора), а затем мы визуализируем эту партию частиц (работа видеокарты). Затем мы копируем в буфер вершин следующую партию частиц и рисуем ее. Это продолжается до тех пор, пока не будут визуализированы все частицы. Как видите, видеокарта больше не простаивает, ожидая пока не будет заполнен весь буфер вершин.

Теперь мы обратим наше внимание на реализацию этой схемы визуализации. Чтобы облегчить визуализацию системы частиц с помощью данной схемы мы будем использовать следующие члены данных класса PSystem:

_vbSize — Количество частиц, которые одновременно могут храниться в нашем буфере вершин. Это значение не зависит от количества частиц в конкретной системе частиц.

_vbOffset — Переменная хранит смещение в буфере вершин (измеряемое в частицах, а не в байтах), начиная с которого мы должны выполнять копирование очередной партии частиц. Например, если первая партия частиц заняла элементы буфера вершин с номерами от 0 до 499, то копирование следующей партии должно начинаться со смещения 500.

_vbBatchSize — Количество частиц в партии.

Теперь мы представим вам код метода визуализации:

void PSystem::render() { if(!_particles.empty()) { // Установка режимов визуализации preRender(); _device->SetTexture(0, _tex); _device->SetFVF(Particle::FVF); _device->SetStreamSource(0, _vb, 0, sizeof(Particle));

// Если мы достигли конца буфера вершин, // возвращаемся к его началу if(_vbOffset >= _vbSize) _vbOffset = 0;

Particle* v = 0;

_vb->Lock( _vbOffset * sizeof(Particle), _vbBatchSize * sizeof(Particle), (void**)&v, _vbOffset ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD);

DWORD numParticlesInBatch = 0;

// // Пока все частицы не будут визуализированы // std::list<Attribute>::iterator i; for(i = _particles.begin(); i != _particles.end(); i++) { if(i->_isAlive) { // // Копируем партию живых частиц в // очередной сегмент буфера вершин // v->_position = i->_position; v->_color = (D3DCOLOR)i->_color; v++; // следующий элемент;



numParticlesInBatch++; // увеличиваем счетчик партий

// партия полная? if(numParticlesInBatch == _vbBatchSize) { // // Рисуем последнюю партию частиц, которая // была скопирована в буфер вершин. // _vb->Unlock(); _device->DrawPrimitive( D3DPT_POINTLIST, _vbOffset, _vbBatchSize); // // Пока партия рисуется, начинаем заполнять // следующую партию частиц. // // Увеличиваем смещение к началу следующей партии

_vbOffset += _vbBatchSize;

// Проверяем не вышли ли мы за пределы буфера вершин. // Если да, то возвращаемся к началу буфера. if(_vbOffset >= _vbSize) _vbOffset = 0;

_vb->Lock( _vbOffset * sizeof(Particle), _vbBatchSize * sizeof(Particle), (void**)&v, _vbOffset ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD);

numParticlesInBatch = 0; // обнуляем количество частиц в партии }//конец инструкции if }//конец инструкции if }//конец инструкции for

_vb->Unlock();

// Возможно, ПОСЛЕДНЯЯ партия частиц начала заполняться, // но не была визуализирована, потому что условие // (numParticlesInBatch == _vbBatchSize) не было выполнено. // Сейчас мы нарисуем эту последнюю частично заполненную партию частиц if( numParticlesInBatch ) { _device->DrawPrimitive( D3DPT_POINTLIST, _vbOffset, numParticlesInBatch); }

// Следующий блок _vbOffset += _vbBatchSize;

postRender(); }//конец инструкции if }// конец метода render()


Содержание раздела