逐步解說:建立裝載於 WPF 中的 Direct3D9 內容
本逐步解說說明如何建立適合裝載於 Windows Presentation Foundation (WPF) 應用程式的 Direct3D9 內容。 有關在 WPF 應用程式中裝載 Direct3D9 內容的詳細資訊,請參閱 WPF 和 Direct3D9 的互操作性。
在本逐步解說中,您將會執行下列工作:
建立一個 Direct3D9 專案。
配置 Direct3D9 專案以裝載於 WPF 應用程式。
完成後,您將擁有一個包含用於 WPF 應用程式的 Direct3D9 內容的 DLL。
必要條件
您需要下列元件才能完成這個逐步解說:
Visual Studio 2010。
DirectX SDK 9 或更高版本。
建立 Direct3D9 專案
第一步是建立並配置 Direct3D9 專案。
如需建立 Direct3D9 專案
在 C++ 中建立一個名為
D3DContent
的新 Win32 專案。Win32 應用程式精靈將開啟並顯示歡迎畫面。
按一下 [下一步] 。
將出現應用程式設定畫面。
在 應用程式類型: 區段中,選擇 DLL 選項。
按一下完成。
生成 D3DContent 專案。
在方案總管中,右鍵點擊 D3DContent 專案,然後選擇 屬性。
D3DContent 屬性頁面 對話方塊將開啟。
選擇 C/C++ 節點。
在 其他包含目錄 欄位中指定 DirectX 包含資料夾的位置。 此資料夾的預設位置為 %ProgramFiles%\Microsoft DirectX SDK (版本)\Include。
按兩下 連結器 節點加以展開。
在 其他庫目錄 欄位中指定 DirectX 庫資料夾的位置。 此資料夾的預設位置為 %ProgramFiles%\Microsoft DirectX SDK (版本)\Lib\x86。
選取 [輸入] 節點。
在 [ 其他相依性 ] 字段中,新增
d3d9.lib
和d3dx9.lib
檔案。在方案總管中,將一個名為
D3DContent.def
的新模組定義文件 (.def) 添加到專案中。
建立 Direct3D9 內容
若要獲得最佳效能,您的 Direct3D9 內容必須使用特定的設定。 下列程式代碼示範如何建立具有最佳效能特性的 Direct3D9 介面。 如需詳細資訊,請參閱 Direct3D9 和 WPF 交互操作效能考量 。
如需建立 Direct3D9 內容
使用方案總管將以下三個C++類別新增至名為下列的專案中。
CRenderer
(含虛擬解構函式)CRendererManager
CTriangleRenderer
在程式碼編輯器中開啟 Renderer.h 檔案,並以下列程式碼取代自動產生的程式碼。
#pragma once class CRenderer { public: virtual ~CRenderer(); HRESULT CheckDeviceState(); HRESULT CreateSurface(UINT uWidth, UINT uHeight, bool fUseAlpha, UINT m_uNumSamples); virtual HRESULT Render() = 0; IDirect3DSurface9 *GetSurfaceNoRef() { return m_pd3dRTS; } protected: CRenderer(); virtual HRESULT Init(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter); IDirect3DDevice9 *m_pd3dDevice; IDirect3DDevice9Ex *m_pd3dDeviceEx; IDirect3DSurface9 *m_pd3dRTS; };
在程式碼編輯器中開啟 Renderer.cpp 檔案,並以下列程式碼取代自動產生的程式碼。
//+----------------------------------------------------------------------------- // // CRenderer // // An abstract base class that creates a device and a target render // surface. Derive from this class and override Init() and Render() // to do your own rendering. See CTriangleRenderer for an example. //------------------------------------------------------------------------------ #include "StdAfx.h" //+----------------------------------------------------------------------------- // // Member: // CRenderer ctor // //------------------------------------------------------------------------------ CRenderer::CRenderer() : m_pd3dDevice(NULL), m_pd3dDeviceEx(NULL), m_pd3dRTS(NULL) { } //+----------------------------------------------------------------------------- // // Member: // CRenderer dtor // //------------------------------------------------------------------------------ CRenderer::~CRenderer() { SAFE_RELEASE(m_pd3dDevice); SAFE_RELEASE(m_pd3dDeviceEx); SAFE_RELEASE(m_pd3dRTS); } //+----------------------------------------------------------------------------- // // Member: // CRenderer::CheckDeviceState // // Synopsis: // Returns the status of the device. 9Ex devices are a special case because // TestCooperativeLevel() has been deprecated in 9Ex. // //------------------------------------------------------------------------------ HRESULT CRenderer::CheckDeviceState() { if (m_pd3dDeviceEx) { return m_pd3dDeviceEx->CheckDeviceState(NULL); } else if (m_pd3dDevice) { return m_pd3dDevice->TestCooperativeLevel(); } else { return D3DERR_DEVICELOST; } } //+----------------------------------------------------------------------------- // // Member: // CRenderer::CreateSurface // // Synopsis: // Creates and sets the render target // //------------------------------------------------------------------------------ HRESULT CRenderer::CreateSurface(UINT uWidth, UINT uHeight, bool fUseAlpha, UINT m_uNumSamples) { HRESULT hr = S_OK; SAFE_RELEASE(m_pd3dRTS); IFC(m_pd3dDevice->CreateRenderTarget( uWidth, uHeight, fUseAlpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8, static_cast<D3DMULTISAMPLE_TYPE>(m_uNumSamples), 0, m_pd3dDeviceEx ? FALSE : TRUE, // Lockable RT required for good XP perf &m_pd3dRTS, NULL )); IFC(m_pd3dDevice->SetRenderTarget(0, m_pd3dRTS)); Cleanup: return hr; } //+----------------------------------------------------------------------------- // // Member: // CRenderer::Init // // Synopsis: // Creates the device // //------------------------------------------------------------------------------ HRESULT CRenderer::Init(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter) { HRESULT hr = S_OK; D3DPRESENT_PARAMETERS d3dpp; ZeroMemory(&d3dpp, sizeof(d3dpp)); d3dpp.Windowed = TRUE; d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; d3dpp.BackBufferHeight = 1; d3dpp.BackBufferWidth = 1; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; D3DCAPS9 caps; DWORD dwVertexProcessing; IFC(pD3D->GetDeviceCaps(uAdapter, D3DDEVTYPE_HAL, &caps)); if ((caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == D3DDEVCAPS_HWTRANSFORMANDLIGHT) { dwVertexProcessing = D3DCREATE_HARDWARE_VERTEXPROCESSING; } else { dwVertexProcessing = D3DCREATE_SOFTWARE_VERTEXPROCESSING; } if (pD3DEx) { IDirect3DDevice9Ex *pd3dDevice = NULL; IFC(pD3DEx->CreateDeviceEx( uAdapter, D3DDEVTYPE_HAL, hwnd, dwVertexProcessing | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE, &d3dpp, NULL, &m_pd3dDeviceEx )); IFC(m_pd3dDeviceEx->QueryInterface(__uuidof(IDirect3DDevice9), reinterpret_cast<void**>(&m_pd3dDevice))); } else { assert(pD3D); IFC(pD3D->CreateDevice( uAdapter, D3DDEVTYPE_HAL, hwnd, dwVertexProcessing | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE, &d3dpp, &m_pd3dDevice )); } Cleanup: return hr; }
在程式碼編輯器中開啟 RendererManager.h 檔案,並以下列程式碼取代自動產生的程式碼。
#pragma once class CRenderer; class CRendererManager { public: static HRESULT Create(CRendererManager **ppManager); ~CRendererManager(); HRESULT EnsureDevices(); void SetSize(UINT uWidth, UINT uHeight); void SetAlpha(bool fUseAlpha); void SetNumDesiredSamples(UINT uNumSamples); void SetAdapter(POINT screenSpacePoint); HRESULT GetBackBufferNoRef(IDirect3DSurface9 **ppSurface); HRESULT Render(); private: CRendererManager(); void CleanupInvalidDevices(); HRESULT EnsureRenderers(); HRESULT EnsureHWND(); HRESULT EnsureD3DObjects(); HRESULT TestSurfaceSettings(); void DestroyResources(); IDirect3D9 *m_pD3D; IDirect3D9Ex *m_pD3DEx; UINT m_cAdapters; CRenderer **m_rgRenderers; CRenderer *m_pCurrentRenderer; HWND m_hwnd; UINT m_uWidth; UINT m_uHeight; UINT m_uNumSamples; bool m_fUseAlpha; bool m_fSurfaceSettingsChanged; };
在程式碼編輯器中開啟 RendererManager.cpp 檔案,並以下列程式碼取代自動產生的程式碼。
//+----------------------------------------------------------------------------- // // CRendererManager // // Manages the list of CRenderers. Managed code pinvokes into this class // and this class forwards to the appropriate CRenderer. // //------------------------------------------------------------------------------ #include "StdAfx.h" const static TCHAR szAppName[] = TEXT("D3DImageSample"); typedef HRESULT (WINAPI *DIRECT3DCREATE9EXFUNCTION)(UINT SDKVersion, IDirect3D9Ex**); //+----------------------------------------------------------------------------- // // Member: // CRendererManager ctor // //------------------------------------------------------------------------------ CRendererManager::CRendererManager() : m_pD3D(NULL), m_pD3DEx(NULL), m_cAdapters(0), m_hwnd(NULL), m_pCurrentRenderer(NULL), m_rgRenderers(NULL), m_uWidth(1024), m_uHeight(1024), m_uNumSamples(0), m_fUseAlpha(false), m_fSurfaceSettingsChanged(true) { } //+----------------------------------------------------------------------------- // // Member: // CRendererManager dtor // //------------------------------------------------------------------------------ CRendererManager::~CRendererManager() { DestroyResources(); if (m_hwnd) { DestroyWindow(m_hwnd); UnregisterClass(szAppName, NULL); } } //+----------------------------------------------------------------------------- // // Member: // CRendererManager::Create // // Synopsis: // Creates the manager // //------------------------------------------------------------------------------ HRESULT CRendererManager::Create(CRendererManager **ppManager) { HRESULT hr = S_OK; *ppManager = new CRendererManager(); IFCOOM(*ppManager); Cleanup: return hr; } //+----------------------------------------------------------------------------- // // Member: // CRendererManager::EnsureRenderers // // Synopsis: // Makes sure the CRenderer objects exist // //------------------------------------------------------------------------------ HRESULT CRendererManager::EnsureRenderers() { HRESULT hr = S_OK; if (!m_rgRenderers) { IFC(EnsureHWND()); assert(m_cAdapters); m_rgRenderers = new CRenderer*[m_cAdapters]; IFCOOM(m_rgRenderers); ZeroMemory(m_rgRenderers, m_cAdapters * sizeof(m_rgRenderers[0])); for (UINT i = 0; i < m_cAdapters; ++i) { IFC(CTriangleRenderer::Create(m_pD3D, m_pD3DEx, m_hwnd, i, &m_rgRenderers[i])); } // Default to the default adapter m_pCurrentRenderer = m_rgRenderers[0]; } Cleanup: return hr; } //+----------------------------------------------------------------------------- // // Member: // CRendererManager::EnsureHWND // // Synopsis: // Makes sure an HWND exists if we need it // //------------------------------------------------------------------------------ HRESULT CRendererManager::EnsureHWND() { HRESULT hr = S_OK; if (!m_hwnd) { WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = DefWindowProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = NULL; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; if (!RegisterClass(&wndclass)) { IFC(E_FAIL); } m_hwnd = CreateWindow(szAppName, TEXT("D3DImageSample"), WS_OVERLAPPEDWINDOW, 0, // Initial X 0, // Initial Y 0, // Width 0, // Height NULL, NULL, NULL, NULL); } Cleanup: return hr; } //+----------------------------------------------------------------------------- // // Member: // CRendererManager::EnsureD3DObjects // // Synopsis: // Makes sure the D3D objects exist // //------------------------------------------------------------------------------ HRESULT CRendererManager::EnsureD3DObjects() { HRESULT hr = S_OK; HMODULE hD3D = NULL; if (!m_pD3D) { hD3D = LoadLibrary(TEXT("d3d9.dll")); DIRECT3DCREATE9EXFUNCTION pfnCreate9Ex = (DIRECT3DCREATE9EXFUNCTION)GetProcAddress(hD3D, "Direct3DCreate9Ex"); if (pfnCreate9Ex) { IFC((*pfnCreate9Ex)(D3D_SDK_VERSION, &m_pD3DEx)); IFC(m_pD3DEx->QueryInterface(__uuidof(IDirect3D9), reinterpret_cast<void **>(&m_pD3D))); } else { m_pD3D = Direct3DCreate9(D3D_SDK_VERSION); if (!m_pD3D) { IFC(E_FAIL); } } m_cAdapters = m_pD3D->GetAdapterCount(); } Cleanup: if (hD3D) { FreeLibrary(hD3D); } return hr; } //+----------------------------------------------------------------------------- // // Member: // CRendererManager::CleanupInvalidDevices // // Synopsis: // Checks to see if any devices are bad and if so, deletes all resources // // We could delete resources and wait for D3DERR_DEVICENOTRESET and reset // the devices, but if the device is lost because of an adapter order // change then our existing D3D objects would have stale adapter // information. We'll delete everything to be safe rather than sorry. // //------------------------------------------------------------------------------ void CRendererManager::CleanupInvalidDevices() { for (UINT i = 0; i < m_cAdapters; ++i) { if (FAILED(m_rgRenderers[i]->CheckDeviceState())) { DestroyResources(); break; } } } //+----------------------------------------------------------------------------- // // Member: // CRendererManager::GetBackBufferNoRef // // Synopsis: // Returns the surface of the current renderer without adding a reference // // This can return NULL if we're in a bad device state. // //------------------------------------------------------------------------------ HRESULT CRendererManager::GetBackBufferNoRef(IDirect3DSurface9 **ppSurface) { HRESULT hr = S_OK; // Make sure we at least return NULL *ppSurface = NULL; CleanupInvalidDevices(); IFC(EnsureD3DObjects()); // // Even if we never render to another adapter, this sample creates devices // and resources on each one. This is a potential waste of video memory, // but it guarantees that we won't have any problems (e.g. out of video // memory) when switching to render on another adapter. In your own code // you may choose to delay creation but you'll need to handle the issues // that come with it. // IFC(EnsureRenderers()); if (m_fSurfaceSettingsChanged) { if (FAILED(TestSurfaceSettings())) { IFC(E_FAIL); } for (UINT i = 0; i < m_cAdapters; ++i) { IFC(m_rgRenderers[i]->CreateSurface(m_uWidth, m_uHeight, m_fUseAlpha, m_uNumSamples)); } m_fSurfaceSettingsChanged = false; } if (m_pCurrentRenderer) { *ppSurface = m_pCurrentRenderer->GetSurfaceNoRef(); } Cleanup: // If we failed because of a bad device, ignore the failure for now and // we'll clean up and try again next time. if (hr == D3DERR_DEVICELOST) { hr = S_OK; } return hr; } //+----------------------------------------------------------------------------- // // Member: // CRendererManager::TestSurfaceSettings // // Synopsis: // Checks to see if our current surface settings are allowed on all // adapters. // //------------------------------------------------------------------------------ HRESULT CRendererManager::TestSurfaceSettings() { HRESULT hr = S_OK; D3DFORMAT fmt = m_fUseAlpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8; // // We test all adapters because because we potentially use all adapters. // But even if this sample only rendered to the default adapter, you // should check all adapters because WPF may move your surface to // another adapter for you! // for (UINT i = 0; i < m_cAdapters; ++i) { // Can we get HW rendering? IFC(m_pD3D->CheckDeviceType( i, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, fmt, TRUE )); // Is the format okay? IFC(m_pD3D->CheckDeviceFormat( i, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DUSAGE_RENDERTARGET | D3DUSAGE_DYNAMIC, // We'll use dynamic when on XP D3DRTYPE_SURFACE, fmt )); // D3DImage only allows multisampling on 9Ex devices. If we can't // multisample, overwrite the desired number of samples with 0. if (m_pD3DEx && m_uNumSamples > 1) { assert(m_uNumSamples <= 16); if (FAILED(m_pD3D->CheckDeviceMultiSampleType( i, D3DDEVTYPE_HAL, fmt, TRUE, static_cast<D3DMULTISAMPLE_TYPE>(m_uNumSamples), NULL ))) { m_uNumSamples = 0; } } else { m_uNumSamples = 0; } } Cleanup: return hr; } //+----------------------------------------------------------------------------- // // Member: // CRendererManager::DestroyResources // // Synopsis: // Delete all D3D resources // //------------------------------------------------------------------------------ void CRendererManager::DestroyResources() { SAFE_RELEASE(m_pD3D); SAFE_RELEASE(m_pD3DEx); for (UINT i = 0; i < m_cAdapters; ++i) { delete m_rgRenderers[i]; } delete [] m_rgRenderers; m_rgRenderers = NULL; m_pCurrentRenderer = NULL; m_cAdapters = 0; m_fSurfaceSettingsChanged = true; } //+----------------------------------------------------------------------------- // // Member: // CRendererManager::SetSize // // Synopsis: // Update the size of the surface. Next render will create a new surface. // //------------------------------------------------------------------------------ void CRendererManager::SetSize(UINT uWidth, UINT uHeight) { if (uWidth != m_uWidth || uHeight != m_uHeight) { m_uWidth = uWidth; m_uHeight = uHeight; m_fSurfaceSettingsChanged = true; } } //+----------------------------------------------------------------------------- // // Member: // CRendererManager::SetAlpha // // Synopsis: // Update the format of the surface. Next render will create a new surface. // //------------------------------------------------------------------------------ void CRendererManager::SetAlpha(bool fUseAlpha) { if (fUseAlpha != m_fUseAlpha) { m_fUseAlpha = fUseAlpha; m_fSurfaceSettingsChanged = true; } } //+----------------------------------------------------------------------------- // // Member: // CRendererManager::SetNumDesiredSamples // // Synopsis: // Update the MSAA settings of the surface. Next render will create a // new surface. // //------------------------------------------------------------------------------ void CRendererManager::SetNumDesiredSamples(UINT uNumSamples) { if (m_uNumSamples != uNumSamples) { m_uNumSamples = uNumSamples; m_fSurfaceSettingsChanged = true; } } //+----------------------------------------------------------------------------- // // Member: // CRendererManager::SetAdapter // // Synopsis: // Update the current renderer. Next render will use the new renderer. // //------------------------------------------------------------------------------ void CRendererManager::SetAdapter(POINT screenSpacePoint) { CleanupInvalidDevices(); // // After CleanupInvalidDevices, we may not have any D3D objects. Rather than // recreate them here, ignore the adapter update and wait for render to recreate. // if (m_pD3D && m_rgRenderers) { HMONITOR hMon = MonitorFromPoint(screenSpacePoint, MONITOR_DEFAULTTONULL); for (UINT i = 0; i < m_cAdapters; ++i) { if (hMon == m_pD3D->GetAdapterMonitor(i)) { m_pCurrentRenderer = m_rgRenderers[i]; break; } } } } //+----------------------------------------------------------------------------- // // Member: // CRendererManager::Render // // Synopsis: // Forward to the current renderer // //------------------------------------------------------------------------------ HRESULT CRendererManager::Render() { return m_pCurrentRenderer ? m_pCurrentRenderer->Render() : S_OK; }
在程式碼編輯器中開啟 TriangleRenderer.h 檔案,並以下列程式碼取代自動產生的程式碼。
#pragma once class CTriangleRenderer : public CRenderer { public: static HRESULT Create(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter, CRenderer **ppRenderer); ~CTriangleRenderer(); HRESULT Render(); protected: HRESULT Init(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter); private: CTriangleRenderer(); IDirect3DVertexBuffer9 *m_pd3dVB; };
在程式碼編輯器中開啟 TriangleRenderer.cpp 檔案,並以下列程式碼取代自動產生的程式碼。
//+----------------------------------------------------------------------------- // // CTriangleRenderer // // Subclass of CRenderer that renders a single, spinning triangle // //------------------------------------------------------------------------------ #include "StdAfx.h" struct CUSTOMVERTEX { FLOAT x, y, z; DWORD color; }; #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE) //+----------------------------------------------------------------------------- // // Member: // CTriangleRenderer ctor // //------------------------------------------------------------------------------ CTriangleRenderer::CTriangleRenderer() : CRenderer(), m_pd3dVB(NULL) { } //+----------------------------------------------------------------------------- // // Member: // CTriangleRenderer dtor // //------------------------------------------------------------------------------ CTriangleRenderer::~CTriangleRenderer() { SAFE_RELEASE(m_pd3dVB); } //+----------------------------------------------------------------------------- // // Member: // CTriangleRenderer::Create // // Synopsis: // Creates the renderer // //------------------------------------------------------------------------------ HRESULT CTriangleRenderer::Create(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter, CRenderer **ppRenderer) { HRESULT hr = S_OK; CTriangleRenderer *pRenderer = new CTriangleRenderer(); IFCOOM(pRenderer); IFC(pRenderer->Init(pD3D, pD3DEx, hwnd, uAdapter)); *ppRenderer = pRenderer; pRenderer = NULL; Cleanup: delete pRenderer; return hr; } //+----------------------------------------------------------------------------- // // Member: // CTriangleRenderer::Init // // Synopsis: // Override of CRenderer::Init that calls base to create the device and // then creates the CTriangleRenderer-specific resources // //------------------------------------------------------------------------------ HRESULT CTriangleRenderer::Init(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter) { HRESULT hr = S_OK; D3DXMATRIXA16 matView, matProj; D3DXVECTOR3 vEyePt(0.0f, 0.0f,-5.0f); D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f); D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f); // Call base to create the device and render target IFC(CRenderer::Init(pD3D, pD3DEx, hwnd, uAdapter)); // Set up the VB CUSTOMVERTEX vertices[] = { { -1.0f, -1.0f, 0.0f, 0xffff0000, }, // x, y, z, color { 1.0f, -1.0f, 0.0f, 0xff00ff00, }, { 0.0f, 1.0f, 0.0f, 0xff00ffff, }, }; IFC(m_pd3dDevice->CreateVertexBuffer(sizeof(vertices), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &m_pd3dVB, NULL)); void *pVertices; IFC(m_pd3dVB->Lock(0, sizeof(vertices), &pVertices, 0)); memcpy(pVertices, vertices, sizeof(vertices)); m_pd3dVB->Unlock(); // Set up the camera D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec); IFC(m_pd3dDevice->SetTransform(D3DTS_VIEW, &matView)); D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4, 1.0f, 1.0f, 100.0f); IFC(m_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj)); // Set up the global state IFC(m_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE)); IFC(m_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE)); IFC(m_pd3dDevice->SetStreamSource(0, m_pd3dVB, 0, sizeof(CUSTOMVERTEX))); IFC(m_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX)); Cleanup: return hr; } //+----------------------------------------------------------------------------- // // Member: // CTriangleRenderer::Render // // Synopsis: // Renders the rotating triangle // //------------------------------------------------------------------------------ HRESULT CTriangleRenderer::Render() { HRESULT hr = S_OK; D3DXMATRIXA16 matWorld; IFC(m_pd3dDevice->BeginScene()); IFC(m_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB(128, 0, 0, 128), // NOTE: Premultiplied alpha! 1.0f, 0 )); // Set up the rotation UINT iTime = GetTickCount() % 1000; FLOAT fAngle = iTime * (2.0f * D3DX_PI) / 1000.0f; D3DXMatrixRotationY(&matWorld, fAngle); IFC(m_pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld)); IFC(m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1)); IFC(m_pd3dDevice->EndScene()); Cleanup: return hr; }
在程式碼編輯器中開啟 stdafx.h 檔案,並以下列程式碼取代自動產生的程式碼。
// stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #pragma once #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers // Windows Header Files: #include <windows.h> #include <d3d9.h> #include <d3dx9.h> #include <assert.h> #include "RendererManager.h" #include "Renderer.h" #include "TriangleRenderer.h" #define IFC(x) { hr = (x); if (FAILED(hr)) goto Cleanup; } #define IFCOOM(x) { if ((x) == NULL) { hr = E_OUTOFMEMORY; IFC(hr); } } #define SAFE_RELEASE(x) { if (x) { x->Release(); x = NULL; } }
在程式碼編輯器中開啟 dllmain.cpp 檔案,並以下列程式碼取代自動產生的程式碼。
// dllmain.cpp : Defines the entry point for the DLL application. #include "stdafx.h" BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } static CRendererManager *pManager = NULL; static HRESULT EnsureRendererManager() { return pManager ? S_OK : CRendererManager::Create(&pManager); } extern "C" HRESULT WINAPI SetSize(UINT uWidth, UINT uHeight) { HRESULT hr = S_OK; IFC(EnsureRendererManager()); pManager->SetSize(uWidth, uHeight); Cleanup: return hr; } extern "C" HRESULT WINAPI SetAlpha(BOOL fUseAlpha) { HRESULT hr = S_OK; IFC(EnsureRendererManager()); pManager->SetAlpha(!!fUseAlpha); Cleanup: return hr; } extern "C" HRESULT WINAPI SetNumDesiredSamples(UINT uNumSamples) { HRESULT hr = S_OK; IFC(EnsureRendererManager()); pManager->SetNumDesiredSamples(uNumSamples); Cleanup: return hr; } extern "C" HRESULT WINAPI SetAdapter(POINT screenSpacePoint) { HRESULT hr = S_OK; IFC(EnsureRendererManager()); pManager->SetAdapter(screenSpacePoint); Cleanup: return hr; } extern "C" HRESULT WINAPI GetBackBufferNoRef(IDirect3DSurface9 **ppSurface) { HRESULT hr = S_OK; IFC(EnsureRendererManager()); IFC(pManager->GetBackBufferNoRef(ppSurface)); Cleanup: return hr; } extern "C" HRESULT WINAPI Render() { assert(pManager); return pManager->Render(); } extern "C" void WINAPI Destroy() { delete pManager; pManager = NULL; }
在程式碼編輯器中開啟 D3DContent.def 檔案
以下列程式碼取代自動產生的程式碼。
LIBRARY "D3DContent" EXPORTS SetSize SetAlpha SetNumDesiredSamples SetAdapter GetBackBufferNoRef Render Destroy
組建專案。
後續步驟
- 在 WPF 應用程式中裝載 Direct3D9 內容。 如需詳細資訊,請參閱 逐步解說:在 WPF 中裝載 Direct3D9 內容。