И матрицы проходят

О чем?

Рассмотрим то какими уроками наполнен сам сдк и попробуем создать чего-нибудь свое.

Что-нибудь такое вы сможете сделать после урока этой части:

Разглядываем примеры.

Построение примеров из «Tutorials» sdk пакета напоминает снежный ком, где каждый следующий файл это модификация предыдущего (всего их 6).

Первый пример показывает, как в созданное окно инициализировать DirectX. Думаю его объяснять не надо, тут все предельно понятно.

Второй пример показывает, как создавать vertex buffer и заносить в него точки для построения плоскостей. Вообще об этом можно прочитать в интернете много статей (очень много:) несколько сайтов в конце урока), но немного и я скажу.

В программах по 3d плоскость образуют только 3 точки и модель загружаемая из макса, если она polymesh, все равно преобразуется и квадраты разбиваются на треугольники, поэтому лучше перед экспортом (об этом подробнее будет далее по уроку) сетку преобразовать в mesh. Но это не обязательно если модель не анимируется, в обратном случае будет сбита нумерация точек сетки и анимация будет потеряна.

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

Функция для установки параметров перспективы, где второй параметр отвечает за перспективное искажение, вроде линзы в камере, в 3dsMax.

    D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/3, 1.0f, 1.0f, 100.0f );   

Функция для поворота координат по определенной оси. (timeGetTime() – возвращает текущее время выполнения программы, меняется с течением времени, зависит от времени в винде)

    D3DXMatrixRotationY( &matWorld, timeGetTime()/1000.0f );

Векторы с параметрами камеры:  

    D3DXVECTOR3 vEyePt( 0.0f, 3.0f,-5.0f );//точка где находится камера

    D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );//точка куда смотрит камера

    D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );//вектор для поворота по оси просмотра камеры

Пример четвертый показывает как ввести простое освещение. Впринципе для включения самого простого освещения надо только вписать следующюю строку, например в InitD3D():

g_pd3dDevice->SetRenderState( D3DRS_AMBIENT, 0xffffffff ); //Объекты осветит со всех сторон.

Если хотите создать точечный свет (вроде omni в 3dmax) введите следующее:

   //в начале кода, где объявляем переменные

   D3DLIGHT9 light;

   //в  InitD3D(), выделяем память под свет

   ZeroMemory(&light, sizeof(D3DLIGHT9));

   //устанавливаем тип свет, если выбирете DIRECTLIGHT то не забудьте указать точку куда он направлен,  

   //например — light.Direction = D3DXVECTOR3(0.0f,0.0f,0.0f);

   light.Type = D3DLIGHT_POINT;

   //выбираем точку расположения света

   light.Position.x = 15.5f;

   light.Position.y = 15.5f;

   light.Position.z = 0.5f;

  //устанавливаем яркость

   light.Diffuse.g = light.Diffuse.b = light.Diffuse.a= light.Diffuse.r = 0.1f;

   //можно ограничить дальность света

   //light.Range = 20.0f;

   //назначаем на индекс, в этом случае 0 (для всех источников света он должен быть разный!)

   g_pd3dDevice->SetLight(0,&light);

   //включаем свет

   g_pd3dDevice->LightEnable(0,true);

В пятом примере разбирается нанесение текстур на объекты, но тут мы объединим рассмотрение этого примера с шестым где загружается модель из файла *.x

Именно шестой пример мы будем модифицировать в дальнейшем.

Среди переменных сразу отметим следующие:

LPD3DXMESH                          g_pMesh                = NULL;      //  переменная для  хранения сетки

D3DMATERIAL9*                   g_pMeshMaterials  = NULL;     // переменная для  хранения материалов нашей сетки

LPDIRECT3DTEXTURE9*     g_pMeshTextures   = NULL;     // текстуры для сетки (аналогично 3dsmax`у)

DWORD                                 g_dwNumMaterials = 0L;          // число созданных материалов

Уже знакомая нам InitD3D() инициализирует все параметры dx и создает ambient-освещение.

И вот перед нами функция InitGeometry() которая и создает объект. Посмотрим на сам код и мои комментарии:

HRESULT InitGeometry()

