65.9K
CodeProject 正在变化。 阅读更多。
Home

Direct3D 10 入门

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (13投票s)

2008 年 4 月 11 日

CPOL

5分钟阅读

viewsIcon

92799

downloadIcon

1134

使用 Direct3D 10 创建您的第一个应用程序。

引言

欢迎来到 Direct3D 10 编程系列教程。要跟上本教程,您需要熟悉 C++ 编程。我假设您也对 Win32 API 编程略有了解,并且知道如何使用该 API 创建一个简单的窗口(如果您不熟悉,请先查看这些 Win32 API 教程)。

Direct3D 10 带来了许多承诺;它旨在将游戏和实时应用程序的视觉水平提升到新的高度。近期图形硬件在质量和性能上的巨大提升带来了新的需求和必要性,而 Direct3D 10 将回应这些需求,并为未来的改进和新功能提供一个灵活的平台。

基本概念

前缓冲

与显示器屏幕上的实际像素绑定的缓冲区。

后缓冲

D3D 使用后缓冲作为输出缓冲区,然后在每一帧中交换后缓冲和前缓冲以显示场景。这可以避免显示器图像闪烁。

渲染目标

Direct3D 设备用于渲染的表面。

DXGI

DirectX 10 设计中的第一个新组件是 DirectX 图形基础结构,它是提供图形硬件访问的主要设备无关层;DXGI 负责处理以前在 D3D API 中实现的某些基本功能。

dxgi.jpg

应用程序可以通过 Direct3D 10 API 与 DXGI 通信,但有时您需要直接访问 DXGI,例如枚举视频适配器、呈现场景等。

Direct3D 设备

Direct3D 的大部分核心功能都可以通过 Direct3D 设备接口访问,因此我们首先需要创建一个 D3D 设备。但在此之前,我们需要有一个可见的窗口。

// Create A Window Class Structure 
WNDCLASSEX wc; 
ZeroMemory(&wc, sizeof(wc));        
wc.cbSize = sizeof(wc);                
wc.hInstance = GetModuleHandle(NULL);        
wc.lpfnWndProc = WndProc;                    
wc.lpszClassName = "GPORG";                        
wc.style = CS_HREDRAW | CS_VREDRAW;            

// Register Window Class 
RegisterClassEx(&wc); 

// Create Window 
hWnd = CreateWindowEx(0, "GPORG", 
       "GameProgrammer.org Direct3D 10 Tutorial",
       WS_OVERLAPPEDWINDOW|WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, width,
       height, NULL,NULL,wc.hInstance,0);

此代码注册了一个新的窗口类,并使用该类创建了一个新窗口。(此代码在 Win32 SDK 教程部分有详细解释)。现在,我们通过调用 D3D10CreateDeviceAndSwapChain() 函数来创建一个 D3D10 设备。

DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory( &swapChainDesc, sizeof(swapChainDesc) );
swapChainDesc.BufferCount = 1;
swapChainDesc.BufferDesc.Width = width;
swapChainDesc.BufferDesc.Height = height;
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.OutputWindow = hWnd;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.Windowed = TRUE;

if( FAILED( D3D10CreateDeviceAndSwapChain( NULL, 
    D3D10_DRIVER_TYPE_HARDWARE, NULL, 0, 
    D3D10_SDK_VERSION, &swapChainDesc, 
    &pSwapChain, &pDevice ) ) )
{
    Error("Failed to create device and swap chain.");
    return false;
}

顾名思义,此函数创建了一个 Direct3D 设备和一个 DXGI 交换链。交换链负责从 D3D 设备获取数据并在屏幕上显示。要创建交换链,D3D 需要了解有关它的几个细节,您需要使用 DXGI_SWAP_CHAIN_DESC 结构来提供这些信息。查看 DXGI_SWAP_CHAIN_DESC 定义,您会发现它包含另外两个结构:DXGI_MODE_DESCDXGI_SAMPLE_DESC

typedef struct DXGI_SWAP_CHAIN_DESC
{
    DXGI_MODE_DESC BufferDesc;
    DXGI_SAMPLE_DESC SampleDesc;
    DXGI_USAGE BufferUsage;
    UINT BufferCount;
    HWND OutputWindow;
    BOOL Windowed;
    DXGI_SWAP_EFFECT SwapEffect;
    UINT Flags;
}     DXGI_SWAP_CHAIN_DESC;

DXGI_MODE_DESC 包含显示模式的描述

typedef struct DXGI_MODE_DESC
{
    UINT Width;
    UINT Height;
    DXGI_RATIONAL RefreshRate;
    DXGI_FORMAT Format;
    DXGI_MODE_SCANLINE_ORDER ScanlineOrdering;
    DXGI_MODE_SCALING Scaling;
}     DXGI_MODE_DESC;

