Загрузка...
 
Печать

DirectX Graphics (DirectX 8). Билборды (Billboards)


Для компиляции примеров на понадобится:

    • MS Visual C++ 2010 Express,
    • Microsoft DirectX SDK 8.

Всё легко гуглится + есть в разделе "Софт" нашего сайта.

Рис. 1 Билборды с деревьями всегда обращены лицевой поверхностью к наблюдателю, в независимости от его (наблюдателя) положения.
Рис. 1 Билборды с деревьями всегда обращены лицевой поверхностью к наблюдателю, в независимости от его (наблюдателя) положения.

Billboard — спрайт, постоянно повёрнутый лицом к камере (по аналогии с рекламными щитами на автодорогах, которые повёрнуты под наиболее выгодным углом).1

  • Может быть анимированным (пример - пар из трубы в игре Half-Life).

Billboarding - это крутая техника, позволяющая эффектно отображать 2D-объекты в 3D-пространстве.2
Например, такой сложный объект как дерево может быть сперва отрендерен в 3D-редакторе только с одной стороны и затем отрисован в виде текстуры, натянутой на полигональный объект прямоугольной формы. Данный объект с текстурой всегда повёрнут лицевой частью к наблюдателю (viewpoint). Таким образом, независимо от того, как повернётся взгляд наблюдателя, дерево всегда будет повёрнуто к нему "лицом" (См. Рис.1).
Игрокодеры испокон веков юзают билборды (Doom, Duke Nukem, Redneck Rampage и многие другие игры) т.к. с ними относительно легко работать. В большинстве классических игр все персонажи предварительно отрендерены в виде 2D-изображений (часто анимированных) и затем затекстурированы (texture-mapped) на полигональные прямоугольники. Часто процесс вращения билбордов в след за поворотом "головы" наблюдателя (viewer-а) отчётливо виден, что придаёт сцене некую комичность. В большинстве случаев это соответствует общей концепции "несерьёзности" происходящего на 3D-сцене игры.
Билбординг работает путём применения мировой матрицы (world matrix), которая определённым образом размещает полигон относительно вьюера. Для создания билборда достаточно взять готовую матрицу вида (view transformation matrix) и на её основе создать новую матрицу, с противоположными углами вращения. Да, именно вращения. Т.к. позиция объекта здесь роли не играет.

Создаём мировую матрицу билборда (world billboard matrix)

Способ 1

Первый способ создать мировую матрицу билборда (billboard world matrix, которую затем можно применить к мешу или полигональному объекту) - это использовать отрицательные (opposite) значения углов вида (view angles).
К примеру, допустим, что у нас уже настроен вершинный буфер и заполнен вершинами. Углы вида хранятся здесь как XRot, YRot и ZRot. Координаты билборда заданы как XCoord, YCoord, ZCoord. Вот так мы настраиваем матрицу, применяемую для рендеринга вершинного буфера билборда:

// g_pD3DDevice - предварительно созданный и проинициализированный объект устройства Direct3D.
D3DXMATRIX matBillboard;
D3DXMATRIX matBBXRot, matBBYRotm matBBZRot;
D3DXMATRIX matBBTrans;

// Создаём (мировую) матрицу билборда.

// Применяем противоположные (opposite, здесь отрицательные) значения углов вьюера для применения к текущему виду.
D3DXMatrixRotationX(&matBBXRot, -XRot);
D3DXMatrixRotationY(&matBBYRot, -YRot);
D3DXMatrixRotationZ(&matBBZRot, -ZRot);

// Применяем координаты позиции объекта билборда.
D3DXMatrixTranslation(&matBBTrans, XCoord, YCoord, ZCoord);

// Комбинируем матрицы:
D3DXMatrixIdentity(&matBillboard);
D3DXMatrixMultiply(&matBillboard, &matBillboard, &matBBTrans);
D3DXMatrixMultiply(&matBillboard, &matBillboard, &matBBZRot);
D3DXMatrixMultiply(&matBillboard, &matBillboard, &matBBYRot);
D3DXMatrixMultiply(&matBillboard, &matBillboard, &matBBXRot);

// Устанавливаем матрицу-результат в качестве действующей матрицы преобразований для объекта Direct3D.
g_pD3DDevice->SetTransform(D3DTS_WORLD);

// Продолжаем отрисовывать вершинный буфер, который сориентирован "лицевой" поверхностью
// к наблюдателю, но расположен по корректным координатам.


В конце данного фрагмента кода мировая матрица трансформаций (world matrix) настроена и готова к применению для рендеринга объекта билборда.

Способ 2

