Putting It All Together

Putting It All Together

掌握了初始化Direct3D的细节之后,我们就可以开始创建第一个Direct3D应用程序了。首先需要在Game类中添加Direct3D initialization部分的代码,然后创建Game类的一个具体的派生类,并在WinMain()函数中使用这个派生类。下面的开始讲述这几个步骤。

Updated General-Purpose Game Class

列表11.10中列出了Game.h头文件的部分代码。在第10章“Project Setup and Window Initialization”的window initialization基础上增加了Direct3d initialization部分的代码。在本书配套网站上提供了完全的代码。
列表11.10 An Abbreviated Game.h Header File

#pragma once
#include "Common.h"
#include "GameClock.h"
#include "GameTime.h"
namespace Library
{
	class Game
	{
	public:
		/* ... Previously presented members removed for brevity ... */
		ID3D11Device1* Direct3DDevice() const;
		ID3D11DeviceContext1* Direct3DDeviceContext() const;
		bool DepthBufferEnabled() const;
		bool IsFullScreen() const;
		const D3D11_TEXTURE2D_DESC& BackBufferDesc() const;
		const D3D11_VIEWPORT& Viewport() const;
	protected:
		/* ... Previously presented members removed for brevity ... */
		virtual void InitializeDirectX();
		static const UINT DefaultFrameRate;
		static const UINT DefaultMultiSamplingCount;
		D3D_FEATURE_LEVEL mFeatureLevel;
		ID3D11Device1* mDirect3DDevice;
		ID3D11DeviceContext1* mDirect3DDeviceContext;
		IDXGISwapChain1* mSwapChain;
		UINT mFrameRate;
		bool mIsFullScreen;
		bool mDepthStencilBufferEnabled;
		bool mMultiSamplingEnabled;
		UINT mMultiSamplingCount;
		UINT mMultiSamplingQualityLevels;
		ID3D11Texture2D* mDepthStencilBuffer;
		D3D11_TEXTURE2D_DESC mBackBufferDesc;
		ID3D11RenderTargetView* mRenderTargetView;
		ID3D11DepthStencilView* mDepthStencilView;
		D3D11_VIEWPORT mViewport;
	};
}



如以上代码所示,Game类已经包含了Direct3D initialization相关的成员变量,用于表示Direct3D device和device context,multisampling settings,depth-stencil buffer和depth-stencil view,render target以及viewport。所有成员变量都不是public变量,不能从外部访问,并通过调用InitializeDirectX()函数初始化Direct3D。在列出Game类的实现代码之前,先讨论一下Game.h中引用的头文件Common.h。Common.h头文件中主要是一些通用的声明(如列表11.11所示)。
列表11.11 The Common.h Header File
#pragma once

#include <windows.h>
#include <exception>
#include <cassert>
#include <string>
#include <vector>
#include <map>
#include <memory>

#include <d3d11_1.h>
#include <DirectXMath.h>
#include <DirectXPackedVector.h>

#define DeleteObject(object) if((object) != NULL) { delete object; object = NULL; }
#define DeleteObjects(objects) if((objects) != NULL) { delete[] objects; objects = NULL; }
#define ReleaseObject(object) if((object) != NULL) { object->Release(); object = NULL; }

namespace Library
{
    typedef unsigned char byte;
}

using namespace DirectX;
using namespace DirectX::PackedVector;



其中头文件d3d11_1.h,DirectXMath.h和DirectXPackedVector.h包含了Direct3D 11.1和DirectXMath的声明。DirectXMath包含于DirectX和DirectX::PackedVector命名空间中,因此包含了这两个命名空间的声明。
列表11.12列出了Game类修改后的实现代码。与列表11.10一样,还是只列出了新增的部分代码,略去了之前已经展示过的部分。
列表11.12 An Abbreviated Game.cpp File
#include "Game.h"
#include "GameException.h"

namespace Library
{
	const UINT Game::DefaultScreenWidth = 1024;
	const UINT Game::DefaultScreenHeight = 768;
	const UINT Game::DefaultFrameRate = 60;
	const UINT Game::DefaultMultiSamplingCount = 4;	