在这里,我们仅使用了 WidthHeightFormat 参数,它们分别描述了我们缓冲区的宽度、高度和显示格式。DXGI_SAMPLE_DESC 描述了资源的抗锯齿参数。

typedef struct DXGI_SAMPLE_DESC
{
    UINT Count;
    UINT Quality;
}     DXGI_SAMPLE_DESC;

Count 表示每像素采样数,第二个参数描述了图像质量级别。将 Count 设置为 1,Quality 设置为 0,我们就可以禁用抗锯齿功能。

回到 DXGI_SWAP_CHAIN_DESC 结构,我们将介绍其余参数:BufferCount 是交换链中的缓冲区数量;我们只希望在此创建前缓冲,因此将其设置为 1。通过将 BufferUsage 设置为 DXGI_USAGE_RENDER_TARGET_OUTPUT,我们声明该缓冲区将用作渲染目标输出。OutputWindow 是我们希望用于表示渲染输出的窗口的句柄。最后一个参数 Windowed 指示应用程序是全屏模式还是窗口模式启动。(您可以在运行时通过 ALT+Enter 切换全屏模式)。

现在,让我们仔细看看 D3D10CreateDeviceAndSwapChain() 函数

HRESULT D3D10CreateDeviceAndSwapChain(
    IDXGIAdapter *pAdapter,
    D3D10_DRIVER_TYPE DriverType,
    HMODULE Software,
    UINT Flags,
    UINT SDKVersion,
    DXGI_SWAP_CHAIN_DESC *pSwapChainDesc,
    IDXGISwapChain **ppSwapChain,    
    ID3D10Device **ppDevice);

第一个参数是指向 DXGI 适配器的指针;将其设置为 NULL,Direct3D 将为我们创建一个默认适配器。第二个参数描述了要使用的显示驱动程序的类型;最有可能的是,我们希望使用硬件驱动程序,因此将其设置为 D3D10_DRIVER_TYPE_HARDWARE。下一个参数应为 null,除非您想使用自定义软件光栅化器,这非常不可能。我们不使用任何特殊标志,因此将 Flags 设置为 0。SDKVersion 应始终设置为 D3D10_SDK_VERSION。对于下一个参数,我们应传递一个指向我们刚刚创建的 DXGI_SWAP_CHAIN_DESC 结构的指针。最后两个参数是指向交换链和 D3D 设备指针,Direct3D 将为我们创建它们。

现在我们已经创建了一个 D3D 设备和一个交换链,我们需要将它们连接起来,以便交换链中的后缓冲可以被 D3D 设备用作渲染目标(您可能会想为什么 D3D10CreateDeviceAndSwapChain() 函数不自动完成此操作;答案是,这样可以为您提供更大的灵活性)。为了建立这种连接,我们需要将后缓冲从交换链获取为一个纹理,使用该纹理创建一个渲染目标,并将其作为活动渲染目标传递给 D3D 设备,这段代码将完成这一切

ID3D10Texture2D *pBackBuffer;

if( FAILED( pSwapChain->GetBuffer( 0, __uuidof( ID3D10Texture2D ), 
          (LPVOID*)&pBackBuffer ) ) )
{
    Error("Failed to create back buffer.");
    return false;
}

if(FAILED( pDevice->CreateRenderTargetView( pBackBuffer, NULL, 
                                               &pRenderTargetView )))
{
    Error("Failed to create render target view.");
      return false;
}

pBackBuffer->Release();

pDevice->OMSetRenderTargets( 1, &pRenderTargetView, NULL );

初始化部分到此完成;现在我们想使用 Direct3D 来清除屏幕

pDevice->ClearRenderTargetView( pRenderTargetView, D3DXVECTOR4(0, 0, 0, 1) );

pSwapChain->Present( 0, 0 );

pDevice->ClearRenderTargetView() 函数用指定的颜色(这里是黑色,r=0, g=0, b=0, a=1)清除整个渲染目标;之后,我们使用 pSwapChain->Present() 将场景显示在屏幕上。交换链将自动为我们处理后缓冲和前缓冲的交换。就这样,我们在这里创建了第一个 Direct3D 10 应用程序。如果在屏幕上有形状,那会更有趣。您可以参考 MSDN 获取有关 Direct3D 10 的更多信息,或者等待我们在这里的下一个教程 这里

代码

最后,我提供了应用程序的完整源代码。要使其正常工作,您需要一个 Visual Studio 中的空项目,在项目设置中将字符集设置为“使用多字节字符集”,然后添加一个包含此代码的 *cpp* 文件

#include <windows.h> 
#include <d3d10.h> 
#include <d3dx10.h>

#define Error(X) MessageBox(NULL, X, "Error", MB_OK)

class Application
{
public:

    Application();
    ~Application();

    bool CreateD3DWindow(int width, int height);

    bool Initialize(); 
    void Render(float deltaTime);
    void MainLoop();

private:

    static LRESULT CALLBACK WndProc( HWND hWnd , UINT message , 
                                     WPARAM wParam , LPARAM lParam);

    HWND                        hWnd; 
    IDXGISwapChain*                pSwapChain;
    ID3D10RenderTargetView*        pRenderTargetView;
    ID3D10Device*                pDevice; 
    int                            width, height;
};

#pragma comment (lib, "d3d10.lib")
#pragma comment (lib, "d3dx10.lib")

Application::Application()
{
}

Application::~Application()
{
}

LRESULT CALLBACK Application::WndProc( HWND hWnd , UINT message , 
                                       WPARAM wParam , LPARAM lParam)
{ 
    switch(message){ 
    case WM_CLOSE: 
    case WM_DESTROY: 
        PostQuitMessage(0); 
        break;; 
    } 
    return DefWindowProc(hWnd,message,wParam,lParam); 
} 

bool Application::CreateD3DWindow(int width, int height)
{ 
    this->width = width;
    this->height = height;
    
    // Create A Window Class Structure 
    WNDCLASSEX wc; 
    ZeroMemory(&wc, sizeof(wc));        
    wc.cbSize = sizeof(wc);                
    wc.hInstance = GetModuleHandle(NULL);        
    wc.lpfnWndProc = WndProc;                    
    wc.lpszClassName = "GPORG";                        
    wc.style = CS_HREDRAW | CS_VREDRAW;            
    
    // Register Window Class 
    RegisterClassEx(&wc); 
    
    // Create Window 
    hWnd = CreateWindowEx(0, "GPORG", 
           "GameProgrammer.org Direct3D 10 Tutorial", 
           WS_OVERLAPPEDWINDOW|WS_VISIBLE, CW_USEDEFAULT, 
           CW_USEDEFAULT, width, height, NULL,NULL,wc.hInstance,0);
    
    return true; 
} 

bool Application::Initialize()
{
    DXGI_SWAP_CHAIN_DESC swapChainDesc;
    ZeroMemory( &swapChainDesc, sizeof(swapChainDesc) );
    swapChainDesc.BufferCount = 1;
    swapChainDesc.BufferDesc.Width = width;
    swapChainDesc.BufferDesc.Height = height;
    swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.OutputWindow = hWnd;
    swapChainDesc.SampleDesc.Count = 1;
    swapChainDesc.SampleDesc.Quality = 0;
    swapChainDesc.Windowed = TRUE;

    if( FAILED( D3D10CreateDeviceAndSwapChain( NULL, 
        D3D10_DRIVER_TYPE_HARDWARE, NULL, 
        0, D3D10_SDK_VERSION, &swapChainDesc, 
        &pSwapChain, &pDevice ) ) )
    {
        if( FAILED( D3D10CreateDeviceAndSwapChain( NULL, 
            D3D10_DRIVER_TYPE_REFERENCE, NULL, 
            0, D3D10_SDK_VERSION, &swapChainDesc, 
            &pSwapChain, &pDevice ) ) )
        {
            Error("Failed to create device and swap chain.");
            return false;
        }
    }

    ID3D10Texture2D *pBackBuffer;
    if( FAILED( pSwapChain->GetBuffer( 0, __uuidof( ID3D10Texture2D ), 
              (LPVOID*)&pBackBuffer ) ) )
    {
        Error("Failed to create back buffer.");
        return false;
    }

    if(FAILED( pDevice->CreateRenderTargetView( pBackBuffer, 
               NULL, &pRenderTargetView )))
    {
        Error("Failed to create render target view.");
        return false;
    }

    pBackBuffer->Release();

    pDevice->OMSetRenderTargets( 1, &pRenderTargetView, NULL );

    D3D10_VIEWPORT vp = {0, 0, width, height, 0, 1};
    pDevice->RSSetViewports( 1, &vp );
    
    return true; 
} 

void Application::Render(float deltaTime)
{
    pDevice->ClearRenderTargetView( pRenderTargetView, 
                                       D3DXVECTOR4(0, 0, 0, 1) );

    pSwapChain->Present( 0, 0 );
} 

void Application::MainLoop()
{ 
    MSG msg; 
    long prevTime = GetTickCount(), curTime = GetTickCount();
    while(true)
    {
        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if(msg.message==WM_QUIT)
                break;

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else{
            Render((curTime-prevTime)/1000.f);
            prevTime = curTime;
            curTime = GetTickCount();
        }
    }
} 

INT WINAPI WinMain( HINSTANCE , HINSTANCE , LPSTR , INT )
{ 
    Application app;
    
    if(app.CreateD3DWindow(640, 480))
    {
        if(app.Initialize())
        {
            app.MainLoop();
        }
    }
    
    return 0; 
}
© . All rights reserved.