3. Рисование в Direct3D

3.1. Подготовка к рисованию

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

   struct SimpleVertex
   {
        float x, y, z; //координаты
        static const DWORD FVF;
   };

    Константа FVF (Flexible Vertex Format) описывает формат хранения вершин.

    const DWORD SimpleVertex::FVF = D3DFVF_XYZ;//только координаты

    Затем, для конструирования сцены нам понадобятся три матрицы: матрица мира, матрица проекции и матрица вида. Рассмотрим назначение каждой из них.
    Во многих случаях удобнее каждый объект сцены задавать относительно своей системы координат. Но, в последствии, их все-равно нужно привести к единой (мировой) системе координат. За это отвечает матрица мира. Допустим, нам необходимо поместить некий объект в точку мирового пространства с координатами (x, y, z). Для этого мы должны написать следующий код:

    //создаем матрицу
    D3DXMATRIX WorldMatrix;
    //преобразуем её в матрицу перемещения
    D3DXMatrixTranslation(&WorldMatrix, x, y, z);
    //и задаем как мировую матрицу
    pDevice->SetTransform(D3DTS_WORLD, &WorldMatrix);

Затем, нам нужно указать, как наша сцена будет проецироваться на экранную плоскость. Для этого используется матрица проекции.

    //создаем матрицу
    D3DXMATRIX ProjMatrix;
    //заполняем её
    D3DXMatrixPerspectiveFovLH(&ProjMatrix,
          PI * 0.5f,//угол поля зрения
          (float)width / (float)height,//ширина экрана / высота экрана
          1.0,
          1000.0f);
    //задаем как матрицу проекции
    рDevice->SetTransform(D3DTS_PROJECTION, &ProjMatrix);

    И последнее, что нужно сделать для создания сцены - расположить в пространстве виртуальную камеру. Камера задается точкой местоположения и тремя векторами-направлениями, ориентирующими её в пространстве.

    //задаем соответствующие вектора
    D3DXVECTOR3 position(5.0f, 3.0f, –10.0f);
    D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
    D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
    //создаем матрицу
    D3DXMATRIX V;
    //инициализируем её
    D3DXMatrixLookAtLH(&V, &position, &target, &up);
    //и задаем как матрицу вида
    pDevice->SetTransform(D3DTS_VIEW, &V);

3.2. Использование буферов вершин и индексов

    Буферы вершин и индексов представляют собой непрерывные участки памяти. В буфере вершин хранится информация о каждой точке сцены. В буфере индексов хранится информация о том, как эти точки соединяются. В коде программы, буферы вершин и индексов задаются интерфейсами IDirect3DVertexBuffer9 и IDirect3DIndexBuffer9 соответственно. Рассмотрим функции создания буферов: HRESULT IDirect3DDevice9::CreateVertexBuffer() - для вершин, HRESULT IDirect3DDevice9::CreateIndexBuffer() - для индексов. Так как эти функции различаются только одним параметром, то рассмотрим их вместе.

    HRESULT IDirect3DDevice9::CreateVertexBuffer(
       UINT Length,
       DWORD Usage,
       DWORD FVF,
       D3DPOOL Pool
       IDirect3DVertexBuffer9** ppVertexBuffer,
       HANDLE* pSharedHandle);

    HRESULT IDirect3DDevice9::CreateIndexBuffer(
       UINT Length,
       DWORD Usage,
       D3DFORMAT Format,
       D3DPOOL Pool,
       IDirect3DIndexBuffer9** ppIndexBuffer,
       HANDLE* pSharedHandle);

    Length - размер буфера в байтах.
    Usage - параметр, указывающий особенности использования буфера.
    FVF - настраиваемый формат вершин.
    Pool - пул памяти, в который будет помещен буфер.
    Format - формат индексов.
    ppVertexBuffer (ppIndexBuffer) - адрес указателя на будер вершин (индексов).
    pSharedHandle - всегда NULL.

    Вот пример создания буфера вершин:

    IDirect3DVertexBuffer9* vb;
    pDevice->CreateVertexBuffer(
       8 * sizeof(SimpleVertex),//8 вершин
       0,
       D3DFVF_XYZ,
       D3DPOOL_MANAGED,
       &vb,
       0);

    Для доступа к буферу необходимо получить указатель на область памяти, в которой находится буфер. Этот указатель получается с помощью метода Lock();

    SimpleVertex* vertices;
    vb->Lock(0, 0, (void**)&vertices, 0);// заблокировать буфер
    //пишем данные в буфер
    vb->Unlock();// разблокировать буфер

3.3. Вывод на экран

    Рассмотрим код процедуры, выводящей нашу сцену на экран:

    void Draw(void)
    {
       //очистка экрана
       pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
          D3DCOLOR_COLORVALUE(0.0f,0.0f,0.0f,1.0f), 1.0f, 0);
       //мировая матрица
       D3DXMATRIX matWorld;
       //приводим ее к единичной и задаем как мировую
       D3DXMatrixIdentity(&matWorld);
       pDevice->SetTransform(D3DTS_WORLD, &matWorld);
       //начинаем визуализацию
       pDevice->BeginScene();
       //задаем буфер вершин
       pDevice->SetStreamSource(0, vb, 0, sizeof(SimpleVertex));
       //задаем буфер индексов
       pDevice->SetIndices(ib);
       //задаем формат вершин
       pDevice->SetFVF(SimpleVertex::FVF);
       //рисуем сцену
       pDevice->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, 8, 0, 12);
       //заканчивем визуализацию
       pDevice->EndScene();
       //производим блиттинг
       pDevice->Present(NULL, NULL, NULL, NULL);
    }

    Отдельного рассмотрения заслуживает только метод IDirect3DDevice9::DrawIndexedPrimitive.

    HRESULT IDirect3DDevice9::DrawIndexedPrimitive(
       D3DPRIMITIVETYPE Type,
       INT BaseVertexIndex,
       UINT MinIndex,
       UINT NumVertices,
       UINT StartIndex,
       UINT PrimitiveCount);

    Type - тип выводимого примитива (точка, линия, треугольник).
    BaseVertexIndex - смещение (в вершинах) относительно начального индекса первого объекта в потоке вывода.
    MinIndex - минимальное значение индекса, которое будет использовано.
    NumVertices - количество выводимых вершин.
    StartIndex - индекс, с которого начинается вывод.
    PrimitiveCount - количество выводимых примитивов.

    Пример приложения: "Куб"