我们首先创建一个窗口,该窗口带有消息处理:
最基本的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();
}