Второй способ создать мировую матрицу билборда (billboard world matrix) - это получить текущую матрицу вида (view matrix) у объекта устройства Direct3D и транспонировать (transpose; здесь инвертировать = inverse) её. Полученная транспонированная матрица будет поворачивать сразу все объекты сцены лицевой поверхностью к наблюдателю. Далее применяем полученную матрицу трансляции меша (mesh translation matrix) для корректного позиционирования меша (с билбордом) в 3D-сцене.
В следующем примере на основе матрицы вида, запрошенной у объекта устройства Direct3D, создаётся матрица билборда, которая затем применяется к объекту билборда.

// g_pD3DDevice - предварительно созданный и проинициализированный объект устройства Direct3D.

D3DXMATRIX matTrans, matWorld, matTransposed;

// Получаем текущую матрицу вида у объекта устройства Direct3D.
g_pD3DDevice->GetTransform(D3DTS_VIEW, &matTranspose);

// Создаём матрицу трансляции меша.
D3DXMatrixTranslation(&matTrans, XCoord, YCoord, ZCoord);

// Перемножаем матрицы для получения мировой матрицы трансформации (world transformation matrix).
D3DXMatrixMultiply(&matWorld, &matTranspose, &matTrans);

// Назначаем полученную мировую матрицу трансформации в качестве основной в объекте устройства Direct3D.
g_pD3DDevice->SetTransform(D3DTS_WORLD, &matWorld);

// Продолжаем отрисовку вершинного буфера.
// Полученный объект всё время повёрнут лицевой частью к наблюдателю и размещён по корректным координатам.

Пример приложения с билбордом (Win32, DirectX 8)

Перед началом проверь, что у тебя установлены следующие программные компоненты:

  • MS Visual C++ 2010;
  • DirectX SDK 8.1
  • Windows SDK (для программирования под Win7/8/10).

Все эти штуки:

  • + инструкции по их установке ты найдёшь в разделе "Софт" нашего сайта;
  • Бесплатны;
  • Без труда гуглятся.

Создаём Проект приложения

  • Создай пустой Проект с именем "Billboard01". Проект автоматически разместится внутри Решения с таким же именем.

Весь процесс подробно расписан в статье Настройка MS Visual C plus plus 2010 и DirectX SDK.

Добавляем в Проект WinMain.cpp

Для чистоты эксперимента мы создали пустой Проект, т.е. без каких-либо файлов в нём. Создадим единственный файл с исходным кодом WinMain.cpp.

  • В "Обозревателе решений" главного окна MSVC++2010 щёлкни правой кнопкой мыши по папке (в терминологии Майкрософт это не папки, а фильтры!) "Файлы исходного кода" Проекта Billboard01.
  • Во всплывающем меню Добавить->Создать элемент...
Добавляем исходный файл
Добавляем исходный файл
  • В появившемся окне выбери "Файл С++ (.cpp)" и в поле "Имя" введи WinMain.cpp.
  • Жмём "Добавить".

Image
Добавленный файл сразу откроется в правой части MSVC++2010.

  • В только что созданном и открытом файле WinMain.cpp набираем следующий код:
WinMain.cpp
/**************************************************
WinMain.cpp
Chapter 6 Billboard Demo

Programming Role-Playing Games with DirectX
by Jim Adams (01 Jan 2002)

Required libraries:
  D3D8.LIB and D3DX8.LIB
**************************************************/

// Include files
#include <windows.h>
#include <stdio.h>
#include "d3d8.h"
#include "d3dx8.h"

// Window handles, class and caption text
HWND          g_hWnd;
HINSTANCE     g_hInst;
static char   g_szClass[]   = "BillboardClass";
static char   g_szCaption[] = "Billboard Demo by Jim Adams";

// The Direct3D and Device object
IDirect3D8       *g_pD3D       = NULL;
IDirect3DDevice8 *g_pD3DDevice = NULL;

// The 3D vertex format and descriptor
typedef struct {
  FLOAT x, y, z;   // 3D coordinates
  FLOAT u, v;      // Texture coordinates
} s3DVertex;
#define FVF3D (D3DFVF_XYZ | D3DFVF_TEX1)

// The billboard vertex buffer and texture
IDirect3DVertexBuffer8 *g_pBillboardVB      = NULL;
IDirect3DTexture8      *g_pBillboardTexture = NULL;

// The floor vertex buffer and texture
IDirect3DVertexBuffer8 *g_pFloorVB      = NULL;
IDirect3DTexture8      *g_pFloorTexture = NULL;

// Function prototypes
int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int nCmdShow);
long FAR PASCAL WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

BOOL DoInit();
BOOL DoShutdown();
BOOL DoFrame();
BOOL SetupMeshes();