	Game::Game(HINSTANCE instance, const std::wstring& windowClass, const std::wstring& windowTitle, int showCommand)
		: mInstance(instance), mWindowClass(windowClass), mWindowTitle(windowTitle), mShowCommand(showCommand),
		mWindowHandle(), mWindow(),
		mScreenWidth(DefaultScreenWidth), mScreenHeight(DefaultScreenHeight),
		mGameClock(), mGameTime(),
		mFeatureLevel(D3D_FEATURE_LEVEL_9_1), mDirect3DDevice(nullptr), mDirect3DDeviceContext(nullptr), mSwapChain(nullptr),  
		mFrameRate(DefaultFrameRate), mIsFullScreen(false),
		mDepthStencilBufferEnabled(false), mMultiSamplingEnabled(false), mMultiSamplingCount(DefaultMultiSamplingCount), mMultiSamplingQualityLevels(0), 
		mDepthStencilBuffer(nullptr), mRenderTargetView(nullptr), mDepthStencilView(nullptr), mViewport()
	{
	}

	void Game::Run()
	{
		InitializeWindow();
		InitializeDirectX();
		Initialize();

		MSG message;
		ZeroMemory(&message, sizeof(message));

		mGameClock.Reset();		

		while(message.message != WM_QUIT)
		{
			if (PeekMessage(&message, nullptr, 0, 0, PM_REMOVE))
			{
				TranslateMessage(&message);
				DispatchMessage(&message);
			}
			else
			{
				mGameClock.UpdateGameTime(mGameTime);
				Update(mGameTime);
				Draw(mGameTime);
			}
		}

		Shutdown();
	}

	void Game::Shutdown()
	{
		ReleaseObject(mRenderTargetView);
		ReleaseObject(mDepthStencilView);
		ReleaseObject(mSwapChain);
		ReleaseObject(mDepthStencilBuffer);

		if (mDirect3DDeviceContext != nullptr)
		{
			mDirect3DDeviceContext->ClearState();
		}

		ReleaseObject(mDirect3DDeviceContext);
		ReleaseObject(mDirect3DDevice);

		UnregisterClass(mWindowClass.c_str(), mWindow.hInstance);
	}

	void Game::InitializeDirectX()
	{
		HRESULT hr;
		UINT createDeviceFlags = 0;

#if defined(DEBUG) || defined(_DEBUG)  
		createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

		D3D_FEATURE_LEVEL featureLevels[] = {
			D3D_FEATURE_LEVEL_11_0,
			D3D_FEATURE_LEVEL_10_1,
			D3D_FEATURE_LEVEL_10_0
		};

		// Step 1: Create the Direct3D device and device context interfaces
		ID3D11Device* direct3DDevice = nullptr;
		ID3D11DeviceContext* direct3DDeviceContext = nullptr;
		if (FAILED(hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, &direct3DDevice, &mFeatureLevel, &direct3DDeviceContext)))
		{
			throw GameException("D3D11CreateDevice() failed", hr);
		}

		if (FAILED(hr = direct3DDevice->QueryInterface(__uuidof(ID3D11Device1), reinterpret_cast<void**>(&mDirect3DDevice))))
		{
			throw GameException("ID3D11Device::QueryInterface() failed", hr);
		}

		if (FAILED(hr = direct3DDeviceContext->QueryInterface(__uuidof(ID3D11DeviceContext1), reinterpret_cast<void**>(&mDirect3DDeviceContext))))
		{
			throw GameException("ID3D11Device::QueryInterface() failed", hr);
		}

		ReleaseObject(direct3DDevice);
		ReleaseObject(direct3DDeviceContext);

		// Step 2: Check for multisampling support
		mDirect3DDevice->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM, mMultiSamplingCount, &mMultiSamplingQualityLevels);
		if (mMultiSamplingQualityLevels == 0)
		{
			throw GameException("Unsupported multi-sampling quality");
		}

		DXGI_SWAP_CHAIN_DESC1 swapChainDesc;
		ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
		swapChainDesc.Width = mScreenWidth;
		swapChainDesc.Height = mScreenHeight;
		swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;

		if (mMultiSamplingEnabled)
		{
			swapChainDesc.SampleDesc.Count = mMultiSamplingCount;
			swapChainDesc.SampleDesc.Quality = mMultiSamplingQualityLevels - 1;
		}
		else
		{
			swapChainDesc.SampleDesc.Count = 1;
			swapChainDesc.SampleDesc.Quality = 0;
		}

		swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
		swapChainDesc.BufferCount = 1;
		swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

		// Step 3: Create the swap chain
		IDXGIDevice* dxgiDevice = nullptr;
		if (FAILED(hr = mDirect3DDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgiDevice))))
		{
			throw GameException("ID3D11Device::QueryInterface() failed", hr);
		}

		IDXGIAdapter *dxgiAdapter = nullptr;
		if (FAILED(hr = dxgiDevice->GetParent(__uuidof(IDXGIAdapter),reinterpret_cast<void**>(&dxgiAdapter))))
		{
			ReleaseObject(dxgiDevice);
			throw GameException("IDXGIDevice::GetParent() failed retrieving adapter.", hr);
		}

		IDXGIFactory2* dxgiFactory = nullptr;		
		if (FAILED(hr = dxgiAdapter->GetParent(__uuidof(IDXGIFactory2), reinterpret_cast<void**>(&dxgiFactory))))
		{
			ReleaseObject(dxgiDevice);
			ReleaseObject(dxgiAdapter);
			throw GameException("IDXGIAdapter::GetParent() failed retrieving factory.", hr);
		}

		DXGI_SWAP_CHAIN_FULLSCREEN_DESC fullScreenDesc;
		ZeroMemory(&fullScreenDesc, sizeof(fullScreenDesc));
		fullScreenDesc.RefreshRate.Numerator = mFrameRate;
		fullScreenDesc.RefreshRate.Denominator = 1;
		fullScreenDesc.Windowed = !mIsFullScreen;

		if (FAILED(hr = dxgiFactory->CreateSwapChainForHwnd(dxgiDevice, mWindowHandle, &swapChainDesc, &fullScreenDesc, nullptr, &mSwapChain)))
		{
			ReleaseObject(dxgiDevice);
			ReleaseObject(dxgiAdapter);
			ReleaseObject(dxgiFactory);
			throw GameException("IDXGIDevice::CreateSwapChainForHwnd() failed.", hr);
		}

		ReleaseObject(dxgiDevice);
		ReleaseObject(dxgiAdapter);
		ReleaseObject(dxgiFactory);

		// Step 4: Create the render target view
		ID3D11Texture2D* backBuffer;
		if (FAILED(hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&backBuffer))))
		{
			throw GameException("IDXGISwapChain::GetBuffer() failed.", hr);
		}

		backBuffer->GetDesc(&mBackBufferDesc);

		if (FAILED(hr = mDirect3DDevice->CreateRenderTargetView(backBuffer, nullptr, &mRenderTargetView)))
		{
			ReleaseObject(backBuffer);
			throw GameException("IDXGIDevice::CreateRenderTargetView() failed.", hr);
		}

		ReleaseObject(backBuffer);

		// Step 5: Create the depth-stencil view
		if (mDepthStencilBufferEnabled)
		{
			D3D11_TEXTURE2D_DESC depthStencilDesc;
			ZeroMemory(&depthStencilDesc, sizeof(depthStencilDesc));
			depthStencilDesc.Width = mScreenWidth;
			depthStencilDesc.Height = mScreenHeight;
			depthStencilDesc.MipLevels = 1;
			depthStencilDesc.ArraySize = 1;
			depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
			depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
			depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;            

			if (mMultiSamplingEnabled)
			{
				depthStencilDesc.SampleDesc.Count = mMultiSamplingCount;
				depthStencilDesc.SampleDesc.Quality = mMultiSamplingQualityLevels - 1;
			}
			else
			{
				depthStencilDesc.SampleDesc.Count = 1;
				depthStencilDesc.SampleDesc.Quality = 0;
			}

			if (FAILED(hr = mDirect3DDevice->CreateTexture2D(&depthStencilDesc, nullptr, &mDepthStencilBuffer)))
			{
				throw GameException("IDXGIDevice::CreateTexture2D() failed.", hr);
			}

			if (FAILED(hr = mDirect3DDevice->CreateDepthStencilView(mDepthStencilBuffer, nullptr, &mDepthStencilView)))
			{
				throw GameException("IDXGIDevice::CreateDepthStencilView() failed.", hr);
			}
		}
		
		// Step 6: Bind the render target and depth-stencil views to OM stage
		mDirect3DDeviceContext->OMSetRenderTargets(1, &mRenderTargetView, mDepthStencilView);

		mViewport.TopLeftX = 0.0f;
		mViewport.TopLeftY = 0.0f;
		mViewport.Width = static_cast<float>(mScreenWidth);
		mViewport.Height = static_cast<float>(mScreenHeight);
		mViewport.MinDepth = 0.0f;
		mViewport.MaxDepth = 1.0f;

		// Step 7: Set the viewport
		mDirect3DDeviceContext->RSSetViewports(1, &mViewport);
	}
}



