연습: WPF에서 호스팅할 Direct3D9 콘텐츠 만들기
이 연습에서는 WPF(Windows Presentation Foundation) 애플리케이션에서 호스팅하는 데 적합한 Direct3D9 콘텐츠를 만드는 방법을 보여 줍니다. WPF 애플리케이션에서 Direct3D9 콘텐츠를 호스팅하는 방법에 대한 자세한 내용은 WPF 및 Direct3D9 상호 운용성을 참조하세요.
이 연습에서는 다음 작업을 수행합니다.
Direct3D9 프로젝트를 만듭니다.
WPF 애플리케이션에서 호스팅할 Direct3D9 프로젝트를 구성합니다.
완료되면 WPF 애플리케이션에서 사용할 Direct3D9 콘텐츠가 포함된 DLL이 생성됩니다.
필수 구성 요소
이 연습을 완료하려면 다음과 같은 구성 요소가 필요합니다.
Visual Studio 2010
DirectX SDK 9 이상.
Direct3D9 프로젝트 만들기
첫 번째 단계는 Direct3D9 프로젝트를 만들고 구성하는 것입니다.
Direct3D9 프로젝트를 만들려면
D3DContent
라는 C++에서 새 Win32 프로젝트를 만듭니다.Win32 애플리케이션 마법사가 열리고 시작 화면이 표시됩니다.
다음을 클릭합니다.
애플리케이션 설정 화면이 나타납니다.
애플리케이션 유형: 섹션에서 DLL 옵션을 선택합니다.
Finish를 클릭합니다.
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 콘텐츠 호스팅합니다.
참고 항목
.NET Desktop feedback