簡單渲染流水管線C++代碼實現(一)---雙緩衝技術

最近非常喜歡遊戲開發中的渲染流水管線,因此,特地網上看了很多資料,閒暇時間想利用C++的桌面開發將其慢慢實現它,廢話不多說,下面就來個開胃的,直接貼代碼,圖片資料放在了文末

ps:本篇主要是創建一個窗口,然後利用雙緩衝技術實現一個簡單的移動遊戲,後續會慢慢更新

#include <iostream>
#include <windows.h>

#pragma comment(lib, "Msimg32.lib")

//定義一些窗口常量
#define _CLIENT_PW 640   //屏幕像素寬
#define _CLIENT_PH 480   //屏幕像素高
#define _FRAME_PER_SECOND 40  //屏幕每秒刷新幀數
#define _FRAME_SLEEPTIME (1000/_FRAME_PER_SECOND)  //每幀休息時間

//全局窗口句柄
HWND g_hWnd = nullptr;
//窗口活動
BOOL g_Active = FALSE;
//函數聲明
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void GameInit();
void GameRun();
void GameEnd();

//程序入口點
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	//填充窗口類別結構體
	WNDCLASS wc;
	wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
	wc.lpfnWndProc = WindowProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
	wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
	wc.lpszMenuName = nullptr;
	wc.lpszClassName = __TEXT("My3D");

	//註冊窗口類別結構體
	RegisterClass(&wc);

	//根據客戶區矩形計算窗口矩形
	int screen_pw = GetSystemMetrics(SM_CXSCREEN);
	int screen_ph = GetSystemMetrics(SM_CYSCREEN);
	RECT r =
	{
		(screen_pw - _CLIENT_PW) / 2,
		(screen_ph - _CLIENT_PH) / 2,
		r.left + _CLIENT_PW,
		r.top + _CLIENT_PH
	};
	//校準窗口矩形
	AdjustWindowRect(&r, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, FALSE);

	//創建窗口
	g_hWnd = CreateWindow(
		wc.lpszClassName,
		__TEXT(""),
		WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
		r.left, r.top, r.right - r.left, r.bottom - r.top,  //窗口左上角位置和寬高
		HWND_DESKTOP,
		NULL,
		wc.hInstance,
		NULL);

	//更新窗口
	UpdateWindow(g_hWnd);

	//顯示窗口
	ShowWindow(g_hWnd, nCmdShow);

	//遊戲初始化
	GameInit();

	//消息循環
	MSG msg = {};
	while (WM_QUIT != msg.message)
	{
		//下面的代碼表示,一旦有消息就調用消息處理函數來處理消息
		if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		//當沒有消息且窗口激活那麼就執行遊戲循環
		else if (TRUE == g_Active)
		{
			//遊戲運行
			GameRun();
		}
		//當沒有消息且窗口未激活就等待消息
		else
			WaitMessage();
	}

	//遊戲結束
	GameEnd();

	return (int)msg.wParam;
}

//消息回調函數
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_ACTIVATEAPP:
	{
		g_Active = static_cast<BOOL>(wParam);
		return 0;
	}
	case WM_DESTROY:
	{
		PostQuitMessage(1);
		return 0;
	}
	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
//主顯示設備
HDC g_MainDC = nullptr;
//後備顯示設備
HDC g_BackDC = nullptr;

//位圖設備
HDC g_BmpDC[2];

//英雄位置
int x = _CLIENT_PW / 2, y = _CLIENT_PH / 2;

void GameInit()
{
	//得到主顯示設備
	g_MainDC = GetDC(g_hWnd);

	//創建後備顯示設備
	g_BackDC = CreateCompatibleDC(g_MainDC);

	//創建和主顯示設備兼容的位圖
	HBITMAP hbmp = CreateCompatibleBitmap(g_MainDC, _CLIENT_PW, _CLIENT_PH);

	//將兼容位圖選入兼容設備,刪除老的位圖
	DeleteObject(SelectObject(g_BackDC, hbmp));

	//創建位圖
	for (int i = 0; i < 2; ++i)
	{
		char fn[32];
		sprintf_s(fn, "%c.bmp", i + 'a');
		HBITMAP hbmp = static_cast<HBITMAP>(LoadImageA(nullptr, static_cast<LPCSTR>(fn), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE));
		g_BmpDC[i] = CreateCompatibleDC(g_MainDC);
		DeleteObject(SelectObject(g_BmpDC[i], hbmp));
	}
}

void GameRun()
{
	//得到從操作系統啓動一瞬間到目前的毫秒數
	ULONGLONG begin_time = GetTickCount64();

	//BitBlt()將一幅位圖從一個設備場景複製到另一個。源和目標DC相互間必須兼容
	BitBlt(
		g_BackDC,
		0, 0, _CLIENT_PW, _CLIENT_PH,
		g_BmpDC[0],
		0, 0,
		SRCCOPY);

	//位圖透明化
	TransparentBlt(
		g_BackDC,
		x - 50, y - 50, 100, 100,
		g_BmpDC[1],
		0, 0, 100, 100,
		RGB(0xff, 0xff, 0xff));

	if (GetAsyncKeyState('W') & 0x8000)
		y -= 5;
	if (GetAsyncKeyState('S') & 0x8000)
		y += 5;
	if (GetAsyncKeyState('A') & 0x8000)
		x -= 5;
	if (GetAsyncKeyState('D') & 0x8000)
		x += 5;
	if (GetAsyncKeyState(VK_LBUTTON) & 1)
	{
		//得到當前光標相對於桌面的座標
		POINT p;
		GetCursorPos(&p);

		//將桌面的座標轉換到客戶區的座標
		ScreenToClient(g_hWnd, &p);

		x = p.x;
		y = p.y;
	}
	//後備顯示設備顏色傳輸給主顯示設備
	BitBlt(
		g_MainDC,
		0, 0, _CLIENT_PW, _CLIENT_PH,
		g_BackDC,
		0, 0,
		SRCCOPY);

	//得到本次遊戲循環總的消耗時間
	ULONGLONG frame_time = GetTickCount() - begin_time;

	//進行本次遊戲循環休息
	if (_FRAME_SLEEPTIME > frame_time)
		Sleep(_FRAME_SLEEPTIME - frame_time);
	else
		Sleep(1);  //必須休息一下
}

void GameEnd()
{
	for (int i = 0; i < 2; ++i)
		DeleteDC(g_BmpDC[i]);

	//刪除後備設備,釋放主設備
	DeleteDC(g_BackDC);
	ReleaseDC(g_hWnd, g_MainDC);
}

效果圖
在這裏插入圖片描述

a圖片
a圖片b圖片
b圖片

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章