netlib.narod.ru | < Назад | Оглавление | Далее > |
Прогрессивные сетки, представленные интерфейсом ID3DXPMesh, позволяют упрощать сетку путем последовательности преобразований слияния граней (edge collapse transformations, ECT). Каждое такое преобразование удаляет из сетки одну вершину и одну или две грани. Поскольку преобразование является обратимым (обратное преобразование называется расщеплением вершин (vertex split)), мы можем обратить процесс упрощения в обратную сторону и восстановить первоначальное состояние сетки. Конечно же мы не можем сделать сетку более детализированной чем оригинал; можно только упростить сетку и вернуть ее в исходное состояние. На рис. 11.2 показана сетка с тремя различными уровнями детализации (levels of detail, LOD): высоким средним и низким.
Рис. 11.2. Изображение сетки с тремя различными уровнями детализации
В основе прогрессивных сеток лежит таже самая идея, что и у детализируемых текстур. Работая с текстурами мы отметили, что нерационально использовать текстуры с большим разрешением для мелких удаленных объектов на которых дополнительные детали все равно будут невидимы. Тоже самое верно и для сеток; небольшим, удаленным сеткам не требуется такое же большое число граней, что и крупным, близким к зрителю сеткам, потому что дополнительные детали на мелких сетках невидимы. Таким образом мы можем прекратить тратить время на визуализацию моделей с большим количеством граней в тех случаях, когда достаточно более простой модели.
Один из способов использования прогрессивных сеток — настройка уровня детализации сетки в зависимости от расстояния между ней и камерой. При уменьшении дистанции мы добавляем детали (треугольные грани) к сетке, а когда дистанция увеличивается — мы можем убирать детали.
Обратите внимание, что мы обсуждаем не способы реализации прогрессивных сеток, а варианты использования интерфейса ID3DXPMesh. Тех читателей, кого интересуют детали реализации, мы отсылаем к посвященной прогрессивным сеткам статье на сайте Хьюджеса Хоппа http://research.microsoft.com/~hoppe/.
Мы можем создать объект ID3DXPMesh с помощью следующей функции:
HRESULT D3DXGeneratePMesh( LPD3DXMESH pMesh, CONST DWORD *pAdjacency, CONST LPD3DXATTRIBUTEWEIGHTS pVertexAttributeWeights, CONST FLOAT *pVertexWeights, DWORD MinValue, DWORD Options, LPD3DXPMESH *ppPMesh );
pMesh — Исходная сетка на основании данных которой будет создаваться прогрессивная сетка.
pAdjacency — Указатель на массив значений типа DWORD, содержащий информацию о смежности граней сетки pMesh.
pVertexAttributeWeights — Указатель на массив элементов D3DXATTRIBUTEWEIGHTS размера pMesh->GetNumVertices(), в котором i-ый элемент соответствует i-ой вершине сетки pMesh и задает веса ее атрибутов. Веса атрибутов (attribute weight) используются при определении того какая именно вершина будет удалена при упрощении сетки. Вы можете передать в этом параметре ноль, и тогда для каждой вершины будут использованы веса атрибутов по умолчанию. Более подробно веса атрибутов и структура D3DXATTRIBUTEWEIGHTS обсуждаются в разделе 11.3.2.
pVertexWeights — Указатель на массив чисел с плавающей запятой размера pMesh->GetNumVertices(), в котором i-ый элемент соответствует i-ой вершине сетки pMesh и задает вес вершины. Чем больше вес вершины, тем меньше у нее шансов, что она будет удалена в процессе упрощения сетки. Вы можете передать в этом параметре ноль и тогда вес каждой вершины будет равен 1.0 (значение по умолчанию).
MinValue — Минимально возможное количество вершин или граней в сетке (что будет учитываться — вершины или грани — определяет следующий параметр Options) до которого может производиться упрощение. Обратите внимание, что это только желаемое значение и, в зависимости от весов вершин/атрибутов, параметры полученной в результате сетки могут не соответствовать этому значению.
Options — Один из членов перечисления D3DXMESHSIMP:
D3DXMESHSIMP_VERTEX — Указывает, что предыдущий параметр MinValue задает количество вершин.
D3DXMESHSIMP_FACE — Указывает, что предыдущий параметр MinValue задает количество граней.
ppPMesh — Возвращает созданную прогрессивную сетку.
typedef struct _D3DXATTRIBUTEWEIGHTS { FLOAT Position; FLOAT Boundary; FLOAT Normal; FLOAT Diffuse; FLOAT Specular; FLOAT Texcoord[8]; FLOAT Tangent; FLOAT Binormal; } D3DXATTRIBUTEWEIGHTS;
Структура данных с весами атрибутов вершины позволяет нам указать вес для каждого возможного компонента вершины. Значение 0.0 указывает, что данный компонент не обладает весом. Чем больше вес компонентов вершины, тем меньше вероятность, что данная вершина будет удалена при упрощении сетки. По умолчанию используются следующие значения:
D3DXATTRIBUTEWEIGHTS AttributeWeights; AttributeWeights.Position = 1.0; AttributeWeights.Boundary = 1.0; AttributeWeights.Normal = 1.0; AttributeWeights.Diffuse = 0.0; AttributeWeights.Specular = 0.0; AttributeWeights.Tex[8] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
Рекомендуется всегда использовать значения по умолчанию, только если у приложения нет веских причин отказаться от них.
Интерфейс ID3DXPMesh является наследником интерфейса ID3DXBaseMesh. Поэтому он наследует всю функциональность изученного нами ранее интерфейса ID3DXMesh, а также предоставляет следующие дополнительные методы (учтите, что это не полный список):
DWORD GetMaxFaces(VOID) — Возвращает максимальное количество граней, которое может быть в прогрессивной сетке.
DWORD GetMaxVertices(VOID) — Возвращает максимальное количество вершин, которое может быть в прогрессивной сетке.
DWORD GetMinFaces(VOID) — Возвращает минимальное количество граней, которое может быть в прогрессивной сетке.
DWORD GetMinVertices(VOID) — Возвращает минимальное количество вершин, которое может быть в прогрессивной сетке.
HRESULT SetNumFaces(DWORD Faces) — Данный метод позволяет задать количество граней до которого мы хотим упростить или усложнить сетку. Предположим, сетка состоит из 50 граней и мы хотим упростить ее до 30 граней; тогда нам следует написать:
pmesh->SetNumFaces(30);
Обратите внимание, что после изменения реальное количество граней сетки может отличаться от запрошенного. Если параметр Faces меньше, чем GetMinFaces(), он будет увеличен до GetMinFaces(). Аналогично, если Faces больше чем GetMaxFaces(), он будет уменьшен до GetMaxFaces().
HRESULT SetNumVertices(DWORD Vertices) — Метод позволяет задать количество вершин до которого мы хотим упростить или усложнить сетку. Предположим, сетка состоит из 20 вершин и мы хотим повысить уровень детализации, чтобы она содержала 40 вершин; тогда нам следует написать:
pmesh->SetNumVertices(40);
Обратите внимание, что после изменения реальное количество вершин сетки может отличаться от запрошенного. Если параметр Vertices меньше, чем GetMinVertices(), он будет увеличен до GetMinVertices(). Аналогично, если Vertices больше чем GetMaxVertices(), он будет уменьшен до GetMaxVertices().
HRESULT TrimByFaces( DWORD NewFacesMin, DWORD NewFacesMax, DWORD *rgiFaceRemap, // Данные о перемещении граней DWORD *rgiVertRemap // Данные о перемещении вершин );
Метод позволяет изменить минимальное и максимальное количество граней сетки, указав соответственно значения NewFacesMin и NewFacesMax. Обратите внимание, что новые значения должны попадать в существующий интервал от минимального до максимального значения, то есть находиться в пределах [GetMinFaces(), GetMaxFaces()]. Помимо этого функция возвращает информацию о перемещении вершин и граней. Данные о перемещении вершин и граней обсуждались в разделе 10.4.
HRESULT TrimByVertices( DWORD NewVerticesMin, DWORD NewVerticesMax, DWORD *rgiFaceRemap, // Данные о перемещении граней DWORD *rgiVertRemap // Данные о перемещении вершин );
Метод позволяет изменить минимальное и максимальное количество вершин сетки, указав соответственно значения NewVerticesMin и NewVerticesMax. Обратите внимание, что новые значения должны попадать в существующий интервал от минимального до максимального значения, то есть находиться в пределах [GetMinVertices(), GetMaxVertices()]. Помимо этого функция возвращает информацию о перемещении вершин и граней. Данные о перемещении вершин и граней обсуждались в разделе 10.4.
Приложение Progressive Mesh очень похоже на пример XFile, за исключением того, что в нем мы создаем и визуализируем прогрессивную сетку, которая, соответственно, будет представлена интерфейсом ID3DXPMesh. Пользователь может изменять уровень детализации сетки с помощью клавиатуры. Чтобы увеличить количество граней сетки надо нажать клавишу A, а чтобы удалить грани из сетки — нажать клавишу S.
Используемые в данном примере глобальные переменные похожи на глобальные переменные приложения XFile, за исключением того, что мы добавили переменную для хранения прогрессивной сетки:
ID3DXMesh* SourceMesh = 0; ID3DXPMesh* PMesh = 0; // прогрессивная сетка std::vector<D3DMATERIAL9> Mtrls(0); std::vector<IDirect3DTexture9*> Textures(0);
Вспомните, что для создания прогрессивной сетки нам надо указать исходную обычную сетку на основании данных которой будет сгенерирована прогрессивная сетка. Поэтому сперва мы загружаем данные из X-файла в объект ID3DXMesh с именем SourceMesh, и лишь потом создаем прогрессивную сетку:
bool Setup() { HRESULT hr = 0; // ...Код загрузки данных из X-файла в SourceMesh пропущен // // ...Извлечение материалов и текстур пропущено
Поскольку данный код аналогичен коду приложения XFile мы пропустили его. Теперь, когда у нас есть исходная сетка, мы можем создать из нее прогрессивную сетку с помощью следующего кода:
// // Создание прогрессивной сетки // hr = D3DXGeneratePMesh( SourceMesh, (DWORD*)adjBuffer->GetBufferPointer(), // смежность граней 0, // использовать веса атрибутов вершин по умолчанию 0, // использовать веса вершин по умолчанию 1, // упрощать насколько возможно D3DXMESHSIMP_FACE, // упрощать по числу граней &PMesh); d3d::Release<ID3DXMesh*>(SourceMesh); // закончили работу с исходной сеткой d3d::Release<ID3DXBuffer*>(adjBuffer); // закончили работу с буфером if(FAILED(hr)) { ::MessageBox(0, "D3DXGeneratePMesh() - FAILED", 0, 0); return false; }
Обратите внимание, что хотя мы и указали возможность упрощения сетки до одной грани, обычно такого упрощения не происходит из-за весов вершин и атрибутов; указание 1 приводит к упрощению сетки до минимально возможного разрешения.
Теперь прогрессивная сетка создана, но если мы ее визуализируем прямо сейчас, то она будет изображена с минимально возможным разрешением. Поскольку мы хотим визуализировать сетку с максимальным разрешением, необходимо установить его:
// установить максимальную детализацию DWORD maxFaces = PMesh->GetMaxFaces(); PMesh->SetNumFaces(maxFaces);
В функции Display мы проверяем нажатие клавиш A и S и обрабатываем их:
bool Display(float timeDelta) { if( Device ) { // // Обновление: Смена разрешения сетки // // Получаем текущее количество граней в сетке pmesh int numFaces = PMesh->GetNumFaces(); // Добавляем грань. Обратите внимание, что SetNumFaces() // автоматически корректирует значение, если оно // выходит за допустимые границы. if(::GetAsyncKeyState('A') & 0x8000f) { // Иногда из-за деталей внутренней реализации // интерфейса ID3DXPMesh, для того, чтобы инвертировать // преобразование слияния граней необходимо добавить // более одной грани. Другими словами, иногда // в результате добавления одной грани количество граней // сетки может остаться неизменным. В таком случае // для увеличения счетчика количества граней // необходимо добавить сразу две грани. PMesh->SetNumFaces(numFaces + 1); if(PMesh->GetNumFaces() == numFaces) PMesh->SetNumFaces(numFaces + 2); } // Удаляем грань. Обратите внимание, что SetNumFaces() // автоматически корректирует значение, если оно // выходит за допустимые границы. if(::GetAsyncKeyState('S') & 0x8000f) PMesh->SetNumFaces(numFaces - 1);
Код достаточно прямолинеен, следует только обратить внимание, что иногда для инверсии преобразования слияния граней необходимо добавить к сетке не одну грань, а две.
В завершение мы визуализируем объект ID3DXPMesh точно так же, как визуализировали объект ID3DXMesh. Кроме того, мы желтыми линиями рисуем треугольные ячейки сетки, для чего рисуем сетку в каркасном режиме установив материал желтого цвета. Мы делаем это для того, чтобы можно было видеть добавление и удаление отдельных треугольников при увеличении и уменьшении уровня детализации прогрессивной сетки.
Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0); Device->BeginScene(); for(int i = 0; i < Mtrls.size(); i++) { Device->SetMaterial(&Mtrls[i]); Device->SetTexture(0, Textures[i]); PMesh->DrawSubset(i); // рисуем каркас сетки Device->SetMaterial(&d3d::YELLOW_MTRL); Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); PMesh->DrawSubset(i); Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); } Device->EndScene(); Device->Present(0, 0, 0, 0); } return true; } // конец функции Display
Рис. 11.3. Окно приложения Progressive Mesh
netlib.narod.ru | < Назад | Оглавление | Далее > |