Создание игр » DirectX 9, Featured » Vertex buffer, Index buffer практика
Vertex buffer, Index buffer практика
Что такое Vertex buffer и Index buffer я рассказал Вам в предыдущей статье. Так же мы рассмотрели в чём преимущества использования Vertex buffer и Index buffer по сравнению с рендерингом обычныых данных, лежащих просто в оперативной памяти. Теперь пришло время на практике разобраться как создавать Vertex buffer, как заполнить его данными, как указать формат этих данных, а так же то, как создать, заполнить и использовать Index buffer для получения наибольшего быстродействия, максимального использования кэша видео-карты, а та же уменьшения количества данных, занимаемых моделью. В принципе, то, что касается уменьшения объёма данных за счёт использования Index buffer, этот вопрос мы с Вами уже рассматривали в уроке Загрузка модели из 3d MAX – в нём мы как раз “оптимизировали” модель за счёт удаления дублирующихся vertex’ов и, тем самым, уменьшили объём памяти, занимаемый моделью. Теперь же давайте на практическом примере рассмотрим как нам сохранить нашу модель в памяти видео-карты и работать с моделью, которая хранится там, а не в обычной оперативной памяти.
Vertex buffer и Index buffer в модели
Добавим Vertex buffer и Index buffer в класс модели, а так же сделаем функции для их получения:
class CMesh { //! вертексы модели std::vector<VertPosNormalTc> m_vecVerts; //! фэйсы. по 3 индекса на каждый Face std::vector<WORD> m_vecFaces; //! Добавляет вертекс в массив и возвращает его индекс WORD AddVert(const VertPosNormalTc& vert); //! Добавляет вертекс и заносит его индекс в массив индексов void AddFaceVert(const VertPosNormalTc& vert); // index buffer LPDIRECT3DINDEXBUFFER9 m_pIB; // vertex buffer LPDIRECT3DVERTEXBUFFER9 m_pVB; void Release(); protected: friend class Graphics; LPDIRECT3DINDEXBUFFER9 GetIB() { return m_pIB; } LPDIRECT3DVERTEXBUFFER9 GetVB() { return m_pVB; } public: const std::vector<VertPosNormalTc>& GetVerts() { return m_vecVerts; } const int GetVertsCount() { return int(m_vecVerts.size()); } const std::vector<WORD>& GetIdx() { return m_vecFaces; } const int GetIdxCount() { return int(m_vecFaces.size()); } CMesh(void); virtual ~CMesh(void); virtual bool Load(const std::string& strFileName); }; |
Доработаем функцию загрузки модели, что бы она, при загрузке, создавала Vertex buffer и Index buffer (добавляем в конец функции CMesh::Load ) :
// создаём Vertex Buffer m_pVB = Graphics::get().CreateVertexBuffer( GetVertsCount()*sizeof(VertPosNormalTc), D3DUSAGE_WRITEONLY, D3DPOOL_MANAGED); if (m_pVB==NULL) // если ошибка return false; // создаём Index Buffer m_pIB = Graphics::get().CreateIndexBuffer( GetIdxCount()*sizeof(WORD), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED); if (m_pIB==NULL) // если ошибка { Release(); return false; } void* pData; if (FAILED(m_pVB->Lock(0, 0, &pData, D3DLOCK_DISCARD))) { Release(); return false; } memcpy(pData, &m_vecVerts[0], GetVertsCount()*sizeof(VertPosNormalTc)); m_pVB->Unlock(); if (FAILED(m_pIB->Lock(0, 0, &pData, D3DLOCK_DISCARD))) { Release(); return false; } return true; } |
А так же реализуем функцию Release(), которая будет освобождать созданные ранее ресурсы:
void CMesh::Release() { if (m_pVB) { Graphics::get().Release(m_pVB); m_pVB = NULL; } if (m_pIB) { Graphics::get().Release(m_pIB); m_pIB = NULL; } } |
Для создания и освобождения Vertex buffer и Index buffer нужно будет реализовать дополнительные функции в классе движка:
LPDIRECT3DINDEXBUFFER9 Graphics::CreateIndexBuffer(UINT Length, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool) { LPDIRECT3DINDEXBUFFER9 pIB = NULL; HRESULT hr = m_pd3dDevice->CreateIndexBuffer( Length, Usage, Format, Pool, &pIB, NULL); if (FAILED(hr)) return NULL; m_vecIB.push_back(pIB); return pIB; } bool Graphics::Release(LPDIRECT3DINDEXBUFFER9 pIB) { for(size_t i=0; i<m_vecIB.size(); i++) if (m_vecIB[i]==pIB) { // нашли VertexBuffer std::swap(m_vecIB[i], *m_vecIB.rbegin()); pIB->Release(); m_vecIB.pop_back(); // удалили его return true; } // мы не нашли такого IndexBuffer return false; } LPDIRECT3DVERTEXBUFFER9 Graphics::CreateVertexBuffer(UINT Length, DWORD Usage, D3DPOOL Pool) { LPDIRECT3DVERTEXBUFFER9 pVertexBuffer = NULL; HRESULT hr = m_pd3dDevice->CreateVertexBuffer( Length, Usage, 0, Pool, &pVertexBuffer, NULL); if (FAILED(hr)) return NULL; m_vecVB.push_back(pVertexBuffer); return pVertexBuffer; } bool Graphics::Release(LPDIRECT3DVERTEXBUFFER9 pVB) { for(size_t i=0; i<m_vecVB.size(); i++) if (m_vecVB[i]==pVB) { // нашли VertexBuffer std::swap(m_vecVB[i], *m_vecVB.rbegin()); pVB->Release(); m_vecVB.pop_back(); // удалили его return true; } // мы не нашли такого VertexBuffer return false; } |
В хидере движка вектора для хранения буферов объявлены вот так:
std::vector<LPDIRECT3DINDEXBUFFER9> m_vecIB; std::vector<LPDIRECT3DVERTEXBUFFER9> m_vecVB; |
Кроме того, меши теперь загружаются так же через движок:
CMeshPtr Graphics::LoadMesh( const std::string& szFileName ) { CMeshPtr pMesh = new CMesh(); if (!pMesh->Load(szFileName)) { delete pMesh; return NULL; } m_vecMeshes.push_back(pMesh); return pMesh; } |
Ну и сделал немного косметических правок, что бы было поудобнее работать с движком. В архивах всё есть, посмотрите сами. В этот раз получилось много кода, но мало слов… Вроде бы, комментариев по коду много, да и сам код не сложный – надеюсь, разобраться для Вас проблемы на составит. Если же составит (да и если нет тоже))) – как обычно, жду ваших комментов!
Проект с исходным кодом движка и игры
Демки нет – она внешне от предыдущей ничем не отличается.
Раздел: DirectX 9, Featured · Теги: Direct3D, DirectX, Оптимизация, Создание движка
Вячеслав, поправьте, пожалуйста, ссылку на архив, что-то не работает она
Прошу прощения, исправил.
А по DirectX 11 буферам будет такая же статья?
По DirectX 11 будет – когда найдётся время сделать уроки по этому самому DirectX 11. В данный момент, к сожалению, со временем некоторая напряжёнка
Вячеслав заметил что тут нет урока по таймерам
вот на всякий случай код который я склепал
.h file
class Timer
{
private:
DWORD startTime;
DWORD lastTime;
DWORD updateInterval;
DWORD currentTime;
public:
Timer(DWORD UpdateInterval):updateInterval(UpdateInterval),startTime(timeGetTime())
{}
bool UdateTimer();
};
.cpp file
bool Timer::UdateTimer()
{
DWORD differentTime = currentTime – lastTime;
if(differentTime >= updateInterval)
{
lastTime = currentTime;
return true;
}else
{
currentTime = timeGetTime();
return false;
}
return false;
}
использовать легко при создании указываешь время обновления и в нужном месте вызываешь UpdateTimer и проверяешь прошёл тот или иной отрезок времени
работает пока что у меня норм
и хочеца бы урок по выводу текста(ну когда будет время напишите пожалуйста)
Ок, сделаю такой урок. Спасибо за подсказку.
ага а то dx учу месяц и по френку луну как то трудновато
Я не понимаю в твоём примере какую роль играет vector массив вершин и индексов , ты так и не показал как именно удалить нужный мне буфер из контекста устройства, когда ты делаешь pIB->Release(); ты чистишь вообще все буферы. В реальности со сценой где есть разные 3d объекты, ты и я будем создавать буферы для каждого объекта и помещать их в контекст,
hr = d3d_Устройство->CreateBuffer( &bd, &InitData, &g_pIndexBuffer );
d3d_cont->IASetIndexBuffer( g_pIndexBuffer, DXGI_FORMAT_R16_UINT, 0 );
так вот мой вопрос как удалять этот буфер для определенного набора точек из контекста, вопрос по DX 11 , но сути не меняет это.