int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int nCmdShow)
{
  WNDCLASSEX wcex;
  MSG        Msg;

  g_hInst = hInst;

  // Create the window class here and register it
  wcex.cbSize        = sizeof(wcex);
  wcex.style         = CS_CLASSDC;
  wcex.lpfnWndProc   = WindowProc;
  wcex.cbClsExtra    = 0;
  wcex.cbWndExtra    = 0;
  wcex.hInstance     = hInst;
  wcex.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
  wcex.hCursor       = LoadCursor(NULL, IDC_ARROW);
  wcex.hbrBackground = NULL;
  wcex.lpszMenuName  = NULL;
  wcex.lpszClassName = g_szClass;
  wcex.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
  if(!RegisterClassEx(&wcex))
    return FALSE;

  // Create the Main Window
  g_hWnd = CreateWindow(g_szClass, g_szCaption,
        WS_CAPTION | WS_SYSMENU,
        0, 0, 400, 400,
        NULL, NULL,
        hInst, NULL );
  if(!g_hWnd)
    return FALSE;
  ShowWindow(g_hWnd, SW_NORMAL);
  UpdateWindow(g_hWnd);

  // Run init function and return on error
  if(DoInit() == FALSE)
    return FALSE;

  // Start message pump, waiting for signal to quit
  ZeroMemory(&Msg, sizeof(MSG));
  while(Msg.message != WM_QUIT) {
    if(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) {
      TranslateMessage(&Msg);
      DispatchMessage(&Msg);
    }
    if(DoFrame() == FALSE)
      break;
  }

  // Run shutdown function
  DoShutdown();
  
  UnregisterClass(g_szClass, hInst);

  return Msg.wParam;
}

long FAR PASCAL WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  switch(uMsg) {
    case WM_DESTROY:
      PostQuitMessage(0);
      return 0;
  }

  return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

BOOL DoInit()
{
  D3DPRESENT_PARAMETERS d3dpp;
  D3DDISPLAYMODE        d3ddm;
  D3DXMATRIX            matView, matProj;

  // Do a windowed mode initialization of Direct3D
  if((g_pD3D = Direct3DCreate8(D3D_SDK_VERSION)) == NULL)
    return FALSE;
  if(FAILED(g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm)))
    return FALSE;
  ZeroMemory(&d3dpp, sizeof(d3dpp));
  d3dpp.Windowed = TRUE;
  d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
  d3dpp.BackBufferFormat = d3ddm.Format;
  d3dpp.EnableAutoDepthStencil = TRUE;
  d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
  if(FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd,
                                  D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                  &d3dpp, &g_pD3DDevice)))
    return FALSE;

  // Set the render states
  g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
  g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
  g_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
  g_pD3DDevice->SetRenderState(D3DRS_ALPHAREF, 0x01);
  g_pD3DDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);

  // Create and set the projection transformation
  D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI/4, 1.0f, 1.0f, 1000.0f);
  g_pD3DDevice->SetTransform(D3DTS_PROJECTION, &matProj);

  // Create the meshes
  SetupMeshes();

  return TRUE;
}

BOOL DoShutdown()
{
  // Release textures and vertex buffers
  if(g_pBillboardTexture != NULL)
    g_pBillboardTexture->Release();

  if(g_pBillboardVB != NULL)
    g_pBillboardVB->Release();

  if(g_pFloorTexture != NULL)
    g_pFloorTexture->Release();

  if(g_pFloorVB != NULL)
    g_pFloorVB->Release();

  // Release device and 3D objects
  if(g_pD3DDevice != NULL)
    g_pD3DDevice->Release();

  if(g_pD3D != NULL)
    g_pD3D->Release();

  return TRUE;
}

BOOL DoFrame()
{
  D3DXMATRIX matView, matWorld;
  float      Angle;
  short      i, j;

  // Clear device backbuffer
  g_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA(0,64,128,255), 1.0f, 0);

  // Update the view position
  Angle = (float)timeGetTime() / 2000.0f;
  D3DXMatrixLookAtLH(&matView, &D3DXVECTOR3((float)cos(Angle) * 200.0f, 200.0f, (float)sin(Angle) * 200.0f), &D3DXVECTOR3(0.0f, 0.0f, 0.0f), &D3DXVECTOR3(0.0f, 1.0f, 0.0f));
  g_pD3DDevice->SetTransform(D3DTS_VIEW, &matView);

  if(SUCCEEDED(g_pD3DDevice->BeginScene())) {
    // Draw the floor
    g_pD3DDevice->SetStreamSource(0, g_pFloorVB, sizeof(s3DVertex));
    g_pD3DDevice->SetVertexShader(FVF3D);
    g_pD3DDevice->SetTexture(0, g_pFloorTexture);
    D3DXMatrixIdentity(&matWorld);
    g_pD3DDevice->SetTransform(D3DTS_WORLD, &matWorld);
    g_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);

    // Draw the billboards
    g_pD3DDevice->SetStreamSource(0, g_pBillboardVB, sizeof(s3DVertex));
    g_pD3DDevice->SetTexture(0, g_pBillboardTexture);

    g_pD3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
    D3DXMatrixTranspose(&matWorld, &matView);
    for(i=0;i<3;i++) {
      for(j=0;j<3;j++) {
        matWorld._41 = (float)i *  80.0f - 80.0f;
        matWorld._42 = 0.0f;
        matWorld._43 = (float)j *  80.0f - 80.0f;

        g_pD3DDevice->SetTransform(D3DTS_WORLD, &matWorld);
        g_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
      }
    }
    g_pD3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);

    // Clear the texture usage
    g_pD3DDevice->SetTexture(0, NULL);

    // End the scene
    g_pD3DDevice->EndScene();
  }

  // Display the scene
  g_pD3DDevice->Present(NULL, NULL, NULL, NULL);

  return TRUE;
}

BOOL SetupMeshes()
{
  BYTE *Ptr;
  s3DVertex BillboardVerts[4] = {
      { -42.0f, 80.0f, 0.0f, 0.0f, 0.0f },
      {  40.0f, 80.0f, 0.0f, 1.0f, 0.0f },
      { -40.0f,  0.0f, 0.0f, 0.0f, 1.0f },
      {  40.0f,  0.0f, 0.0f, 1.0f, 1.0f }
    };

  s3DVertex FloorVerts[4] = {
      { -100.0f, 0.0f,  100.0f, 0.0f, 0.0f },
      {  100.0f, 0.0f,  100.0f, 1.0f, 0.0f },
      { -100.0f, 0.0f, -100.0f, 0.0f, 1.0f },
      {  100.0f, 0.0f, -100.0f, 1.0f, 1.0f }
    };

  // Create vertex buffers and stuff in data
  // Billboard
  if(FAILED(g_pD3DDevice->CreateVertexBuffer(                 \
            sizeof(BillboardVerts), 0, FVF3D,                 \
            D3DPOOL_DEFAULT, &g_pBillboardVB)))
    return FALSE;
  if(FAILED(g_pBillboardVB->Lock(0,0, (BYTE**)&Ptr, 0)))
    return FALSE;
  memcpy(Ptr, BillboardVerts, sizeof(BillboardVerts));
  g_pBillboardVB->Unlock();

   // Floor
  if(FAILED(g_pD3DDevice->CreateVertexBuffer(                 \
            sizeof(FloorVerts), 0, FVF3D,                     \
            D3DPOOL_DEFAULT, &g_pFloorVB)))
    return FALSE;
  if(FAILED(g_pFloorVB->Lock(0,0, (BYTE**)&Ptr, 0)))
    return FALSE;
  memcpy(Ptr, FloorVerts, sizeof(FloorVerts));
  g_pFloorVB->Unlock();

  // Get textures 
  D3DXCreateTextureFromFile(g_pD3DDevice, "Floor.bmp", &g_pFloorTexture);
  D3DXCreateTextureFromFileEx(g_pD3DDevice, "Billboard.bmp", 
    D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_A1R5G5B5, 
    D3DPOOL_MANAGED, D3DX_FILTER_TRIANGLE, D3DX_FILTER_TRIANGLE,
    D3DCOLOR_RGBA(0,0,0,255), NULL, NULL, &g_pBillboardTexture);

  return TRUE;
}
  • Сохрани Решение (Файл -> Сохранить все).

Данный исходный код целиком взят из примера к книге Programming Role-Playing Games with DirectX by Jim Adams (01 Jan 2002). Автор прогал на MS Visual C++ 6.0 (вышла в свет в 1998 г.) c установленным DirecX SDK 8.0. И если в нашем случае в качестве Graphic API мы юзаем практически идентичный DirectX SDK 8.1, то IDE у нас совсем другая (MS Visual C++ 2010 Express Edition). Нет, не с целью усложнения задачи. Просто MS Visual C++ 6.0 в своё время стоила 500 USD. Сейчас её, в принципе, можно поискать в торрентах. (За всё, что ты там скачаешь, администрация Igrocoder.ru ответственности не несёт!) Но использование платного/пиратского софта не соответствует концепции сайта Igrocoder.ru .
Поэтому наш выбор:

  • Бесплатная IDE MS Visual C++ 2010 Express edition;
  • Бесплатный DirectX SDK 8.1 .

