netlib.narod.ru< Назад | Оглавление | Далее >

11.3. Прогрессивные сетки

Прогрессивные сетки, представленные интерфейсом ID3DXPMesh, позволяют упрощать сетку путем последовательности преобразований слияния граней (edge collapse transformations, ECT). Каждое такое преобразование удаляет из сетки одну вершину и одну или две грани. Поскольку преобразование является обратимым (обратное преобразование называется расщеплением вершин (vertex split)), мы можем обратить процесс упрощения в обратную сторону и восстановить первоначальное состояние сетки. Конечно же мы не можем сделать сетку более детализированной чем оригинал; можно только упростить сетку и вернуть ее в исходное состояние. На рис. 11.2 показана сетка с тремя различными уровнями детализации (levels of detail, LOD): высоким средним и низким.


Рис. 11.2. Изображение сетки с тремя различными уровнями детализации

Рис. 11.2. Изображение сетки с тремя различными уровнями детализации


В основе прогрессивных сеток лежит таже самая идея, что и у детализируемых текстур. Работая с текстурами мы отметили, что нерационально использовать текстуры с большим разрешением для мелких удаленных объектов на которых дополнительные детали все равно будут невидимы. Тоже самое верно и для сеток; небольшим, удаленным сеткам не требуется такое же большое число граней, что и крупным, близким к зрителю сеткам, потому что дополнительные детали на мелких сетках невидимы. Таким образом мы можем прекратить тратить время на визуализацию моделей с большим количеством граней в тех случаях, когда достаточно более простой модели.

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

Обратите внимание, что мы обсуждаем не способы реализации прогрессивных сеток, а варианты использования интерфейса ID3DXPMesh. Тех читателей, кого интересуют детали реализации, мы отсылаем к посвященной прогрессивным сеткам статье на сайте Хьюджеса Хоппа http://research.microsoft.com/~hoppe/.

11.3.1. Создание прогессивной сетки

Мы можем создать объект ID3DXPMesh с помощью следующей функции:

HRESULT D3DXGeneratePMesh(
     LPD3DXMESH pMesh,
     CONST DWORD *pAdjacency,
     CONST LPD3DXATTRIBUTEWEIGHTS pVertexAttributeWeights,
     CONST FLOAT *pVertexWeights,
     DWORD MinValue,
     DWORD Options,
     LPD3DXPMESH *ppPMesh
);

11.3.2. Веса атрибутов вершин

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};

Рекомендуется всегда использовать значения по умолчанию, только если у приложения нет веских причин отказаться от них.

11.3.3. Методы ID3DXPMesh

Интерфейс ID3DXPMesh является наследником интерфейса ID3DXBaseMesh. Поэтому он наследует всю функциональность изученного нами ранее интерфейса ID3DXMesh, а также предоставляет следующие дополнительные методы (учтите, что это не полный список):

 

ПРИМЕЧАНИЕ
Особый интерес представляют методы SetNumFaces и SetNumVertices, поскольку они позволяют нам изменять уровень детализации сетки.

11.3.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

Рис. 11.3. Окно приложения Progressive Mesh



netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz