DirectX11 创建窗口与框架

前言

在本节我们将创建我们自己的框架并创建一个Win32窗口,并且在后面我们会一步一步地完善框架。

本节需要创建的文件如下

  • framework.cpp
  • framework.h
  • graphics.cpp
  • graphics.h
  • input.cpp
  • input.h
  • main.cpp

框架将被封装到framework.h,然后我们会在input.hgraphics.h分别创建InputClassGraphicsClass两个类,用来处理用户输入和渲染。将渲染与处理用户输入部分分离出来可以使我们的思路更清晰。

结构如下
在这里插入图片描述

Framework

我们开始来编写框架

framework.h

我们这里引入Windows.h头文件,在前面我们可以定义 WIN32_LEAN_AND_MEAN 这可以排除一些我们不常用的一些API,提高构建的速度。

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

然后包含头文件input.hgraphics.h

#include "input.h"
#include "graphics.h"

Framework类:

class Framework {
public:
	Framework(HINSTANCE hInstance);
	Framework(const Framework&);
	~Framework();

	bool Initialize();
	void Shutdown();
	void Run();

	LRESULT CALLBACK MessageHandler(HWND, UINT, WPARAM, LPARAM);

private:
	bool Frame();
	void InitializeWindows();
	void ShutdownWindows();

private:
	struct {
		LPWSTR m_title = L"DX11 Window";
		LPCWSTR m_APP_NAME = L"DX11 Engine";
		HINSTANCE m_hInstance;
		HWND m_hWnd;
		UINT width = 800;
		UINT height = 600;
		bool fullscreen = false;
	}window;
	
	InputClass* m_Input;
	GraphicsClass* m_Graphics;
};

其中,有初始化、关闭和运行函数。还有一个MessageHandler函数来处理窗口消息。我们会在回调函数WndProc里调用它最后将其注册到上下文里。

此外还有一些私有函数。最后,我们有一些私有变量windowm_Inputm_Graphics,第一个储存了窗口的一些信息,后面两个为指向将处理图形和输入的两个对象的指针。
然后,我们要声明一下WndProc函数

static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

最后声明全局指针对象appHandle

static Framework* appHandle = nullptr;

framework.cpp

先包含framework.h,然后我们在framework.cpp里实现Framework

#include "framework.h"

下面是构造,拷贝构造和析构。为了内存安全我们还是显式地写了拷贝构造和析构。构造函数初始化 m_Inputm_Graphics 为空指针。然后将传入实例句柄绑定到成员m_hInstance上,方便内部引用。

Framework::Framework(HINSTANCE hInstance) {
	m_Input = nullptr;
	m_Graphics = nullptr;
	window.m_hInstance = hInstance;
}
Framework::Framework(const Framework&){}
Framework::~Framework() {}

初始化框架部分:

bool Framework::Initialize()
{
	bool result;

	InitializeWindows();

	m_Input = new InputClass;
	m_Input->Initialize();

	m_Graphics = new GraphicsClass;
	if (!m_Graphics->Initialize(window.width, window.height, window.fullscreen, window.m_hWnd))
	{
		return false;
	}
	return true;
}

退出框架部分:

void Framework::Shutdown()
{
	if (m_Graphics)
	{
		m_Graphics->Shutdown();
		delete m_Graphics;
		m_Graphics = nullptr;
	}

	if (m_Input)
	{
		delete m_Input;
		m_Input = nullptr;
	}

	ShutdownWindows();
}

运行部分:

void Framework::Run()
{
	MSG msg = {};

	while (GetMessage(&msg, nullptr, 0, 0) > 0)
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
		
		if (!Frame()){
			PostQuitMessage(0);
		}
	}
}

这里就是窗口主循环部分,如果获取到窗口消息为WM_QUIT就退出主循环。否则执行Frame函数。

Frame函数,也就是执行每一帧的函数:

bool Framework::Frame()
{
	bool result;

	if (m_Input->IsKeyDown(VK_ESCAPE))
	{
		return false;
	}

	result = m_Graphics->Frame();
	if (!result)
	{
		return false;
	}

	return true;
}

检测如果按下ESC键就返回false,在上一个我们看到了,如果为false就退出循环了。这里就是实现了按ESC退出。然后就是调用渲染帧函数m_Graphics->Frame。具体的这些,我们会在后面实现。

下面的是我们的消息处理函数,它将被WndProc调用然后注册到上下文中。

LRESULT CALLBACK Framework::MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_KEYDOWN:
	{
		m_Input->KeyDown((unsigned int)wParam);
		return 0;
	}
	case WM_KEYUP:
	{
		m_Input->KeyUp((unsigned int)wParam);
		return 0;
	}
	default:
	{
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}
	}
}

然后我们来初始化窗口,并获取指向此对象的外部指针appHandle

void Framework::InitializeWindows(int& screenWidth, int& screenHeight){
	appHandle = this;
}

注册窗口类

	WNDCLASSEX wcex;
	
	wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = window.m_hInstance;
	wcex.hIcon = LoadIcon(nullptr, IDI_WINLOGO);
	wcex.hIconSm = wcex.hIcon;
	wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wcex.lpszMenuName = nullptr;
	wcex.lpszClassName = window.m_APP_NAME;
	wcex.cbSize = sizeof(WNDCLASSEX);
	
	RegisterClassEx(&wcex);

计算窗口位置,使其在最中间出现

	int posX = (GetSystemMetrics(SM_CXSCREEN) - window.width) / 2;
	int posY = (GetSystemMetrics(SM_CYSCREEN) - window.height) / 2;

创建窗口

	window.m_hWnd = CreateWindowEx(WS_EX_APPWINDOW, 
		window.m_APP_NAME, window.m_title,
		WS_OVERLAPPEDWINDOW,
		posX, posY, window.width, window.height, 
		nullptr, nullptr, window.m_hInstance, nullptr);

