• 初学Direct X (2)


    初学Direct X (2)


    这一次要学习如何现实位图,尽管看过对双缓冲机制还有很多疑问,但是这并不阻碍我对他的入门了解

    Direct3D提供了一个双重/后台缓冲区,在调用CreateDevice之时就有了。其运行机制就是,我们需要在其中一个缓冲区上绘制完所需要显示的一切,之后将这个缓冲区快速的复制到另一个缓冲区,因为后者的缓冲区被称为Front Buffer,显示器在刷新之时会直接从中取出数据以显示,而前者就称之为backbuffer

    但是,还有一种buffer,是可供我们在内存中进行操作的,被称之为surface,也有离屏表面的称呼,后面的表面都指的是离屏表面

    1. 使用表面的实例

    初始化变量:

    LPDIRECT3D9 d3d = NULL;
    LPDIRECT3DDEVICE9 d3ddev = NULL;
    LPDIRECT3DSURFACE9 backbuffer = NULL;
    // 指向内存中的表面
    LPDIRECT3DSURFACE9 surface = NULL;
    

    1.1 Game_Init()

    bool Game_Init(HWND hwnd)
    {
    	//initialize Direct3D
    	d3d = Direct3DCreate9(D3D_SDK_VERSION);
    	if (d3d == NULL)
    	{
    		MessageBox(hwnd, "Error initializing Direct3D", "Error", MB_OK);
    		return false;
    	}
    
    	//set Direct3D presentation parameters
    	D3DPRESENT_PARAMETERS d3dpp;
    	
    	d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&dm);
    
    	ZeroMemory(&d3dpp, sizeof(d3dpp));
    	d3dpp.Windowed = FALSE;
    	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    	d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    	d3dpp.BackBufferCount = 1;
    	d3dpp.BackBufferWidth = dm.Width;
    	d3dpp.BackBufferHeight = dm.Height;
    	d3dpp.hDeviceWindow = hwnd;
    
    	//create Direct3D device
    	d3d->CreateDevice(D3DADAPTER_DEFAULT,
    		D3DDEVTYPE_HAL, hwnd,
    		D3DCREATE_SOFTWARE_VERTEXPROCESSING,
    		&d3dpp, &d3ddev);
    
    	if (!d3ddev)
    	{
    		MessageBox(hwnd, "Error creating Direct3D device", "Error", MB_OK);
    		return false;
    	}
    
    	//set random number seed
    	// 用于Game_Run()
    	srand((unsigned int)time(NULL));
    
    	//clear the backbuffer to black
    	//注意这里并没有获取到backbuffer指针,就可以直接Clear backbuffer
    	d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    
    	//create pointer to the back buffer
    	//通常这是指向真实后台缓冲区的指针
    	d3ddev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer);
    
    	//create surface
    	HRESULT result = d3ddev->CreateOffscreenPlainSurface(
    		100,                //width of the surface
    		100,                //height of the surface
    		D3DFMT_X8R8G8B8,    //surface format
    		D3DPOOL_DEFAULT,    //memory pool to use
    		&surface,           //pointer to the surface
    		NULL);              //reserved (always NULL)
    	
    	if (result != D3D_OK) return false;
    
    	return true;
    }
    

    这样一来,就获取了backbuffer的位置,并且创造了一个离屏表面,可供我们进行绘图操作

    1.2 Game_Run()

    void Game_Run(HWND hwnd)
    {
    	//make sure the Direct3D device is valid
    	if (!d3ddev) return;
    
    	//start rendering
    	if (d3ddev->BeginScene())
    	{
    		//将离屏表面填色
    		int r = rand() % 255;
    		int g = rand() % 255;
    		int b = rand() % 255; 
    		d3ddev->ColorFill(surface, NULL, D3DCOLOR_XRGB(r, g, b));
    		
    		//把离屏表面复制到backbuffer
    		RECT rect;
    		rect.left = rand() % dm.Width;
    		rect.right = rect.left + rand() % (dm.Width - rect.left);
    		rect.top = rand() % dm.Height;
    		rect.bottom = rect.top + rand() % (dm.Height - rect.top);
    		d3ddev->StretchRect(surface, NULL, backbuffer, &rect, D3DTEXF_NONE);
    		
    		//stop rendering
    		d3ddev->EndScene();
    
    		//将backbuffer的数据显示到屏幕上
    		d3ddev->Present(NULL, NULL, NULL, NULL);
    	}
    
    	//check for escape key (to exit program)
    	if (KEY_DOWN(VK_ESCAPE))
    		PostMessage(hwnd, WM_DESTROY, 0, 0);
    }
    

    1.3写到这里的时候我发现几个问题

    1) 似乎无法操作front buffer?
    这正是双缓冲的机制,因为直接操作front buffer会导致一些问题,想象一下,若屏幕的刷新频率很快,而此时你正在生成一个计算量巨大的数据,正在你计算之时,显示器已经来尝试取n次数据了,而这n次取出的图像都是一部分且不连贯的。故而出现了双缓冲技术,在backbuffer绘制好之后再将其显示在屏幕上

    2)backbuffer和frontbuffer的是怎么运转的,好像只要调用Present就可以将backbuffer的显示在屏幕上了

    2. 加载位图

    需要使用d3dx,它是Direct3D Extensions 即Direct3D的扩展,需要进行如下的引用:

    #include <d3dx9.h>
    #pragma comment(lib,"d3dx9.lib")
    

    有趣的就是D3DXLoadSurfaceFromFile函数了,他可以装载.bmp , .jpng, .png等一系列的位图,调用示例如下:

    result = D3DXLoadSurfaceFromFile(
    		surface,
    		NULL,
    		NULL,
    		"show.png",
    		NULL,
    		D3DX_DEFAULT,
    		0,
    		NULL
    		);
    

    3. 绘制表面

    StretchRect函数可以将某个表面的一部分或全部位块传输到另一个表面

    HRESULT StretchRect(
    	IDirect3DSurface9* pSourceSurface,
    	CONST RECT* pSourceRect,
    	IDirect3DSurface9* pDestSurface,
    	CONST RECT* pDestRect,
    	D3DTEXTUREFILTERTYPE Filter)
    

    这里值得一提的是,若rect超出了pDestSurface的显示范围,函数还是可以正常运行的,下面是调用示例:

    	d3ddev->StretchRect(surface, NULL, backbuffer, &rect, D3DTEXF_NONE);
    
  • 相关阅读:
    [自用] 数论和组合计数类数学相关(定理&证明&板子)
    OI回忆录?
    [UOJ310] 黎明前的巧克力
    [总结] 后缀自动机学习笔记
    [总结] 动态点分治学习笔记
    [HEOI2018] 秘密袭击coat
    [51nod1355] 斐波那契的最小公倍数
    [SRM601] WinterAndSnowmen
    [总结] 二项式反演学习笔记
    [Luogu4705] 玩游戏
  • 原文地址:https://www.cnblogs.com/leihui/p/8898833.html
Copyright © 2020-2023  润新知