我们首先创建一个窗口,该窗口带有消息处理:
最基本的win32中创建窗口程序:
//-------------------------------------------------------------------------------------- //author:tongli //-------------------------------------------------------------------------------------- #include <windows.h> #include "resource.h" HINSTANCE g_hInst = NULL; HWND g_hWnd = NULL; HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow ); LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ); /*入口函数*/ int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow ) { if( FAILED( InitWindow( hInstance, nCmdShow ) ) ) return 0; // Main message loop MSG msg = {0};
/*消息循环*/ while( GetMessage( &msg, NULL, 0, 0 ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } return ( int )msg.wParam; } HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow ) { // Register class WNDCLASSEX wcex; wcex.cbSize = sizeof( WNDCLASSEX ); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon( hInstance, ( LPCTSTR )IDI_TUTORIAL1 ); wcex.hCursor = LoadCursor( NULL, IDC_ARROW ); wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 ); wcex.lpszMenuName = NULL; wcex.lpszClassName = L"TutorialWindowClass"; wcex.hIconSm = LoadIcon( wcex.hInstance, ( LPCTSTR )IDI_TUTORIAL1 ); if( !RegisterClassEx( &wcex ) )//注册窗口类 return E_FAIL; // Create window g_hInst = hInstance; RECT rc = { 0, 0, 640, 480 }; AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE );//根据rc计算窗口的大小 g_hWnd = CreateWindow( L"TutorialWindowClass", L"Direct3D 10 Tutorial 0: Setting Up Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance, NULL ); if( !g_hWnd ) return E_FAIL; ShowWindow( g_hWnd, nCmdShow ); return S_OK; } /*消息处理*/ LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { PAINTSTRUCT ps; HDC hdc; switch( message ) { case WM_PAINT: hdc = BeginPaint( hWnd, &ps ); EndPaint( hWnd, &ps ); break; case WM_DESTROY: PostQuitMessage( 0 ); break; default: return DefWindowProc( hWnd, message, wParam, lParam ); } return 0; }
上面的是创建窗口,有了窗口,接下来的就是渲染:
渲染图像需要两个对象:设备对象和交换链。
设备对象被应用程序用来在缓存上进行绘制,同时该设备也有创建资源的方法。
交换链的作用就是将设备渲染的缓存呈现到屏幕上。交换链包含2个或者2个以上的缓存区,一般是用2个,分别是前景缓存区和后台缓存区,缓存区里存的是设备将要渲染的纹理。前景缓存区存储的是现在正在窗口显示的,该缓存的内存是不可更改的。后台缓存区保存着窗口接下来将要显示的内容,一旦后台缓存区的内容渲染完毕,交换链就交换两个缓存区的内容,后台缓存区就变成前景缓存区。一次类推。
为了创建一个交换链,必须实现DXGI_SWAP_CHAIN_DESC结构,该结构是对即将创建的交换链的描述:
需要注意的是:BackBufferUsage是一个描述应用程序使用后台缓存方式的标示符。在例子中要渲染后台缓存,所以设置为:
BackBufferUsage为DXGI_USAGE_RENDER_TARGET_OUTPUT
DXGI_SWAP_CHAIN_DESC sd; ZeroMemory( &sd, sizeof(sd) );//为sd分配内存 sd.BufferCount = 1; sd.BufferDesc.Width = 640; sd.BufferDesc.Height = 480; sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; sd.BufferDesc.RefreshRate.Numerator = 60; sd.BufferDesc.RefreshRate.Denominator = 1; sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;//渲染后台缓存 sd.OutputWindow = g_hWnd;//交换链要将图像显示的窗口 sd.SampleDesc.Count = 1;//是否开启多重采样。 sd.SampleDesc.Quality = 0; sd.Windowed = TRUE;接下来就是调用D3D10CreateDeviceAndSwapchain函数创建设备和交换链。
if( FAILED( D3D10CreateDeviceAndSwapChain( NULL, D3D10_DRIVER_TYPE_REFERENCE, NULL, 0, D3D10_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice ) ) ) { return FALSE; }
在这里说明一点,第四个参数在某些系统下必须设置为0,具体的原因还没弄清楚。
接下来是创建一个目标渲染视图,该视图是D3D10资源视图的一种,该资源视图允许将资源以一种特定的状态绑定在视图流水线上。可以把该资源视图比作C语言中的类型。C语言中的一块内存可以转换为int,或者float或者结构类型等等。一块内存如果我们不知道他的类型对于我们来讲意义不大。D3D 10资源类型和这个类似。例如:一个2D的纹理,就好比那一块内存,一旦我们得到这块内存,我们可以以不通的资源视图(类型)来绑定这块内存,比如作为一个渲染目标来渲染,作为一个深度模板缓存,或者是一个纹理资源等。
创建一个目标渲染视图,是因为我们想将后台缓存作为缓存目标,这样D3D10才可以在上面渲染。首先调用GetBuffer()来得到后台缓存对象。作为可选项,我们可以创建一个D3D10_RENDERTARGETVIEW_DESC对象来描述目标渲染视图,该描述是CreateRenderTargetView的第二个参数,在这里使用默认的NULL。创建了目标渲染视图,然后用OmSetRenderTarget()来绑定流水线,这样流水线输出的结构将进入了后台缓存中。代码如下:
// Create a render target view ID3D10Texture2D *pBackBuffer; if( FAILED( g_pSwapChain->GetBuffer( 0, __uuidof( ID3D10Texture2D ), (LPVOID*)&pBackBuffer ) ) )//获取后台缓存对象 return FALSE; hr = g_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &g_pRenderTargetView );//创建渲染目标对象 pBackBuffer->Release(); if( FAILED( hr ) ) return FALSE; g_pd3dDevice->OMSetRenderTargets( 1, &g_pRenderTargetView, NULL );最后一步:
在D3D进行渲染前初始化视口(ViewPort)。视口映射剪辑空间坐标,目的是使显示协调。X和Y的范围是-1到1,Z的范围是0到1,这个渲染目标空间,也叫做像素。在D3D 9 中,如果没有设置视口,会有一个默认的,但是D3D10中没有默认的了,也就是必须自己设置。因为我们绝大多数时候是以整个范围作为渲染目标,所以,将左上角设置为(0, 0),而宽和高设置为何渲染目标一致。、
D3D10_VIEWPORT vp; vp.Width = 640; vp.Height = 480; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = 0; vp.TopLeftY = 0; g_pd3dDevice->RSSetViewports( 1, &vp );
建立完窗口和D3D设备,然后去渲染窗口。有一个问题是,如果我们使用GetMessage()来获取消息,如果消息队列中没有消息,则将处于阻塞状态,可以用PeekMessage()来替换,当消息队列中没有消息的时候,可以立即返回而不会阻塞。
更改后的消息循环代码:
MSG msg = {0}; while( WM_QUIT != msg.message ) { if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } else { Render(); // Do some rendering } }
渲染:
这里写个简单的渲染的例子,用单一的颜色来填充整个屏幕,D3D10中最简单的方法来填充整个屏幕就是:ClearTargetView()。定义一个D3D10_COLOR结构来描述颜色,然后作为该函数的参数。然后在调用Present()函数来呈现。Present()函数的主要作用是显示后台缓存的内容来给用户看。
void Render() { // // Clear the backbuffer // float ClearColor[4] = { 0.0f, 0.125f, 0.6f, 1.0f }; // RGBA g_pd3dDevice->ClearRenderTargetView( g_pRenderTargetView, ClearColor ); g_pSwapChain->Present( 0, 0 ); }
/* *author tongli */ #include <windows.h> #include <d3d10.h> #include <d3dx10.h> #include "resource.h" HINSTANCE g_hInst = NULL; HWND g_hWnd = NULL; D3D10_DRIVER_TYPE g_driverType = D3D10_DRIVER_TYPE_NULL; ID3D10Device* g_pd3dDevice = NULL; IDXGISwapChain* g_pSwapChain = NULL; ID3D10RenderTargetView* g_pRenderTargetView = NULL; HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow ); HRESULT InitDevice(); void CleanupDevice(); LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ); void Render(); int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow ) { if( FAILED( InitWindow( hInstance, nCmdShow ) ) ) return 0; if( FAILED( InitDevice() ) ) { CleanupDevice(); return 0; } // Main message loop MSG msg = {0}; while( WM_QUIT != msg.message ) { if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } else { Render(); } } CleanupDevice(); return ( int )msg.wParam; } //-------------------------------------------------------------------------------------- // Register class and create window //-------------------------------------------------------------------------------------- HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow ) { // Register class WNDCLASSEX wcex; wcex.cbSize = sizeof( WNDCLASSEX ); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon( hInstance, ( LPCTSTR )IDI_TUTORIAL1 ); wcex.hCursor = LoadCursor( NULL, IDC_ARROW ); wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 ); wcex.lpszMenuName = NULL; wcex.lpszClassName = L"TutorialWindowClass"; wcex.hIconSm = LoadIcon( wcex.hInstance, ( LPCTSTR )IDI_TUTORIAL1 ); if( !RegisterClassEx( &wcex ) ) return E_FAIL; // Create window g_hInst = hInstance; RECT rc = { 0, 0, 640, 480 }; AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE ); g_hWnd = CreateWindow( L"TutorialWindowClass", L"Direct3D 10 Tutorial 1: Direct3D 10 Basics", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance, NULL ); if( !g_hWnd ) return E_FAIL; ShowWindow( g_hWnd, nCmdShow ); return S_OK; } //-------------------------------------------------------------------------------------- // Called every time the application receives a message //-------------------------------------------------------------------------------------- LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { PAINTSTRUCT ps; HDC hdc; switch( message ) { case WM_PAINT: hdc = BeginPaint( hWnd, &ps ); EndPaint( hWnd, &ps ); break; case WM_DESTROY: PostQuitMessage( 0 ); break; default: return DefWindowProc( hWnd, message, wParam, lParam ); } return 0; } //-------------------------------------------------------------------------------------- // Create Direct3D device and swap chain //-------------------------------------------------------------------------------------- HRESULT InitDevice() { HRESULT hr = S_OK;; RECT rc; GetClientRect( g_hWnd, &rc ); UINT width = rc.right - rc.left; UINT height = rc.bottom - rc.top; UINT createDeviceFlags = 0; //#ifdef _DEBUG // createDeviceFlags |= D3D10_CREATE_DEVICE_DEBUG; //#endif D3D10_DRIVER_TYPE driverTypes[] = { D3D10_DRIVER_TYPE_HARDWARE, D3D10_DRIVER_TYPE_REFERENCE, }; UINT numDriverTypes = sizeof( driverTypes ) / sizeof( driverTypes[0] ); DXGI_SWAP_CHAIN_DESC sd; ZeroMemory( &sd, sizeof( sd ) ); sd.BufferCount = 1; sd.BufferDesc.Width = width; sd.BufferDesc.Height = height; sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; sd.BufferDesc.RefreshRate.Numerator = 60; sd.BufferDesc.RefreshRate.Denominator = 1; sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; sd.OutputWindow = g_hWnd; sd.SampleDesc.Count = 1; sd.SampleDesc.Quality = 0; sd.Windowed = TRUE; for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ ) { g_driverType = driverTypes[driverTypeIndex]; hr = D3D10CreateDeviceAndSwapChain( NULL, g_driverType, NULL, createDeviceFlags, D3D10_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice ); if( SUCCEEDED( hr ) ) break; } if( FAILED( hr ) ) return hr; // Create a render target view ID3D10Texture2D* pBackBuffer; hr = g_pSwapChain->GetBuffer( 0, __uuidof( ID3D10Texture2D ), ( LPVOID* )&pBackBuffer ); if( FAILED( hr ) ) return hr; hr = g_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &g_pRenderTargetView ); pBackBuffer->Release(); if( FAILED( hr ) ) return hr; g_pd3dDevice->OMSetRenderTargets( 1, &g_pRenderTargetView, NULL ); // Setup the viewport D3D10_VIEWPORT vp; vp.Width = width; vp.Height = height; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = 0; vp.TopLeftY = 0; g_pd3dDevice->RSSetViewports( 1, &vp ); return S_OK; } //-------------------------------------------------------------------------------------- // Render the frame //-------------------------------------------------------------------------------------- void Render() { // Just clear the backbuffer float ClearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f }; //red,green,blue,alpha g_pd3dDevice->ClearRenderTargetView( g_pRenderTargetView, ClearColor ); g_pSwapChain->Present( 0, 0 ); } //-------------------------------------------------------------------------------------- // Clean up the objects we've created //-------------------------------------------------------------------------------------- void CleanupDevice() { if( g_pd3dDevice ) g_pd3dDevice->ClearState(); if( g_pRenderTargetView ) g_pRenderTargetView->Release(); if( g_pSwapChain ) g_pSwapChain->Release(); if( g_pd3dDevice ) g_pd3dDevice->Release(); }