• Устройство создано для использования в оконном режиме. Весь рендеринг будет направлен на окно m_hwndRenderTarget. Эта переменная соотвествует окну нашего вида C3DGraphFrame. Антиподом оконному режиму служит полноэкранный режим. Для работы с ним надо при создании устройства переменную Windowed установить в FALSE, а не в TRUE.
• Устройство имеет автоматически созданный depth-буфер, который иногда еще называют z-буфером. Формат z-буфера – 16 бит на точку. В наше время трудно (но, правда, можно) найти видеоадаптер, который не поддерживает такой формат z-буфера. Z-буфер используется вычислительным ядром Direct3D для хранения информации о текущей z-координате каждой точки картинной плоскости. В конечном счете это используется для удаления невидимых линий и поверхностей.
• 3D устройство должно использовать все возможности аппаратуры. Если какое-либо требуемое свойство не поддерживается, Direct3D попытается эмулировать его на программном уровне. Вы можете поменять тип устройства с D3DDEVTYPE_HAL на D3DDEVTYPE_REF. В этом случае все вычисления будут проводиться на программном уровне. Такая эмуляция является очень медленной, и не все на свете можно эмулировать.
• Обрабатываться вершины (vertexes) должны на программном уровне. Мы сделали этот выбор, указав значение D3DCREATE_SOFTWARE_VERTEXPROCESSING. Можно указать вместо этого D3DCREATE_MIXED_VERTEXPROCESSING или D3DCREATE_HARDWARE_VERTEXPROCESSING, но эти режимы поддерживаются далеко не всеми видеокартами.
Стоит заметить, что реальное приложение (игра, например) должны вначале анализировать возможности установленного оборудования, а затем уже принимать решение о возможности или, наоборот, невозможности продолжать работу. Для упрощения такая проверка в демо-приложении не делается.
От f(x,y)=sin(x+y) до 2D картинки
Жизнь всегда была сложнее, чем хотелось бы программисту. Например, все было бы гораздо проще, если бы существовала некая функция ХочуЧтобыНарисовалсяГрафикФункции(). Но во-первых, по-русски функции называть нельзя, во-вторых, такой функции просто нет. Все, что умеет IDirect3DDevice8, это нарисовать некоторые примитивы, да и для этого надо проделать кучу подготовительной работы. Как минимум, вы должны разместить координаты примитивов в вертекс-буфер ("vertex buffer"). Полное обсуждение вертекс-буферов находится за пределами этой статьи и могу лишь сказать, что это некая абстракция простого блока памяти, предназначенного для хранения координат и свойств точек трехмерного пространства. Работа с буфером осуществляется через интерфейс IDirect3DVertexBuffer8. Вы может заблокировать вертекс-буфер, получив при этом указатель на область памяти. Разыменовав указатель, можно записать что-нибудь в буфер, не забыв потом его разблокировать. В демо-приложении вы можете увидеть как это делается.
Обычно перед рисованием чего-либо мы хотим очистить кадр, чтобы новый кадр не накладывался на предыдущий. Это делается вызовом метода IDirect3DDevice8::Clear(). Вы найдете это в функции C3DGraphic::ReRender():
hr = m_p3DDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, m_dwBackColor, f, 0);
if (FAILED(hr)) {
return hr;
}
Мы здесь очищаем весь back-буфер, заполняя его цветом m_dwBackColor. Кроме этого, очищается z-буфер – он заполняется значением 1.0. 1.0 соответствует максимально дальней от наблюдателя плоскости (горизонт, грубо говоря), 0.0 – максимально ближней. Непосредственно рисование на 3D устройстве начинается с
m_p3DDevice->BeginScene();
а заканчивается строкой
m_p3DDevice->EndScene();
Все, что находится между этими вызовами символизирует анимацию очередного кадра. Обратите внимание на то, что мы постоянно употребляем термин "back-буфер". Совершенно верно! Все, что мы сейчас нарисовали, не видно на экране, оно существует пока только в памяти компьютера (не будем уточнять, в какой именно: системной или видео – очень тонкий вопрос). Для того, чтобы эти изменения перенеслись на экран монитора необходимо скопировать изображение из back-буфера на основную поверхность. Это делается вызовом функции IDirect3DDevice8::Present().
Следующий код обеспечивает непосредственно отрисовку изображения трехмерной функции:
hr = m_p3DDevice->SetStreamSource(0, m_pDataVB, sizeof(GRAPH3DVERTEXSTRUCT));
if (FAILED(hr)) {
return hr;
}
hr = m_p3DDevice->SetVertexShader(D3DFVF_GRAPH3DVERTEX);
if (FAILED(hr)) {
return hr;
}
hr = m_p3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, m_dwElementsInVB – 2);
if (FAILED(hr)) {
return hr;
}