К концу данной статьи мы вновь докажем, что игрокодинг (под ОС Windows) в домашних условиях возможен. И для него нужны только компьютер с ОС Windows и доступ к Интернету.
Но вернёмся к нашему Проекту Billboard01. С момента выхода MS Visual C++ 6.0 прошло немало времени. С MSVC++2010 Express их разделяют аж 12 лет. Из-за этого вышеприведённый код (написанный в начале 2002 г.) на данном этапе в MSVC++2010 Express компилироваться не будет, выдавая многочисленные ошибки. Даже с настроенными путями к DirectX SDK.
Но мы это исправим.

Готовим Проект Billboard01 к компиляции

Для успешной компиляции изменим настройки (=свойства) текущего Проекта Billboard01, созданного в MSVC++2010. При этом сам код из книги 2002 года останется нетронутым.

Закрыть
noteОбрати внимание

Напомним, что такую настройку необходимо повторно проделывать при создании каждого нового Проекта. Ниже представлен алгоритм действий по настройке Проекта Billboard01, созданного в MSVC++2010 Express с применением DirectX SDK 8.1 . При создании приложений под платформы, отличные от Win32, либо применении более новых версий DirectX SDK, процесс конфигурирования Проекта может отличаться от приведённого ниже.

Указываем пути к DirectX SDK 8.1

Если попытаться скомпилировать Проект Billboard01 в таком виде, то ничего не выйдет.
В единственном файле исходного кода WinMain.cpp можно увидеть инклуды различных заголовочных файлов DirectX (весии 8).

Фрагмент WinMain.cpp
...
// Include files
#include <windows.h>
#include <stdio.h>
#include "d3d8.h"
#include "d3dx8.h"
...

На данном этапе MSVC++2010 ничего не знает об их местоположении. В статье Настройка MS Visual C plus plus 2010 и DirectX SDK мы указывали пути к DirectX SDK (версии 9 и выше). Для DirectX SDK 8 это делается аналогично. Начнём.

  • Убедись, что MSVC++2010 запущена и в ней открыт наш текущий Проект Billboard01.
  • Убедись, что DirectX SDK 8.1 установлен на компьютере и ты уверенно можешь назвать полный путь к его каталогу.

В Обозревателе решений видим: "Решение "Billboard01"", а строкой ниже жирным шрифтом название Проекта (тоже Billboard01).

  • Жмём правой кнопкой мыши по названию Проекта Billboard01. Во всплывающем меню выбираем пункт "Свойства". Или в Главном меню выбираем Проект->Свойства. Или нажимаем Alt+F7.

Image

  • В появившемся меню свойств проекта выбираем Свойства конфигурации -> Каталоги VC++. В правой части этой страницы расположены пути ко всевозможным каталогам. Здесь нас интересуют только 2 строки: Каталоги включения и Каталоги библиотек.


Указываем каталог включений (include) DirectX SDK 8.1

  • В меню свойств Проекта щёлкаем левой кнопкой мыши по пункту Каталоги включения. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."

Image

  • В появившемся меню "Каталоги включения" жмём кнопку "Создать строку" (с жёлтой папкой) и указываем полный путь к заголовочным файлам DirectX SDK 8.1 (include). В нашем случае это C:\DXSDK8\include. Можно просто выбрать каталог из дерева каталогов, нажав кнопку с троеточием, расположенную справа от строки ввода.

Image

  • Жмём "ОК".


Указываем каталог библиотек (lib) DirectX SDK 8.1

  • В меню свойств Проекта щёлкаем левой кнопкой мыши по пункту Каталоги библиотек. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."
  • В появившемся меню "Каталоги библиотек" жмём кнопку "Создать строку" (с жёлтой папкой) и указываем полный путь к 32-разрядным версиям файлов библиотек DirectX SDK 8.1 (lib). В нашем случае это c:\DXSDK8\lib\. Можно просто выбрать каталог из дерева каталогов, нажав кнопку с троеточием, расположенную справа от строки ввода.
  • Жмём "ОК".

Image

  • На Странице свойств тоже жмём "ОК".

  • Сохрани Решение (File -> Save All).

Готово.

Указываем пути к Windows SDK

Помимо указания путей к заголовкам (include) и библиотекам (lib) DirectX SDK, для любого DirectX-Проекта также необходимо указать пути к заголовкам (include) и библиотекам (lib) Windows SDK. Самое смешное, что в MS Visual C++ 2010 эти пути указываются по умолчанию для каждого создаваемого Проекта. В этом нетрудно убедиться, если ещё раз открыть Проект -> Свойства -> Свойства конфигурации - >Каталоги VC++ -> Каталоги включения -> Изменить. В окне "Каталоги включения" в нижней (недоступной для редактирования) части видим список "Унаследованные значения", где в третьей строке стоит значение:

$(WindowsSdkDir)include

В результате видим, что добавленные пути к DirectX SDK (в обоих окнах: include и lib) расположены вверху списка, а пути к Windows SDK - в недоступной области, на несколько строк ниже.
Но заголовочным файлам DirectX SDK 8.1 "жизненно важно", чтобы они включались после включений заголовков Windows SDK.

Закрыть
noteВажно!

Каталоги, пути к которым указаны в окнах "Каталоги включения" и "Каталоги библиотек" при компиляции считываются один за другим по списку сверху вниз. Поэтому для корректного указания путей надо разместить пути к Windows SDK выше, а к DirectX SDK 8.1 - ниже по списку (чтобы они считывались последними).

В данной ситуации мы не можем поднять пути к Windows SDK, прописанные по умолчанию при создании Проекта, т.к. они расположены в специальной нередактируемой области (ограничение бесплатной версии MSVC++2010 Express).
Но можем схитрить и добавить ещё раз пути к тем же самым каталогам Windows SDK, подняв эти строки выше строк DirectX SDK.
Выше мы указывали пути к DirectX SDK 8.1. Для путей к Windows SDK это делается аналогично. Начнём.

  • Убедись, что MSVC++2010 запущена и в ней открыт наш текущий Проект Billboard01.
  • Убедись, что Windows SDK (в нашем случае версия 7) установлен на компьютере и ты уверенно можешь назвать полный путь к его каталогу.

В Обозревателе решений видим: "Решение "Billboard01"", а строкой ниже жирным шрифтом название Проекта (тоже Billboard01).

  • Жмём правой кнопкой мыши по названию Проекта Billboard01. Во всплывающем меню выбираем пункт "Свойства". Или в Главном меню выбираем Проект->Свойства. Или нажимаем Alt+F7.

Image

  • В появившемся меню свойств проекта выбираем Свойства конфигурации -> Каталоги VC++. В правой части этой страницы расположены пути ко всевозможным каталогам. Здесь нас интересуют только 2 строки: Каталоги включения и Каталоги библиотек.


Указываем каталог включений (include) Windows SDK

  • В меню свойств Проекта щёлкаем левой кнопкой мыши по пункту Каталоги включения. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."

Image

  • В появившемся меню "Каталоги включения" жмём кнопку "Создать строку" (с жёлтой папкой) и указываем полный путь к заголовочным (include) файлам Windows SDK. В нашем случае (Win7 x64) это C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include. Можно просто выбрать каталог из дерева каталогов, нажав кнопку с троеточием, расположенную справа от строки ввода.

Image

  • Меняй порядок считывания каталогов включений с помощью кнопок с чёрными стрелками в верхней части окна "Каталоги вложений".

Каталог включений DirectX SDK 8.1 должен всегда стоять в самом конце списка, как на этом скриншоте:
Image

  • Жмём "ОК".


Указываем каталог библиотек (lib) Windows SDK

  • В меню свойств Проекта щёлкаем левой кнопкой мыши по пункту Каталоги библиотек. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."
  • В появившемся меню "Каталоги библиотек" жмём кнопку "Создать строку" (с жёлтой папкой) и указываем полный путь к папке с файлами библиотек (lib) Windows SDK. В нашем случае (Win7 x64) это C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Lib. Можно просто выбрать каталог из дерева каталогов, нажав кнопку с троеточием, расположенную справа от строки ввода.

Image

  • Меняй порядок считывания каталогов вложений с помощью кнопок с чёрными стрелками в верхней части окна "Каталоги вложений".

Каталог библиотек DirectX SDK 8.1 должен всегда стоять в самом конце списка, как на этом скриншоте:
Image

  • Жмём "ОК".
  • На Странице свойств тоже жмём "ОК".

  • Сохрани Решение (File -> Save All).

Готово.

Выбираем многобайтовую кодировку

Закрыть
noteПримечание

В MS Visual C++ 2010 в настройках по умолчанию стоит набор (кодировка) символов UNICODE. В MS Visual C++ 6.0 - напротив, по умолчанию стоит кодировка ANSI (многобайтовая). Данная настройка сильно влияет на типы используемых переменных, что приводит к заметным различиям в исходном коде.
Несмотря на то, что во всех случаях рекомендуется использовать кодировку UNICODE, поддерживаемую во всех современных ОС семейства MS Windows (начиная с Win 2000/XP), большинство книг по программированию игр на классическом C++ придерживаются именно многобайтовой кодировки. Чтобы сильно не переделывать исходные коды под UNICODE, все наши игровые Проекты мы настроим под многобайтовую кодировку. Для этого...

  • Убедись, что MSVC++2010 запущена и в ней открыт наш текущий Проект Billboard01.
  • В Обозревателе решений щёлкаем правой кнопкой мыши по названию Проекта Billboard01.
  • Во всплывающем контекстном меню выбираем "Свойства".
  • В появившемся окне установки свойств Проекта жмём Свойства конфигурации->Общие, в правой части в строке "Набор символов" выставляем значение "Использовать многобайтовую кодировку".

Image

  • Жмём ОК.
  • Сохрани Решение (Файл->Сохранить все)

Отключаем инкрементную компоновку (incremental linking)

Инкрементная компоновка призвана сократить время компилирования. Но на деле её присутствие часто вызывает ошибки вроде этой:

Error LNK1123: сбой при преобразовании в COFF: файл недопустим или поврежден

Отключается в свойствах открытого Проекта. Для MS Visual C++ 2010 порядок следующий:

  • Убедись, что MSVC++2010 запущена и в ней открыт наш текущий Проект Billboard01.
  • В Главном меню MS Visual C++ 2010 выбираем Проект -> Свойства (Project -> Properties).
  • В появившемся окне установки свойств Проекта жмём Свойства конфигурации -> Компоновщик -> Общие (Configuration Properties -> Linker -> General), в правой части в строке "Включить инкрементную компоновку" ставим значение Нет (/INCREMENTAL:NO).
  • Жмём ОК.

Прописываем библиотеки d3dx8.lib, d3d8.lib и Winmm.lib в окне "Дополнительные зависимости" (Additional dependencies) компоновщика (Linker)

Данный этап проходил даже автор кода Jim Adams в 2002 году, прогая на MSVC++6.0. В начальных комментариях листинга WinMain.cpp он намекнул, что библиотеки D3D8.LIB и D3DX8.LIB необходимо явно указывать в списке дополнительных зависимостей линкера (=компоновщика):