列表11.12中的代码包含Game类的构造函数,Shutdown()函数和InitializeDirectX()函数。其中构造函数初始化所有的成员变量;Shutdown()函数释放在InitializeDirectX()函数中实例化的所有Direct3D对象。Run()函数中增加了对Direct initialization函数的调用。需要注意的是,InitializeDirectX(),Shutdown()和Run()函数都是虚函数,因此可以在派生类中覆盖。这些函数的实现代码是Game类的通用实现。

Specialized Game Class

对于所有的示例工程,都创建一个派生自Game类的具体版本,对应于Visual Studio solution中的一个可执行程序工程。派生类一般地会覆盖通用函数Initialize()和Update()以及Draw(),这些函数在基类的Run()函数中被调用。基类Game中这三个函数的实现代码目前都是空白的。在下一章会讨论game components(游戏组件)的概念,game components是一种在程序中模块化增加功能的方法,那时将会实现这三个函数用于initialize,update,draw所有活动的组件。
列表11.13列出RenderGame类的头文件,该类继承自Library::Game类。RenderGame应该在Game project中创建,而不是在Library project中,Library project包含了目前大部分代码。
列表11.13 The RenderingGame.h File
#pragma once

#include "Common.h"
#include "Game.h"

using namespace Library;

namespace Rendering
{
    class RenderingGame : public Game
    {
    public:
        RenderingGame(HINSTANCE instance, const std::wstring& windowClass, const std::wstring& windowTitle, int showCommand);
        ~RenderingGame();

        virtual void Initialize() override;
        virtual void Update(const GameTime& gameTime) override;
        virtual void Draw(const GameTime& gameTime) override;

    private:
        static const XMVECTORF32 BackgroundColor;
    };
}


RenderGame.h头文件中唯一一点需要特别说明的是XMVECTORF32类型的成员变量BackgroundColor。XMVECTORF32是一个支持SIMD加速的DirectXMath类型,可以使用C++语法进行初始化。列表11.14显示了RenderGame类的实现代码
列表11.14 The RenderingGame.cpp File
#include "RenderingGame.h"
#include "GameException.h"

namespace Rendering
{
    const XMVECTORF32 RenderingGame::BackgroundColor = { 0.392f, 0.584f, 0.929f, 1.0f };

    RenderingGame::RenderingGame(HINSTANCE instance, const std::wstring& windowClass, const std::wstring& windowTitle, int showCommand)
        :  Game(instance, windowClass, windowTitle, showCommand)		   
    {
        mDepthStencilBufferEnabled = true;
        mMultiSamplingEnabled = true;
    }

    RenderingGame::~RenderingGame()
    {
    }

    void RenderingGame::Initialize()
    {
        Game::Initialize();
    }

    void RenderingGame::Update(const GameTime &gameTime)
    {
        Game::Update(gameTime);
    }

    void RenderingGame::Draw(const GameTime &gameTime)
    {
        mDirect3DDeviceContext->ClearRenderTargetView(mRenderTargetView, reinterpret_cast<const float*>(&BackgroundColor));
        mDirect3DDeviceContext->ClearDepthStencilView(mDepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);

        Game::Draw(gameTime);

        HRESULT hr = mSwapChain->Present(0, 0);
        if (FAILED(hr))
        {
            throw GameException("IDXGISwapChain::Present() failed.", hr);
        }
    }
}

RenderGame类在一个空白的屏幕上渲染一种cornflower blue颜色(矢车菊蓝)(或者通过指定BackgroundColor变量值使用其他的任何颜色)。这段代码显示了Game的派生类通用结构,通过扩展Initialize()和Update()函数可以可以创建功能更加丰富的应用程序。
在RenderGame类的构造函数中,通过设置变量的初始值,允许使用depth-stencil buffer以及multisampling(尽管在这个例子中并没具体用到)。在Draw()函数中,通过调用ID3D11DeviceContext1::ClearRenderTargetView()函数重置render target。该函数把整个render target设置为参数指定的颜色。接下来,调用ID3DDeviceContext1::ClearDepthStencilView()函数重置depth-stencil buffer。ClearDepathStencilView()函数的第二个参数值,由一个或多个D3D11_CLEAR_FLAG枚举常量通过按位于运算组合而成,用于指定depth-stencile buffer中要重置的部分。第三个参数指定depth buffer的重置值(一般为1.0),而第四个参数指定stencil buffer的重置值(一般为0)。
在重置了render target和depth-stencil view之后,就开始执行具体游戏的渲染函数。在列表14.14中,对Game::Draw()函数的调用是为了渲染所有可见的游戏组件。关于游戏组件的内容,将在下一章讨论。当完成了所有具体游戏的渲染操作之后,就可以调用IDXGISwapChain1::Present()函数对swap chain中的buffers进行翻转。Present()函数的第一个参数表示,相对于显示器的垂直刷新,该帧的显示方式。参数值0表示该帧会立即显示,而不用与显示器的垂直刷新同步。第二个参数是一组DXGI_PRSENT枚举常量按位于的计算结果。如果参数设为0,就简单地把图像帧显示到输出窗口上,而不需要任何特别的选项。在MSDN在线文档中,可以查看更多高级的垂直刷新同和显示设置。