{  //создаем текстурный буфер, который нужен для создания материалов для объекта (он их перехватит)

    LPD3DXBUFFER pD3DXMtrlBuffer;

    //грузим сетку из файла  Tiger.x в переменную g_pMesh. подробнее о флагах функции можете узнать в хелпе

    if( FAILED( D3DXLoadMeshFromX( L»Tiger.x», D3DXMESH_SYSTEMMEM,

                                  g_pd3dDevice, NULL,

                                  &pD3DXMtrlBuffer, NULL, &g_dwNumMaterials,

                                   &g_pMesh ) ) )

    {  // так как компилятор создает ехе в папке debug программа проверяет файл в папке выше по иерархии

        if( FAILED( D3DXLoadMeshFromX( L»..\\Tiger.x», D3DXMESH_SYSTEMMEM,

                                   g_pd3dDevice, NULL,

                                   &pD3DXMtrlBuffer, NULL, &g_dwNumMaterials,

                                   &g_pMesh ) ) )

        {

            MessageBox(NULL, L»Не найден tiger.x», L»Meshes.exe», MB_OK);

            return E_FAIL;

        }  }

 

    // Теперь мы получаем информацию о материалах и текстурах из pD3DXMtrlBuffer в который

   // они отловились когда мы грузили сетку

    D3DXMATERIAL* d3dxMaterials = (D3DXMATERIAL*)pD3DXMtrlBuffer->GetBufferPointer();

    //создаем массив материалрв и проверяем не произошло ли переполнения памяти

    g_pMeshMaterials = new D3DMATERIAL9[g_dwNumMaterials];

    if( g_pMeshMaterials == NULL )

        return E_OUTOFMEMORY;

    //создаем массив текстур и проверяем не произошло ли переполнения памяти

    g_pMeshTextures  = new LPDIRECT3DTEXTURE9[g_dwNumMaterials];

    if( g_pMeshTextures == NULL )

        return E_OUTOFMEMORY;

 

    //цикл загружает в память сам текстуры по перехваченным путям к ним

    for( DWORD i=0; i

    {

        // Копируем материал

        g_pMeshMaterials[i] = d3dxMaterials[i].MatD3D;

 

        // Установим ambient цвет для материала т.к D3DX этого не делал

        g_pMeshMaterials[i].Ambient = g_pMeshMaterials[i].Diffuse;

 

        g_pMeshTextures[i] = NULL;

        if( d3dxMaterials[i].pTextureFilename != NULL &&

           lstrlenA(d3dxMaterials[i].pTextureFilename) > 0 )

        {

            //создаем текстуру по параметрам из буфера, опять же в хелпе можно глянуть возможные флаги

            if( FAILED( D3DXCreateTextureFromFileA( g_pd3dDevice,

                                               d3dxMaterials[i].pTextureFilename,

                                               &g_pMeshTextures[i] ) ) )

            { //так как компилятор создает ехе в папке debug программа проверяет файл в папке выше по иерархии

                const CHAR* strPrefix = «..\\»;

                CHAR strTexture[MAX_PATH];

                StringCchCopyA( strTexture, MAX_PATH, strPrefix );

                StringCchCatA( strTexture, MAX_PATH, d3dxMaterials[i].pTextureFilename );

              

                if( FAILED(D3DXCreateTextureFromFileA( g_pd3dDevice,

                                                   strTexture,

                                                   &g_pMeshTextures[i] ) ) )

               {

                   MessageBox(NULL, L»не найдена текстура», L»Meshes.exe», MB_OK);

               }   }   }  }

    //освобождаем D3DX буфер

   pD3DXMtrlBuffer->Release();

    return S_OK;}

Надеюсь, с этим мы разобрались, осталось отобразить модель в функции render(), которая вызывается в цикле сообщений нашего окна.

        // В файле сетки могло находится несколько объектов и текстур к ним, так что в цикле

        //мы на них наносим текстуры и все отображаем в цикле

        for( DWORD i=0; i

        {   g_pd3dDevice->SetMaterial( &g_pMeshMaterials[i] );

            g_pd3dDevice->SetTexture( 0, g_pMeshTextures[i] );       

            //Отрисовываем Subset (один из объектов сцены из *.х)

            g_pMesh->DrawSubset( i ); }

 

Итак, мы изучили этот пример. Для интереса можете попробовать «поиграть»  с примером. Например, можно создать в максе файл, в котором будет целая сцена, например космическая станция, вокруг сфера вывернутая наизнанку с текстурой космоса. Теперь вы можете загрузить ее в пример и посмотреть на довольно интересную картинку (хотя все зависит от того, что туда занесли :)). Для того чтобы не было проблем с размерами, пока вы не знаете про масштабирование, занесите в сцену в 3dsMax файл «tiger.x» из примера (про то, как и чем, это сделать посмотри в конце урока).

Разделение.

Когда я рассматривал этот пример, я сразу решил, что когда и directx функции и функции окон находятся в одном файле, это не удобно, просто если их разделить, то лишние функции не будут мешать, а файл с функциями окон будет надолго отложен в сторону. Я перенес все функции графики в graphic_dx.cpp, в graphic_dx.h объявил используемые функции, а в файле meshes.cpp оставил все функции для окон. Хочу отметить, что разные программисты делают сейчас по-разному разделение на файлы *.h и *.cpp, например многие вообще оставляют  только один *.cpp, что вполне можно реализовать. Я делал оба варианта. Так же надо не забывать добавлять в новый файл следующие строки:

Например, у нас “файл geometry.h”

 

#ifndef  geometry _h

#define  geometry _h

//далее ваш код

#endif

Это избавит вас от ошибок в файлах *.obj проекта.

Понравилась статья? Поделиться с друзьями: