Direct3D 11 Tutorial 1: Basics_Direct3D 11 教程1:基础
Github-LearnDirectX-DX3D11 tutorial01
概述
在这第一篇教程中,我们将通过介绍创建最小Direct3D应用程序所必需的元素。每一个Direct3D应用程序必需拥有这些元素才能正常地工作。这些元素包括设置窗口和设备对象,以及在窗口上显示颜色。
资源目录
(SDK root)\Samples\C++\Direct3D11\Tutorials\Tutorial01
设置Direct3D 11 设备
第一步是创建一个窗口和消息循环,这些在Direct3D 9, Direct3D 10, 和Direct3D 11都是相同的。有关此过程的介绍,请参阅Direct3D 10教程00:Win 32 Basics。现在我们有了一个正在显示的窗口,我们可以继续设置一个Direct3D 11设备。如果我们将要渲染任何3D场景,设置这个是有必要的。首先要做的是创建三个对象:一个设备(device),一个直接的上下文(immediate context),一个交换链(swap chain)。直接上下文是Direct3d 11中的一个新对象。
在Direct3D 10中,设备对象用于执行渲染和资源的创建。在Direct3D 11中,应用程序使用直接上下文对缓冲区执行渲染,设备中包含创建资源的方法。
交换链负责接收设备渲染的缓冲区,并在实际监视器屏幕上显示内容。交换链包含两个或多个缓冲区,主要是前面和后面。这些纹理是设备为了在监视器上显示而呈现的纹理。前台缓冲区是当前呈现给用户的内容。这个缓冲区是只能读,不能做修改。后台缓冲区是设备将要绘制的渲染目标。一旦设备完成了绘图操作,交换链将通过交换两个缓冲区来显示后台缓冲区。此时后台缓冲区变成了前台缓冲区,反之亦然。
为了创建交换链,我们填写 DXGI_SWAPCHAIN_DESC 结构来描述我们即将创建的交换链。有一些字段值得一提。BackBufferUsage是一个标志,它告诉应用程序如何使用后台缓冲区。 在这种情况下,我们想要渲染到后台缓冲区,因此我们将 BackBufferUsage 设置为 DXGI_USAGE_RENDER_TARGET_OUTPUT。OutputWindow字段表示交换链将用于在屏幕上显示图像的窗口。SampleDesc用于启用多次采样。 由于本教程不使用多次采样,因此SampleDesc的Count设置为1,Quality设置为0以禁用多次采样。
填写完描述后,我们可以调用D3D11CreateDeviceAndSwapChain函数为我们创建设备和交换链。以下是创建设备和交换链的代码:
DXGI_SWAP_CHAIN_DESC sd; ZeroMemory( &sd, sizeof(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; if( FAILED( D3D11CreateDeviceAndSwapChain( NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, featureLevels, numFeatureLevels, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, NULL, &g_pImmediateContext ) ) ) { return FALSE; }
接下来我们要做的事情是创建一个渲染目标的视图。
渲染目标视图是Direct3D 11里的一种资源视图。资源视图允许资源在特定场合绑定到图形管道上。将资源视图看成是C语言中的类型转换。C中的一块原始内存可以转换为任何数据类型。我们可以将这块内存转换为整数数组,浮点数组,结构数组,结构数组等等。如果我们不知道它的类型,原始内存本身对我们没有太大用处。Direct3D 11中的资源视图与该方法类似。例如,一个2D纹理,类似于原始内存块,是原始的底层资源。一旦我们拥有了该资源,我们就可以创建不同的资源视图,将该纹理绑定到不同格式的图形管道中的不同阶段:作为要渲染的渲染目标,作为将接收深度信息的深度模板缓冲区,或作为纹理资源。在C语言类型转换中允许以不同的方式使用内存块的情况下,Direct3D 11资源视图也是如此。
我们需要创建一个渲染目标视图,因为我们想将交换链中的后台缓冲区绑定为我们的渲染目标。这样能够使Direct3D 11渲染到它上面。我们首先调用GetBuffer()来获取后台缓冲区对象。或者,我们可以填写D3D11_RENDERTARGETVIEW_DESC结构,该结构描述要创建的渲染目标视图。此描述通常是CreateRenderTargetView的第二个参数。然而,对于这些教程,默认的渲染目标视图已经足够了。可以通过传递NULL作为第二个参数来获取默认的渲染目标视图。一旦我们创建了渲染目标视图,我们就可以在直接上下文中调用OMSetRenderTargets()来将它绑定到管道上。这可确保管道呈现的输出被写入后台缓冲区。创建和设置渲染目标视图的代码如下:
// Create a render target view ID3D11Texture2D *pBackBuffer; if( FAILED( g_pSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), (LPVOID*)&pBackBuffer ) ) ) return FALSE; hr = g_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &g_pRenderTargetView ); pBackBuffer->Release(); if( FAILED( hr ) ) return FALSE; g_pImmediateContext->OMSetRenderTargets( 1, &g_pRenderTargetView, NULL );
在Direct3D 11渲染之前我们需要设置的最后一件事是初始化视口。
视口映射剪辑空间坐标,其中X和Y的范围为-1到1,Z的范围为0到1,以渲染目标空间,有时称为像素空间。 在Direct3D 9中,如果应用程序未设置视口,则默认视口设置为与渲染目标的大小相同。在Direct3D 11中,默认情况下不设置视口。 因此,我们必须在屏幕上看到任何内容之前这样做。由于我们希望将整个渲染目标用于输出,因此我们将左上角的点设置为(0,0),将宽度和高度设置为与渲染目标的大小相同。 为此,请使用以下代码:
D3D11_VIEWPORT vp; vp.Width = (FLOAT)width; vp.Height = (FLOAT)height; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = 0; vp.TopLeftY = 0; g_pImmediateContext->RSSetViewports( 1, &vp );
修改消息循环
我们已经设置了窗口和Direct3D 11设备并且我们已准备好渲染。 但是,我们的消息循环仍然存在问题:它使用GetMessage()来获取消息。GetMessage()的问题在于,如果应用程序窗口的队列中没有消息,则GetMessage()会阻塞,并且在消息可用之前不会返回。因此,当消息队列为空时,我们的应用程序在GetMessage()内等待,而不是像渲染那样。我们可以使用PeekMessage()而不是GetMessage()来解决这个问题。 PeekMessage()可以检索像GetMessage()那样的消息,但是当没有消息等待时,PeekMessage()会立即返回而不是阻塞。 然后我们可以花时间做一些渲染。 使用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 } }
渲染代码
渲染在Render()函数中完成。
在这个教程中,我们将渲染最简单的场景,即用单一颜色填充屏幕。在Direct3D 11中,使用单一颜色填充渲染目标的简单方法是使用直接上下文的ClearRenderTargetView()方法。
- 首先,我们定义一个由四个浮点数组成的数组,用于描述我们想要填充屏幕的颜色。
- 接着,我们将它传递给ClearRenderTargetView()。在这个例子中,我们选择了蓝色的阴影。
- 填充后台缓冲区后,我们调用交换链的Present()方法来完成渲染。 Present()负责将交换链的后台缓冲区内容显示在屏幕上,以便用户可以看到它。Render()函数看起来像是这样的:
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 ); }
本教程最终的运行效果:
小结
本教程是我在学习DirectX中查看SDK文档中看到的,因此想在国庆期间翻译成中文,方便以后查阅。
教程总共有七篇:
希望翻译过的教程能对学习DX的朋友们有所帮助,本教程的源码我已上传至Github(在本文首部)。
您的资助是我最大的动力!
金额随意,欢迎来赏!
本博客的所有打赏均将用于博主女朋友的化妆品购买以及养肥计划O(∩_∩)O。我是【~不会飞的章鱼~】!