Фрагмент WinMain.cpp
/**************************************************
WinMain.cpp
Chapter 6 Billboard Demo

Programming Role-Playing Games with DirectX
by Jim Adams (01 Jan 2002)

Required libraries:
  D3D8.LIB and D3DX8.LIB
...

Библиотеки D3D8.LIB и D3DX8.LIB расположены в папке с установленным DirectX SDK 8 (в нашем случае по пути C:\DXSDK8\lib\), пути к которой мы прописали выше.
Т.к. мы создаём исполняемое приложение (исполняемый .exe-файл), а не библиотеку, то после компиляции полученный объектный модуль сразу линкуется путём вызова компоновщика (=linker). Так вот, этот самый компоновщик по ранее прописанным каталогам данные библиотеки не ищет. Поэтому их необходимо указывать отдельно в окне настроек Проекта, в разделе "Компоновщик".
ОК, начинаем.

  • Убедись, что MSVC++2010 запущена и в ней открыт наш текущий Проект Billboard01.
  • В Главном меню MS Visual C++ 2010 выбираем Проект -> Свойства (Project -> Properties).
  • В появившемся окне установки свойств Проекта последовательно щёлкаем по раскрывающимся ветвям иерархического дерева: Свойства конфигурации -> Компоновщик -> Ввод (Configuration Properties -> Linker -> Input).
  • В правой части, напротив строки "Дополнительные зависимости" жмём кнопку с чёрным треугольником.
  • В всплывающем списке жмём "Изменить".
  • В появившемся окне "Дополнительные зависимости" в верхнем поле ввода прописываем в столбик (один под другим) имена файлов трёх библиотек:

d3dx8.lib
d3d8.lib
Winmm.lib
Image
Библиотеку Winmm.lib прописываем, что называется, на всякий случай. Она расположена в каталоге с установленным Windows SDK.

  • Жмём ОК, ОК.
  • Сохрани Решение (Файл->Сохранить все)

Отключаем использование компоновщиком библиотеки libci.dll

Да, даже на данном этапе при компиляции Проект Billboard01 выдаст ошибку. Библиотека libci.dll использовалась в VisualStudio когда-то очень давно. И в современных версях IDE её нет. Тем не менее компоновщик почти всегда вызывает её при компиляции, ругаясь на её отсутствие. Самый простой способ это исправить - запретить использовать libci.dll по умолчанию.
ОК, начнём.

  • Убедись, что MSVC++2010 запущена и в ней открыт наш текущий Проект Billboard01.
  • В Главном меню MS Visual C++ 2010 выбираем Проект -> Свойства (Project -> Properties).
  • В появившемся окне установки свойств Проекта последовательно щёлкаем по раскрывающимся ветвям иерархического дерева: Свойства конфигурации -> Компоновщик -> Командная строка (Configuration Properties -> Linker -> Command Promt).

В правой части, внизу, видим поле ввода "Дополнительные параметры".

  • Пишем в него строку: /NODEFAULTLIB:libci
  • Жмём ОК.
  • Сохрани Решение (Файл->Сохранить все).

Компилируем Проект Billboard01

Наконец, наш тестовый Проект готов к компиляции.

  • Жми кнопку с зелёным треугольником на панели инструментов главного окна MSVC++2010 или F5 нак лавиатуре.

После компилирования приложение Billboard01 автоматически запустится и покажет окно. Согласно замыслу, в окне изображена вращающаяся поляна, на которой размещены несколько billboard-деревьев. Но на деле видим, что все объекты сцены не имеют текстур, т.к. их пока никто не готовил.

Рис. 2 Текстура травы.
Рис. 2 Текстура травы.
Рис. 3 Дерево на чёрном фоне. В коде WinMain.cpp есть функция отсечения по чёрному цвету альфа-канала.
Рис. 3 Дерево на чёрном фоне. В коде WinMain.cpp есть функция отсечения по чёрному цвету альфа-канала.

Готовим текстуры

Если ты внимательно изучил код WinMain.cpp данного примера, то наверняка обратил внимание на строку загрузки текстур в самом конце листинга:

Фрагмент WinMain.cpp
...
  // Get textures 
  D3DXCreateTextureFromFile(g_pD3DDevice, "Floor.bmp", &g_pFloorTexture);
  D3DXCreateTextureFromFileEx(g_pD3DDevice, "Billboard.bmp", 
    D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_A1R5G5B5, 
    D3DPOOL_MANAGED, D3DX_FILTER_TRIANGLE, D3DX_FILTER_TRIANGLE,
    D3DCOLOR_RGBA(0,0,0,255), NULL, NULL, &g_pBillboardTexture);

  return TRUE;
}

Здесь в качестве текстур используются файлы изображения с расширением .bmp. По умолчанию сразу после запуска программа ищет (в той же папке, что и исполняемый exe-файл) 2 файла текстур: Floor.bmp и Billboard.bmp .
Организуем их.

  • Создай в Фотошопе или найди в Интернете любые два изображения.

Первое с текстурой травы (grass). Второе - изображение дерева на чёрном фоне (См. Рис.2 и Рис.3). Желательно, чтобы изображения были небольшого размера и разрешения (например 512х512).

  • Конвертируй их в формат .bmp и переименуй в Floor.bmp и Billboard.bmp соответственно.
  • Размести эти bmp-файлы в папке с полученным исполняемым .exe-файлом (по умолчанию C:\Users\User1\Documents\Visual Studio 2010\Projects\Billboard01\Debug).

Именно здесь его будет по умолчанию искать наша программа.

  • Перезапусти приложение Billboard01.
Закрыть
noteОбрати внимание

Замечено, что в случае перекомпиляции и вызова приложения из MSVC++2010, текстуры часто не подгружаются. Поэтому просто открой Проводником Windows папку C:\Users\User1\Documents\Visual Studio 2010\Projects\Billboard01\Debug и запусти содержащейся в ней исполняемый файл Billboard01.exe.


Если взять в качестве изображения дерева взять Рис.3, то программа автоматически произведёт "обтравку" чёрного цвета по краям и мы увидим деревья, сквозь листву которых через маленькие отверстия видны другие объекты.

Заключение

Билбординг - это крутая техника, которая, в свою очередь, является основой для других техник, как например частицы (particles). С помощью общедоступных и совершенно бесплатных программных средств мы создали графическое приложение.
Все примеры из DirectX SDK 8 компилируются таким же образом.

Задание на дом

В папке с примерами (samples) DirectX SDK 8 есть пример "Billboard", также демонстрирующий технику билбординга. В нашем случае путь до него такой: C:\DXSDK8\samples\Multimedia\Direct3D\Billboard.

  • Найди в папке с данным примером файл исходного кода billboard.cpp.
  • Создай новый Проект Billboard02 в MSVC++2010 и скомпилируй исходный код, содержащийся в billboard.cpp.
  • Зрительно сравни исходные коды примеров техники билбординга Джима Адамса и MS DirectX SDK 8, выявив сходства и различия.

Источники:


1. https://directx.fandom.com/ru/wiki/DirectX_11_%D1%88%D0%B0%D0%B3_%D0%B7%D0%B0_%D1%88%D0%B0%D0%B3%D0%BE%D0%BC:Billboard
2. Adams J. Programming Role Playing Games with DirectX 8.0. - Premier Press. 2002

Contributors to this page: slymentat .
Последнее изменение страницы Вторник 16 / Февраль, 2021 13:00:55 MSK автор slymentat.

Помочь проекту

Яндекс-деньги: 410011791055108