Updated WinMain()

在运行这个示例应用程序之前,还有最后一步要完成,就是修改WinMain()函数,在函数中使用新的RenderGame类。修改非常简单,只需要使用RenderGame.h替换Game.h头文件并把变量game更新到对应的RenderingGame类的实例。列表11.15列出了Program.cpp文件中修改后的WinMain()代码。
列表11.15 The Program.cpp File

#include <memory>
#include "GameException.h"
#include "RenderingGame.h"

#if defined(DEBUG) || defined(_DEBUG)
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif

using namespace Library;
using namespace Rendering;

int WINAPI WinMain(HINSTANCE instance, HINSTANCE previousInstance, LPSTR commandLine, int showCommand)
{
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif

    std::unique_ptr<RenderingGame> game(new RenderingGame(instance, L"RenderingClass", L"Real-Time 3D Rendering", showCommand));

    try
    {
        game->Run();
    }
    catch (GameException ex)
    {
        MessageBox(game->WindowHandle(), ex.whatw().c_str(), game->WindowTitle().c_str(), MB_ABORTRETRYIGNORE);
    }

    return 0;
}


编译并运行示例程序,就能得到与图11.2所示的结果。


图11.2 A solid-color window rendered with Direct3D using the demo framework.
同样,该示例程序依然很单调,但是已经完成了大部分准备工作,下一阶段开始使用Direct3D进行渲染更丰富的场景。

总结

本章主要讲述了Direct3D的初始化。首先,详细描述了Direct3D C++ API,并使用这些API完成了渲染引擎的基础框架。虽然还有很多系统模块没有实现,但是已经成功完成了第一个Direct3D应用程序。
下一章,将创建game组件的接口,实现鼠标和键盘输入系统,创建一个动态的3D camera,并学习如何把2D文本渲染到屏幕上。

Exercises

1. From within the debugger, walk through the code used to initialize Direct3D, to better
understand the initialization process. You’ll find an associated demo application on the book’s
companion website.

1.以调试模式运行程序,并一步步查看Direct3D初始化部分的代码,以便更好了理解初始化的过程。本书配套网站提供了对应的示例程序。

本章配套学习代码

http://download.csdn.net/detail/chenjinxian_3d/9578023

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
图像识别技术在病虫害检测中的应用是一个快速发展的领域,它结合了计算机视觉和机器学习算法来自动识别和分类植物上的病虫害。以下是这一技术的一些关键步骤和组成部分: 1. **数据收集**:首先需要收集大量的植物图像数据,这些数据包括健康植物的图像以及受不同病虫害影响的植物图像。 2. **图像预处理**:对收集到的图像进行处理,以提高后续分析的准确性。这可能包括调整亮度、对比度、去噪、裁剪、缩放等。 3. **特征提取**:从图像中提取有助于识别病虫害的特征。这些特征可能包括颜色、纹理、形状、边缘等。 4. **模型训练**:使用机器学习算法(如支持向量机、随机森林、卷积神经网络等)来训练模型。训练过程中,算法会学习如何根据提取的特征来识别不同的病虫害。 5. **模型验证和测试**:在独立的测试集上验证模型的性能,以确保其准确性和泛化能力。 6. **部署和应用**:将训练好的模型部署到实际的病虫害检测系统中,可以是移动应用、网页服务或集成到智能农业设备中。 7. **实时监测**:在实际应用中,系统可以实时接收植物图像,并快速给出病虫害的检测结果。 8. **持续学习**:随着时间的推移,系统可以不断学习新的病虫害样本,以提高其识别能力。 9. **用户界面**:为了方便用户使用,通常会有一个用户友好的界面,显示检测结果,并提供进一步的指导或建议。 这项技术的优势在于它可以快速、准确地识别出病虫害,甚至在早期阶段就能发现问题,从而及时采取措施。此外,它还可以减少对化学农药的依赖,支持可持续农业发展。随着技术的不断进步,图像识别在病虫害检测中的应用将越来越广泛。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值