然后显示,并将其置于顶层,获得焦点

	ShowWindow(window.m_hWnd, SW_SHOW);
	SetForegroundWindow(window.m_hWnd);
	SetFocus(window.m_hWnd);

退出窗口,释放资源

void Framework::ShutdownWindows()
{
	DestroyWindow(window.m_hWnd);
	window.m_hWnd = nullptr;

	UnregisterClass(window.m_APP_NAME, window.m_hInstance);
	window.m_hInstance = nullptr;

	appHandle = nullptr;
}

下面就是回调函数WndProc

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_DESTROY:
	{
		PostQuitMessage(0);
		return 0;
	}

	case WM_CLOSE:
	{
		PostQuitMessage(0);
		return 0;
	}

	default:
	{
		return appHandle->MessageHandler(hWnd, uMsg, wParam, lParam);
	}
	}
}

至此,framework编写结束

InputClass

我们开始编写处理用户输入的部分

input.h

input.h的内容相对简单

class InputClass
{
public:
	InputClass();
	InputClass(const InputClass&);
	~InputClass();

	void Initialize();

	void KeyDown(unsigned int);
	void KeyUp(unsigned int);

	bool IsKeyDown(unsigned int);

private:
	bool m_keys[256];
};

input.cpp

#include "input.h"

InputClass::InputClass(){}
InputClass::InputClass(const InputClass& other){}
InputClass::~InputClass(){}

void InputClass::Initialize()
{
	int i;
	for(i=0; i<256; i++){
		m_keys[i] = false;
	}
}


void InputClass::KeyDown(unsigned int input){
	m_keys[input] = true;
}


void InputClass::KeyUp(unsigned int input){
	m_keys[input] = false;
}

bool InputClass::IsKeyDown(unsigned int key){
	return m_keys[key];
}

GraphicsClass

下面编写渲染类

graphics.h

包含头文件

#include <windows.h>

然后是声明GraphicsClass

class GraphicsClass
{
public:
	GraphicsClass();
	GraphicsClass(const GraphicsClass&);
	~GraphicsClass();

	bool Initialize(int, int, bool, HWND);
	void Shutdown();
	bool Frame();

private:
	bool Render();
public:
	struct {
		bool vsync_enable = true;
		float screen_depth = 1000.0f;
		float screen_near = 0.1f;
	}cfg;
};

graphics.cpp

我们这节只需搭建一下渲染的框架

#include "graphics.h"


GraphicsClass::GraphicsClass(){
}


GraphicsClass::GraphicsClass(const GraphicsClass& other){
}


GraphicsClass::~GraphicsClass(){
}


bool GraphicsClass::Initialize(int width, int height, bool fullscreen, HWND hwnd){

	return true;
}


void GraphicsClass::Shutdown(){

	return;
}


bool GraphicsClass::Frame(){

	return true;
}


bool GraphicsClass::Render(){

	return true;
}

主函数

最后只需要我们调用框架即可

#include "framework.h"

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR cmdline, int cmdShow) {
	Framework* frame = new Framework(hInstance);

	if (frame->Initialize()) {
		frame->Run();
	}

	frame->Shutdown();
	delete frame;

	return 0;
}

如果你得到了下面的窗口,恭喜你,这一步成功了。
在这里插入图片描述

完整代码

请转到项目仓库下载源码及资源
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
使用DirectX或OpenGL进行显卡编程需要以下步骤: 1. 安装开发境:首先,你需要安装相应的开发环境。对于DirectX,你需要安装Windows SDK,并选择安装DirectX开发工具。对于OpenGL,你需要安装OpenGL的开发库,如GLEW(OpenGL Extension Wrangler Library)或者GLFW(OpenGL FrameWork)。 2. 创建窗口:在进行显卡编程之前,你需要创建一个窗口来显示图像。使用Windows API或者其他框架(如SDL、GLUT等)创建一个窗口,并设置好图像显示的相关参数。 3. 初始化和配置显卡:在窗口创建完成后,你需要初始化和配置显卡。对于DirectX,你可以使用Direct3D API来进行显卡初始化和配置。对于OpenGL,你需要通过调用OpenGL的函数来初始化和配置OpenGL上下文。 4. 加载和处理图像数据:在显卡编程中,你需要加载并处理图像数据。你可以使用图像处理库(如OpenCV)加载图像数据,或者自行编写图像加载函数。一般来说,图像数据会被存储在缓冲区中,然后传递给显卡。 5. 创建着色器和顶点缓冲区:在显卡编程中,你需要创建着色器和顶点缓冲区。对于DirectX,你可以使用HLSL(High-Level Shading Language)来编写着色器,然后通过Direct3D API创建和配置着色器。对于OpenGL,你可以使用GLSL(OpenGL Shading Language)编写着色器,并使用OpenGL的函数来创建和配置着色器。 6. 渲染图像:一切准备就绪后,你可以开始渲染图像了。通过调用显卡驱动程序的API,将图像数据传递给显卡进行渲染。对于DirectX,你可以使用Direct3D API的绘制函数来渲染图像。对于OpenGL,你可以使用OpenGL的绘制函数来渲染图像。 7. 渲染循环和交互:在图像渲染完成后,你可以进入一个渲染循环,以实现动态交互。在循环中,你可以根据用户输入或其他条件更新图像数据,并再次调用渲染函数进行图像的更新和显示。 需要注意的是,显卡编程是一项复杂的任务,需要对图形编程和显卡架构有一定的了解。建议你参考官方文档和教程,以及相关的书籍和在线资源,深入学习和理解相关的知识。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

orbitgw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值