Что еще можно сказать, чего вы еще не знаете? Ну, способ рисования частиц является важным, если их тысячи. Т. к. мы не хотим иметь множество больших буферов вершин, использующих память, необходимо управлять ими, прежде чем двигаться дальше.

Управление буферами вершин означает, что необходимо блокировать и заполнять их кадр за кадром. Я знаю, я говорил, что вы не должны этого делать, но если сделать это правильно, можно эффективно управлять большим количеством частиц при использовании буферов вершин без возникновения задержек при их блокировании, загрузке и разблокировании.

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

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

Для каждого визуализируемого набора частиц необходимо разблокировать буфер вершин и визуализировать многоугольники. После визуализации необходимо по новой заблокировать буфер и продолжить. Предположим, вы используете метод визуализации частиц при помощи вершинного шейдера и частицы содержатся в связанном списке, на который указывает объект cParticle (pRoot). Следующий код иллюстрирует управление буферами вершин, визуализируя частицы группами.

// pRoot = корневой объект cParticle, содержащий все визуализируемые // частицы

// matView, matProj = преобразования вида и проекции

// pTexture = объект текстуры, используемый при визуализации частиц

// pShader, pDecl = объекты вершинного шейдера и объявления

// pVB, pIB = объекты буферов вершин и индексов

// NumParticles = количество частиц, которые могут находится в буфере вершин

// Установить вершинный шейдер, объявления и источники потоков pDevice->SetFVF(NULL); pDevice->SetVertexShader(pShader); pDevice->SetVertexDeclaration(pDecl) ;

pDevice->SetStreamSource(0, pVB, 0, sizeof (sShaderVertex)); pDevice->SetIndices(pIB);

// Включить альфа-тестирование

pDevice->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE) ; pDevice->SetRenderState(D3DRS_ALPHAREF, 0x08) ;

pDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL) ;

// Включить альфа-комбинирование (простого добавочного типа) pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE) ; pDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCCOLOR); pDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_DESTCOLOR) ;

/ / Установить текстуру pDevice->SetTexture(0, pTexture) ;

/ / Сохранить транспонированную матрицу вид*проекция в константах D3DXMATRIX matViewProj = (*matView) * (*matProj); D3DXMatrixTranspose(&matViewProj, &matViewProj); pDevice->SetVertexSnaderConstantF(0, (float*)&matViewProj, 4) ;

// Получить нормальные векторы вверх и вправо из преобразования вида D3DXVECTOR4 vecRight, vecUp;

// Вектор вправо явяляется первым столбцом D3DXVec4Normalize(svecRight, \

&D3DXVECTOR4(matView._11, \

matView._21, \

matView._31, 0.0f));

// Вектор вверх является вторым столбцом D3DXVec4Normalize(&vecUp, \

&D3DXVECTOR4(matView._12, \

matView._22, \

matView._32, 0.0f)); // Сохранить векторы в константах

pDevice->SetVertexShaderConstantF(4, (float*)&vecRight, 1) ; pDevice->SetVertexShaderConstantF(5, (float*)&vecUp, 1) ;

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

Замечание. Т. к. здесь буфер вершин постоянно блокируется, изменяется иразблокиру-ется, возможно вы захотите использовать флаг D3DUSAGEDYNAMIC при вызове IDirect3DDevice9::CreateVertexBuffer. Это позволит сообщить Direct3D, что с буфером будет проводиться многоработы, чтобы разместить его в легко доступной и эффективно используемой памяти.

/ / Начать с первой частицы в списке cParticle *Particle = pRoot;

// Установить количество для сброса буфера вершин DWORD Num = 0;

// Заблокировать буфер вершин sShaderVertex *Ptr;

pVB->Lock(0, 0, (void**)&Ptr, D3DLOCK_DISCARD);

// Просмотреть все частицы while(Particle != NULL) {

После начала цикла вы можете скопировать данные текущей вершины в буфер вершин

// Скопировать данные частиц в буфер вершин float HalfSize = Particle->m_Size / 2.0f;

Ptr[0].vecPos = Particle->m_vecPos;

Ptr[0].vecOffset = D3DXVECTOR2(-HalfSize, HalfSize); Ptr[0].Diffuse = Particle->m_Color ; Ptr[0].u = 0.0f ; Ptr[0].v = 0.0f;

Ptr[1].vecPos = Particle->m_vecPos;

Ptr[1].vecOffset = D3DXVECTOR2(HalfSize, HalfSize); Ptr[1].Diffuse = Particle->m_Color; Ptr[1].u = 1.0f; Ptr[1].v = 0.0f;

Ptr[2].vecPos = Particle->m_vecPos;

Ptr[2].vecOffset = D3DXVECTOR2(-HalfSize, -HalfSize); Ptr[2].Diffuse = Particle->m_Color; Ptr[2].u = 0.0f ; Ptr[2].v = 1.0f;

Ptr[3].vecPos = Particle->m vecPos;

Ptr[3].vecOffset = D3DXVECTOR2(HalfSize, -HalfSize); Ptr[3].Diffuse = Particle->m_Color; Ptr[3].u = 1.0f; Ptr[3].v = 1.0f;

Ptr+=4; // перейти к следующим четырем вершинам

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

// Увеличить количество вершин и очистить буфер, если он заполнен Num++;

if(Num >= NumParticles) {

// разблокировать буфер и визуализировать полигоны pVB->Unlock();

ppDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, \ 0, 0, Num*4, 0, Num*2) ;

/ / Еще раз блокировать буфер вершин pVB->Lock(0, 0, (void**)&Ptr, D3DLOCK_DISCARD);

// Обнулить количество вершин Num=0;

}

// Перейти к следующей частице Particle = Particle->m_Next;

}

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

// Разблокировать буфер вершин pVB->Unlock();

// Визуализировать полигоны, если они остались if(Num)

pDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, \ 0, 0, Num*4, 0, Num*2) ;

}

Вот и весь эффективный способ визуализации неограниченного количества частиц при помощи маленького буфера вершин! А теперь давайте объединим все рассмотренное ранее и создадим пару классов, которые помогали бы полностью управлять частицами.

Создание и уничтожение частиц || Оглавление || Управление частицами с помощью класса