Однажды, у меня появилась необходимость использования Direct3D совместно с Qt. После нескольких дней поисков в интернете, я нашел всего лишь какие-то обрывки информации. Полноценного описания механизма использования Direct3D не было нигде. В результате, после долгих изысканий я добился того, чего хотел:)
Под катом находится инструкция «сближения“ Direct3D и Qt, а так же код виджета, который можно использовать as is
При написании данной статьи использовались следующие средства:
Если Вы используете другую. IDE, то момент сборки будет несколько отличаться. Я привожу пример сборки, только для VS2008. Если вы уверены, что в вашей сборке присутствует поддержка direct3d engine, тогда пропустите шаг конфигурирования и сборки Qt.
Direct3DWidget.h
Direct3DWidget.cpp
Пример использования:
Результатом выполнения это кода будет вот такое окно(если его растянуть):

Казалось бы теперь есть все, чтобы спокойно использовать direct3d с отличным фреймворком Qt, но, к сожалению, мною были обнаружены некоторые подводные камни.
После того, как я стал использовать Direct3d, на некоторых компьютерах, вместо картинки отрисованной Direct3d был экран, зарисованный цветом фона. Я перебрал все комбинации аттрибутов, но убрать данный дефект так и не смог. Так же не удалось выявить причину неправильной работы direct3d engine на некоторых машинах. Т.к документация по Qt утверждает, что «This functionality is experimental.», то можно предположить, что дело в сыроватости поддержки direct3d.
Но, данный недостаток не может быть причиной не использования Direct3D:). Пока trolltech допиливает direct3d до stable state можно воспользоваться следующим workaround’ом:
Закомментируем вызов Rendering () в paintEvent:
и в место использования добавим следующий код:
я добавил этот код в код примера, приведенного мною выше.
Так же не забудьте включить заголовок QTimer
Приятного Вам использования Direct3d API в Qt, и не забывайте, что приложение, должно быть запущено с опцией -direct3d, я включил эту опцию в код моего примера использования.
Под катом находится инструкция «сближения“ Direct3D и Qt, а так же код виджета, который можно использовать as is
При написании данной статьи использовались следующие средства:
- Qt 4.5.0
- VS2008 SP1
- DirectX SDK August 2008
Если Вы используете другую. IDE, то момент сборки будет несколько отличаться. Я привожу пример сборки, только для VS2008. Если вы уверены, что в вашей сборке присутствует поддержка direct3d engine, тогда пропустите шаг конфигурирования и сборки Qt.
Конфигурирование и сборка Qt
- Первым шагом необходимо настроить переменные окружения для DirectX, для этого в консоли выполняем следующую команду:
> "%DXSDK_DIR%\Utilities\Bin\dx_setenv.cmd“ -
Переходим в каталог Qt и конфигурируем его. В моем случае это выглядело так > configure -direct3d -platform win32-msvc2008
-direct3d можно опустить, этот параметр должен быть вычислен конфигуратором.
Если все прошло нормально, то в появившемся выводе конфигуратора будет: Direct3D support…yes
- Делаем nmake, и если не будет проблем то дожидаемся конца сборки. Я столкнулся с проблемой, при сборке была ошибка unresolved external связанная с Direct3D. Чтобы ее исправить, идем в %QTDIR%\src\gui\ и в обоих Makefile’ах(release и debug) меняем dxguid.lib на "%DXSDK_DIR%\Lib\x86\dxguid.lib»(или x64 в зависимости от того, под какую платформу вы собираете). После этого проблема должна исчезнуть так что дожидаемся окончания сборки.
Код виджета
Direct3DWidget.h
- #ifndef DIRECT3DWIDGET_H
- #define DIRECT3DWIDGET_H
- #include <QtGui/QWidget>
- #include <d3d9.h>
- #include <atlbase.h>
- class Direct3DWidget : public QWidget
- {
- Q_OBJECT
- CComPtr<IDirect3D9> m_pD3D;
- CComPtr<IDirect3DDevice9> m_pd3dDevice;
- public:
- Direct3DWidget(QWidget *parent = 0);
- ~Direct3DWidget();
- bool InitDirect3D();
- public slots:
- bool Rendering();
- private:
- void paintEvent(QPaintEvent *pEvent);
- };
- #endif
Direct3DWidget.cpp
- #include "Direct3DWidget.h"
- Direct3DWidget::Direct3DWidget(QWidget *parent /* = 0 */): QWidget(parent)
- {
- setAttribute(Qt::WA_PaintOnScreen);
- }
- Direct3DWidget::~Direct3DWidget()
- {
- }
- bool Direct3DWidget::InitDirect3D()
- {
- m_pD3D = Direct3DCreate9( D3D_SDK_VERSION);
- if(!m_pD3D)
- return false;
- D3DPRESENT_PARAMETERS d3dpp = {0};
- d3dpp.Windowed = TRUE;
- d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
- d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
- d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
- d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
- d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
- d3dpp.EnableAutoDepthStencil = TRUE;
- d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
- HRESULT hrResult = m_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, winId(),
- D3DCREATE_HARDWARE_VERTEXPROCESSING,
- &d3dpp, &m_pd3dDevice );
- if(FAILED(hrResult))
- return false;
- return true;
- }
- bool Direct3DWidget::Rendering()
- {
- if(m_pd3dDevice == 0)
- return false;
- m_pd3dDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255, 255, 0), 1.0f, 0);
- if(SUCCEEDED(m_pd3dDevice->BeginScene()))
- {
- m_pd3dDevice->EndScene();
- }
- m_pd3dDevice->Present( 0, 0, 0, 0 );
- return true;
- }
- void Direct3DWidget::paintEvent(QPaintEvent *pEvent)
- {
- Q_UNUSED(pEvent);
- Rendering();
- }
Пример использования:
- #include <QtGui/QWidget>
- #include <QtGui/QApplication>
- #include <QtGui/QHBoxLayout>
- #include "Direct3DWidget.h"
- int main(int argc, char *argv[])
- {
- //------------------------------Start initialization
- //Additional parameters, just add a parameter separated by comma
- std::string aAdditionalParameters[] = {"-direct3d"};
- int iRealArgumentAmount = argc + sizeof(aAdditionalParameters)/sizeof(std::string);
- //This double pointer will contain parameters from argv and
- //statical parameters which necessary for this application
- char** pArguments = new char*[iRealArgumentAmount];
- //Copy all source(from the command line) parameters
- for(int i = 0; i < argc; ++i)
- {
- *(pArguments + i) = new char[ strlen(argv[i]) + 1 ];
- strcpy( *(pArguments + i), argv[i] );
- }
- //Append parameters we want to add
- for(int i = argc, j = 0; i < iRealArgumentAmount; ++i, ++j)
- {
- *(pArguments + i) = new char[ aAdditionalParameters[j].size() + 1 ];
- strcpy( *(pArguments + i), aAdditionalParameters[j].c_str() );
- }
- QApplication xApplication(iRealArgumentAmount, pArguments);
- for(int i = 0; i < iRealArgumentAmount; ++i)
- delete []*(pArguments + i);
- delete []pArguments;
- //--------------------------------Initialization complete
- QWidget xMainWindow;
- Direct3DWidget* xScene = new Direct3DWidget(&xMainWindow);
- QHBoxLayout* xMainLayout = new QHBoxLayout;
- xMainLayout->addWidget(xScene);
- xMainWindow.setLayout(xMainLayout);
- //It is important to initialize the Direct3d after the widget was embedded to the window
- xScene->InitDirect3D();
- xMainWindow.show();
- return xApplication.exec();
- }
Результатом выполнения это кода будет вот такое окно(если его растянуть):

Казалось бы теперь есть все, чтобы спокойно использовать direct3d с отличным фреймворком Qt, но, к сожалению, мною были обнаружены некоторые подводные камни.
После того, как я стал использовать Direct3d, на некоторых компьютерах, вместо картинки отрисованной Direct3d был экран, зарисованный цветом фона. Я перебрал все комбинации аттрибутов, но убрать данный дефект так и не смог. Так же не удалось выявить причину неправильной работы direct3d engine на некоторых машинах. Т.к документация по Qt утверждает, что «This functionality is experimental.», то можно предположить, что дело в сыроватости поддержки direct3d.
Но, данный недостаток не может быть причиной не использования Direct3D:). Пока trolltech допиливает direct3d до stable state можно воспользоваться следующим workaround’ом:
Закомментируем вызов Rendering () в paintEvent:
- void Direct3DWidget::paintEvent(QPaintEvent *pEvent)
- {
- Q_UNUSED(pEvent);
- //Rendering();
- }
и в место использования добавим следующий код:
- ...
- //It is important to initialize the Direct3d after the widget was embedded to the window
- xScene->InitDirect3D();
- xMainWindow.show();
- QTimer xTimer;
- QWidget::connect(&xTimer, SIGNAL(timeout()), xScene, SLOT(Rendering()));
- xTimer.start(100);
- return xApplication.exec();
- ...
я добавил этот код в код примера, приведенного мною выше.
Так же не забудьте включить заголовок QTimer
Приятного Вам использования Direct3d API в Qt, и не забывайте, что приложение, должно быть запущено с опцией -direct3d, я включил эту опцию в код моего примера использования.