这段时间正在做DX11的图形渲染,打算做一个模型动画预处理的编辑界面,对各种C++ GUI SDK的选择,MFC在Windows中本来是首选,但是自己并不是很熟悉MFC编程,所以我考虑使用我比较熟悉的Qt来做界面,从网上搜索使用Qt窗口显示DX的方法,经过一番尝试大致也找到了具体的做法。
环境:Qt5.0.1 + VS2010 + DirectX11
在Direct3D 11创建渲染设备里面的 IDXGISwapChain*swapChain_;中指定D3D11设备的输出窗口swapChainDesc_.OutputWindow 指定为Qt窗口的HWND即可,其他的地方与正常创建设备一样。
具体步骤如下:
创建一个Widget类
class D3DRenderWidget : publicQWidget { Q_OBJECT public: D3DRenderWidget(QWidget *parent = NULL); ~D3DRenderWidget(); virtualQPaintEngine* paintEngine()const { return NULL; } protected: virtual void resizeEvent(QResizeEvent *e); virtual void paintEvent(QPaintEvent *e); };
然后在D3DRenderWidget的构造函数中,添加窗口的两个属性。并加入一个QTimer来驱动这个Widget的painEvent事件一直运行。
D3DRenderWidget::D3DRenderWidget(QWidget*parent) : QWidget(parent) { setAttribute(Qt::WA_PaintOnScreen, true); setAttribute(Qt::WA_NativeWindow, true); QTimer* timer = newQTimer(this); connect(timer, SIGNAL(timeout()),this,SLOT(update())); }
接下来就可以创建设备了,找到这个Widget的HWND,并将这个HWND用作去D3D11设备的创建。
如何得到这个Widget的HWND呢?在网上搜了下,在Qt5以上,主要下面两种方式:
一个是直接将Widget中的WinId()方法返回的值强制转换为HWND
hwnd = (HWND)WinId();
另外一种是使用下面的函数:
staticQWindow* windowForWidget(const QWidget* widget) { QWindow* window =widget->windowHandle(); if(window) returnwindow; constQWidget* nativeParent = widget->nativeParentWidget(); if(nativeParent) returnnativeParent->windowHandle(); return0; } HWND getHWNDForWidget(const QWidget* widget) { QWindow* window =windowForWidget(widget); if (window&& window->handle()) { QPlatformNativeInterface* interface =QGuiApplication::platformNativeInterface(); returnstatic_cast<HWND>(interface->nativeResourceForWindow(QByteArrayLiteral("handle"), window)); } return0; }
这里我使用的是第一种方法,因为第二种方法在当Widget作为某个窗口的子窗口时候会得不到这个HWND,具体情况不知道是怎么回事。
接下来就是创建D3D11的设备了,创建的步骤跟一般的方法一致,只是将这个窗口的HWND传入IDXGISwapChain中即可。
bool D3DRenderWidget::createDevice() { HRESULT re; UINT createDeviceFlags = 0; #if defined(DEBUG) ||defined(_DEBUG) createDeviceFlags |=D3D11_CREATE_DEVICE_DEBUG; #endif D3D_FEATURE_LEVEL featureLevel; HRESULT hr = D3D11CreateDevice( 0, m_d3dDriverType, 0, createDeviceFlags, 0, 0, D3D11_SDK_VERSION, &m_d3dDevice, &featureLevel, &m_d3dImmediateContext); if(FAILED(hr)) { MessageBox(0, L"D3D11CreateDeviceFailed.", 0, 0); return false; } if(featureLevel != D3D_FEATURE_LEVEL_11_0) { MessageBox(0, L"Direct3DFeature Level 11 unsupported.", 0, 0); return false; } re =m_d3dDevice->CheckMultisampleQualityLevels( DXGI_FORMAT_R8G8B8A8_UNORM, 4,&m_4xMsaaQuality); if(FAILED(re)) returnfalse; assert(m_4xMsaaQuality > 0); DXGI_SWAP_CHAIN_DESC sd; sd.BufferDesc.Width = width(); sd.BufferDesc.Height = height(); sd.BufferDesc.RefreshRate.Numerator = 60; sd.BufferDesc.RefreshRate.Denominator = 1; sd.BufferDesc.Format =DXGI_FORMAT_R8G8B8A8_UNORM; sd.BufferDesc.ScanlineOrdering =DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; sd.BufferDesc.Scaling =DXGI_MODE_SCALING_UNSPECIFIED; if(m_enable4xMsaa) { sd.SampleDesc.Count = 4; sd.SampleDesc.Quality = m_4xMsaaQuality- 1; } else { sd.SampleDesc.Count = 1; sd.SampleDesc.Quality = 0; } sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; sd.BufferCount = 1; sd.OutputWindow = (HWND)winId(); sd.Windowed =true; sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; sd.Flags =0; IDXGIDevice* dxgiDevice = 0; re = m_d3dDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice); if(FAILED(re)) returnfalse; IDXGIAdapter* dxgiAdapter = 0; re = dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&dxgiAdapter); if(FAILED(re)) returnfalse; IDXGIFactory* dxgiFactory = 0; re = dxgiAdapter->GetParent(__uuidof(IDXGIFactory), (void**)&dxgiFactory); if(FAILED(re)) returnfalse; re =dxgiFactory->CreateSwapChain(m_d3dDevice, &sd, &m_swapChain); if(FAILED(re)) returnfalse; safe_release(dxgiDevice); safe_release(dxgiAdapter); safe_release(dxgiFactory); }
将这个Widget放入QMainWindow的centerWidget进行显示。
最后显示了Qt5WithD3D11的蓝色窗口~_~
程序代码:http://download.csdn.net/detail/hgplan/5267421
(PS: 程序运行前,要设置DX的include路径和lib路径~)
参考:
http://jholewinski.org/blog/direct3d-11-with-qt-4/
https://bitbucket.org/jholewinski/qt4-d3d11
http://stackoverflow.com/questions/14048565/get-hwnd-on-windows-with-qt5-from-wid