C++ OpenGL 3D Game Tutorial 2: Making OpenGL 3D Engine学习笔记

视频地址icon-default.png?t=N7T8https://www.youtube.com/watch?v=PH5kH8h82L8&list=PLv8DnRaQOs5-MR-zbP1QUdq5FL0FWqVzg&index=3

一、main类

        接上一篇内容,main.cpp的内容增加了一些代码,显得严谨一些:

#include<OGL3D/Game/OGame.h>
#include<iostream>int main()
{try {OGame game;game.Run();}catch (const std::exception& e){std::cout << e.what() << std::endl;return 1;}return 0;
}

二、OGame类 

       然后是Game类的添加了很多内容:

        Game.h文件:

#pragma once
#include<memory>class OGraphicsEngine;
class OWindow;
class OGame
{
public:OGame();~OGame();virtual void onCreate();virtual void onUpdate();virtual void onQuit();void Run();
protected:bool m_isRunning = true;std::unique_ptr<OGraphicsEngine> m_graphicsEngine;std::unique_ptr<OWindow> m_display;
};

        Game.cpp文件:

#include<OGL3D/Graphics/OGraphicsEngine.h>
#include<OGL3D/Window/OWindow.h>
#include<OGL3D/Game/OGame.h>
#include<Windows.h>OGame::OGame()
{m_graphicsEngine = std::make_unique<OGraphicsEngine>();m_display = std::make_unique<OWindow>();m_display->makeCurrentContext();
}OGame::~OGame()
{
}void OGame::onCreate()
{m_graphicsEngine->clear(OVec4(1,0,0,1));m_display->present(false);
}void OGame::onUpdate()
{
}void OGame::onQuit()
{
}void OGame::Run()
{onCreate();MSG msg;while (m_isRunning){msg = {};if (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)){if (msg.message == WM_QUIT){m_isRunning = false;continue;}else {TranslateMessage(&msg);DispatchMessage(&msg);}}onUpdate();}onQuit();
}

       和上一篇相比,在构造函数里面创建了OGraphicEngine对象,同时执行了OWindow对象m_display的方法makeCurrentContext,这个方法也是OWindow类在本次视频里面后加的。需要注意的是,这两行代码的顺序不能错:

m_graphicsEngine = std::make_unique<OGraphicsEngine>();
m_display = std::make_unique<OWindow>();

         因为OWindow构造期间需要获取的HDC里面的内容,而这个HDC内容要在OGraphicsEngine构造期间设置,所以必定要先构造OGraphicsEngine对象,顺序颠倒了肯定不行。

        同时OGame类里面还添加onCreate、onUpdate、onQuit方法,注意一下这三个方法的调用时机就好了。

三、OWindow类

Owindow.h文件

#pragma once#include<Windows.h>
class OWindow
{
public:OWindow();~OWindow();void makeCurrentContext();void present(bool vsync);
private:HWND m_handle = nullptr;HGLRC m_context = nullptr;
};

OWindow.cpp文件

#include<OGL3D/Window/OWindow.h>
#include<glad/glad.h>
#include<glad/glad_wgl.h>
#include <Windows.h>
#include<assert.h>LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{switch (msg){case WM_DESTROY:{OWindow* window = (OWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA);break;}case WM_CLOSE:{PostQuitMessage(0);break;}default:return DefWindowProc(hwnd, msg, wParam, lParam);}return NULL;
}OWindow::OWindow()
{WNDCLASSEX wc = {};wc.cbSize = sizeof(WNDCLASSEX);wc.lpszClassName = L"OGL3DWindow";wc.lpfnWndProc = WndProc;auto classId = RegisterClassEx(&wc);assert(classId);RECT rc = { 0,0,1024,768 };AdjustWindowRect(&rc, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, false);m_handle = CreateWindowEx(NULL,MAKEINTATOM(classId),L"Parcode | OpenGL 3D Game",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,rc.right - rc.left, rc.bottom - rc.top,NULL, NULL, NULL, NULL);assert(m_handle);SetWindowLongPtr(m_handle, GWLP_USERDATA, (LONG_PTR)this);ShowWindow(m_handle, SW_SHOW);UpdateWindow(m_handle);HDC hDC = GetDC(m_handle);int pixelFormatAttributes[] = {WGL_DRAW_TO_WINDOW_ARB,GL_TRUE,WGL_SUPPORT_OPENGL_ARB,GL_TRUE,WGL_DOUBLE_BUFFER_ARB,GL_TRUE,WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,WGL_PIXEL_TYPE_ARB,WGL_TYPE_RGBA_ARB,WGL_COLOR_BITS_ARB,24,WGL_DEPTH_BITS_ARB,24,WGL_STENCIL_BITS_ARB,8,0};int iPixelFormat = 0;UINT numFormats = 0;wglChoosePixelFormatARB(hDC, pixelFormatAttributes, nullptr, 1, &iPixelFormat, &numFormats);assert(numFormats);PIXELFORMATDESCRIPTOR pixelFormatDesc = {};DescribePixelFormat(hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pixelFormatDesc);SetPixelFormat(hDC, iPixelFormat, &pixelFormatDesc);int openAttributes[] = {WGL_CONTEXT_MAJOR_VERSION_ARB,4,WGL_CONTEXT_MINOR_VERSION_ARB,6,WGL_CONTEXT_PROFILE_MASK_ARB,WGL_CONTEXT_CORE_PROFILE_BIT_ARB,0};m_context = wglCreateContextAttribsARB(hDC, 0, openAttributes);assert(m_context);
}OWindow::~OWindow()
{wglDeleteContext(m_context);DestroyWindow(m_handle);
}void OWindow::makeCurrentContext()
{wglMakeCurrent(GetDC(m_handle), m_context);
}void OWindow::present(bool vsync)
{wglSwapIntervalEXT(vsync);wglSwapLayerBuffers(GetDC(m_handle), WGL_SWAP_MAIN_PLANE);
}

创建窗口

        这里需要解释一些创建窗口的问题,其实在上一篇文件里就应该解释,但是当时理解的也不到位,这里算是补充一下。 
        WNDCLASSEX创建一个名称为wc的结构体实例,这个实例相当于我们事先写一份要创建窗口的类型申请表,RegisterClass这个函数就是把wc这个申请表提交(注册)给Window系统,然后当需要创建窗口时,Window系统就会按这个申请表来创建。
        RegisterClass的形参是指向WNDCLASSEX结构体的指针,这个指针会加入到system atom table即SAT中,这样系统就可以通过查找这张表来找到用户自定义的窗口类,window预定义的窗口类指针也在SAT中。

        SAT实际上实现了一种用于查询的映射,ATOM(翻译过来叫“原子”)实际类型是short,即16整数。ATOM表(原子表)是一个系统定义的用于存放字符串和相应的标识符的表。程序把一个字符串放入ATOM表,获得一个相应的16位整数,这个整数就叫原子,可以用来访问该字符串。一个被放进原子表的字符串叫做原子名称。

        只有系统才可直接访问这张表,但在调用某些api函数时,如Registerclass,可以告知系统来存取这张表。当然,还有本地原子表和全局原子表,这些表应用程序是可以直接访问的。

        MAKEINTATOM 宏 (位于winbase.h)将指定的原子转换为对应的字符串地址,以便可以将其传递给接受原子或字符串的函数。调用CreateWindowEx就用到了这个宏。

获取绘制上下文 

        后面补充的代码是从

HDC hDC = GetDC(m_handle); 

这行代码开始,一直到

assert(m_context); 

这行代码结束,其核心的目的是获取绘制上下文,就是 m_context 。而获取上下文的代码时使用的代码是:

m_context = wglCreateContextAttribsARB(hDC, 0, openAttributes);

        总体来说对我是个晕头转向的过程,不过大概就是先要获取形参,第一个是hDC,也是唯一需要说明一下的,这个内容有点儿多,查了老半天也是很糊涂的,先能明白多少算多少。

        HDC——Handle of the Device Context,中文意思是“设备上下文句柄”,这个东西会在后面多次用到。HDC是一种包含有关某个设备(如显示器或打印机)的绘制属性信息的 Windows 数据结构。所有绘制调用都通过设备上下文对象进行,这些对象封装了用于绘制线条、形状和文本的 Windows API。

        虽然说GetDC函数很容易就获取了这个hDC,但是对其像素格式进行设置。就是下面的代码:

SetPixelFormat(hDC, iPixelFormat, &pixelFormatDesc);

        这个过程设计好几个函数,基本没看明白,估计照着写就行。

        就说一个wglChoosePixelFormatARB函数吧,它能够找到合适的像素格式。

        Windows下要通过程序设置多重采样,必须使用wglChoosePixelFormatARB这个函数。正确使用这个函数的关键,就是需要创建一个临时窗体,通过这个窗体,我们可以获取必须的基础像素格式,然后再使用wglChoosePixelFormatARB这个函数,得到可用的多重采样像素格式,最后,对渲染窗口设置这个像素格式即可。

四、OGraphicsEngine类

        新增加了OGraphicsEngine类,先贴出代码:

        OGraphicsEngine.h文件:

#pragma once
#include<OGL3D/Math/OVec4.h>class OGraphicsEngine
{
public:OGraphicsEngine();~OGraphicsEngine();
public:void clear(const OVec4& color);
};

        OGraphicsEngine.cpp文件:

#include<OGL3D/Graphics/OGraphicsEngine.h>
#include<glad/glad.h>
#include<glad/glad_wgl.h>
#include<assert.h>
#include<stdexcept>OGraphicsEngine::OGraphicsEngine()
{WNDCLASSEX wc = {};wc.cbSize = sizeof(WNDCLASSEX);wc.lpszClassName = L"OGL3DDummyWindow";wc.lpfnWndProc = DefWindowProc;wc.style = CS_OWNDC;auto classId = RegisterClassEx(&wc);assert(classId);auto dummyWindow = CreateWindowEx(NULL, MAKEINTATOM(classId), L"", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL);assert(dummyWindow);HDC dummyDC = GetDC(dummyWindow);PIXELFORMATDESCRIPTOR pixelFormatDesc = {};pixelFormatDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR);pixelFormatDesc.nVersion = 1;pixelFormatDesc.iPixelType = PFD_TYPE_RGBA;pixelFormatDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;pixelFormatDesc.cColorBits = 32;pixelFormatDesc.cAlphaBits = 8;pixelFormatDesc.cDepthBits = 24;pixelFormatDesc.cStencilBits = 8;pixelFormatDesc.iLayerType = PFD_MAIN_PLANE;auto iPixelFormat = ChoosePixelFormat(dummyDC, &pixelFormatDesc);SetPixelFormat(dummyDC, iPixelFormat, &pixelFormatDesc);auto dummyContext = wglCreateContext(dummyDC);assert(dummyContext);wglMakeCurrent(dummyDC, dummyContext);if (!gladLoadWGL(dummyDC))throw std::runtime_error("OGraphic Engine Error:gladLoadWGL failed.");if (!gladLoadGL())throw std::runtime_error("OGraphic Engine Error:gladLoadGL failed.");wglMakeCurrent(dummyDC, 0);wglDeleteContext(dummyContext);ReleaseDC(dummyWindow, dummyDC);DestroyWindow(dummyWindow);
}OGraphicsEngine::~OGraphicsEngine()
{
}void OGraphicsEngine::clear(const OVec4& color)
{glClearColor(color.x,color.y,color.z,color.w);glClear(GL_COLOR_BUFFER_BIT);
}

        ​choosePixelFormat从HDC中选择最匹配的内容,返回一个索引(iPixelFormat,开头的i应该就是index)。

        SetPixelformat函数将指定设备上下文(HDC)的像素格式设置为索引(iPixelFormat)指定的格式。

        这一通操作意思就是你开始定义的PIXELFORMATDESCRIPTOR只是你自己一厢情愿的想法,设备(显示器或者打印机)有可能支持(这当然最好),也有可能不支持,一旦不支持,就给你找个最接近你要求的参数凑合用用。

        wglCreateContext(为什么是wgl开头呢?应该是来源于Wingdi.h文件的GL函数,wingdi应该就是Window Graphic Deviec Interface)函数创建一个呈现上下文,该上下文适用于在 hdc 引用的设备上绘图。 

        其返回值是HGLRC类型(the Handle of GL Rendering Context,GL渲染上下文句柄)

        wglMakeCurrent就是指示后续在hdc上的绘制工作都是基于dummyContext的。

        绕了一大圈都是为了这两句:

	if (!gladLoadWGL(dummyDC))throw std::runtime_error("OGraphic Engine Error:gladLoadWGL failed.");if (!gladLoadGL())throw std::runtime_error("OGraphic Engine Error:gladLoadGL failed.");

        先说一下,通过throw抛出异常会终止线程。

        这两句也都是对hDC状态进行设置。

五、补充

        补充两个文件,一个是OVec4.h文件,另一个是OPerequisites.h文件。

        OVec4.h文件:

#pragma once
#include<OGL3D/OPerequisites.h>class OVec4
{
public:OVec4() {}OVec4(f32 x, f32 y, f32 z, f32 w) :x(x), y(y), z(z), w(w) {}~OVec4() {}public:f32 x = 0, y = 0, z = 0, w = 0;
};

        OPerequisites.h文件:

#pragma oncetypedef float f32;

六、后记

        说实话,Window编程真的很绕,很晦涩(其实本来想说很**,但我要保持优雅的底线),也许这就是垄断的结果吧。其实以前开发苹果App的时候,感觉苹果对开发者就很不友好(对玩家用户倒是友好得很!),禁不住回想在Unity环境下用C#编程,似乎真的很美好(Long Live Unity!)。不过话说回来,开发游戏引擎的通常比使用引擎做游戏的工资更高,算是对被这种代码折磨的补偿吧 :D       

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/613168.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

重新认识Elasticsearch-一体化矢量搜索引擎

前言 2023 哪个网络词最热&#xff1f;我投“生成式人工智能”一票。过去一年大家都在拥抱大模型&#xff0c;所有的行业都在做自己的大模型。就像冬日里不来件美拉德色系的服饰就会跟不上时代一样。这不前段时间接入JES&#xff0c;用上好久为碰的RestHighLevelClient包。心血…

模拟超市商品结算系统

要求:全程一个角色(管理员即用户) (1)需要管理员注册与登录 (2)管理员登录之后&#xff0c;可以进行上架新的商品(商品名称和单价) (3)管理员登录之后&#xff0c;也可以下架商品 (4)在节假日有优惠活动,可以对其中的一些商品修改相应的单价(价格提高和价格降低都可以) (5)用户…

如何使用CentOS系统中的Apache服务器提供静态HTTP服务

在CentOS系统中&#xff0c;Apache服务器是一个常用的Web服务器软件&#xff0c;它可以高效地提供静态HTTP服务。以下是在CentOS中使用Apache提供静态HTTP服务的步骤&#xff1a; 1. 安装Apache服务器 首先&#xff0c;您需要确保已安装Apache服务器。可以使用以下命令安装Ap…

关于burpsuite设置HTTP或者SOCKS代理

使用burpsuite给自己的浏览器做代理&#xff0c;抓包重发这些想必大家都清除 流量请求过程&#xff1a; 本机浏览器 -> burpsuite -> 目标服务器 实质还是本机发出的流量 如果我们想让流量由其他代理服务器发出 实现&#xff1a; 本机浏览器 -> burpsuite -> 某…

Blazor中使用impress.js

impress.js是什么&#xff1f; 你想在浏览器中做PPT吗&#xff1f;比如在做某些类似于PPT自动翻页&#xff0c;局部放大之类&#xff0c;炫酷无比。 在Blazor中&#xff0c;几经尝试&#xff0c;用以下方法可以实现。写文不易&#xff0c;请点赞、收藏、关注&#xff0c;并在转…

Python基础知识:整理9 文件的相关操作

1 文件的打开 # open() 函数打开文件 # open(name, mode, encoding) """name: 文件名&#xff08;可以包含文件所在的具体路径&#xff09;mode: 文件打开模式encoding: 可选参数&#xff0c;表示读取文件的编码格式 """ 2 文件的读取 文…

web缓存代理

缓存代理的概述 wed代理的工作机制 缓存网页对象&#xff0c;减少重复请求 web缓存代理作用 1.存储一些之前被访问的&#xff0c;且可能将要被再次访问的静态网络资源对象&#xff0c;使用户可以直接从缓存代理服务器获取资源&#xff0c;从而减少上游原始服务器的负载压力…

分享7款前端CSS动画特效源码(附在线演示)

精选7款前端CSS动画特效源码 下面我会给出特效样式图或演示效果图 但你也可以点击在线预览查看源码的最终展示效果及下载源码资源 CSS飞行的荷包蛋 CSS荷包蛋动画 荷包蛋会向右前方加速飞行 期间还能看到周围的气流匆匆飞过 以下图片只是简单的模型没有具体的动画效果最终动画…

IPv6路由协议---IPv6动态路由(OSPFv3-5)

OSPFv3各链路状态通告类型 4.Inter-Area-Router-LSA区域间路由器(4类LSA) 边界路由器(ABR)产生的第4类LSA,在Area 范围内泛洪,描述了到本AS内其他区域的ASBR路由器信息; 每各Inter-Area-Router-LSA包含一个ASBR路由器信息,LSA中的能力选项(Options)与所描述的ASBR …

满足ITOM需求的网络监控工具

IT 运营管理&#xff08;ITOM&#xff09;可以定义为监督 IT 基础架构的各种物理和虚拟组件的过程;确保其性能、运行状况和可用性;并使它们能够与基础架构的其他组件无缝协作。IT 运营管理&#xff08;ITOM&#xff09;在大型 IT 管理模型中也发挥着积极作用&#xff0c;包括 I…

强化学习求解TSP(四):Qlearning求解旅行商问题TSP(提供Python代码)

一、Qlearning简介 Q-learning是一种强化学习算法&#xff0c;用于解决基于奖励的决策问题。它是一种无模型的学习方法&#xff0c;通过与环境的交互来学习最优策略。Q-learning的核心思想是通过学习一个Q值函数来指导决策&#xff0c;该函数表示在给定状态下采取某个动作所获…

vue配置qiankun及打包上线

项目结构 基座&#xff1a;vue3 子应用A&#xff1a;vue3 子应用B&#xff1a; react 子应用C&#xff1a;vue3vite 项目目录&#xff1a; 配置基座 首先下载qiankun yarn add qiankun # 或者 npm i qiankun -S 所有子应用也要安装&#xff0c;vue-vite项目安装 cnpm ins…

蓝桥杯练习题(三)

&#x1f4d1;前言 本文主要是【算法】——蓝桥杯练习题&#xff08;三&#xff09;的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 …

[C#]winform部署PaddleOCRV3推理模型

【官方框架地址】 https://github.com/PaddlePaddle/PaddleOCR.git 【算法介绍】 PaddleOCR是由百度公司推出的一款开源光学字符识别&#xff08;OCR&#xff09;工具&#xff0c;它基于深度学习框架PaddlePaddle开发。这款工具提供了一整套端到端的文字检测和识别解决方案&a…

PyQt5零基础入门(二)——QLabel控件

前言 QLabel控件可以视为是一个标签项&#xff0c;具有显示文本、图像的作用。在本篇文章中将介绍QLabel控件的常见用法。 例子 显示文本 import sys from PyQt5.QtWidgets import *if __name__ "__main__":app QApplication([])label QLabel(Hello world!)la…

Zookeeper 和 naocs的区别

Nacos 和 ZooKeeper 都是服务发现和配置管理的工具&#xff0c;它们的主要区别如下&#xff1a;功能特性&#xff1a;Nacos 比 ZooKeeper 更加强大&#xff0c;Nacos 支持服务发现、动态配置、流量管理、服务治理、分布式事务等功能&#xff0c;而 ZooKeeper 主要用于分布式协调…

【天龙怀旧服】攻略day4

关键字&#xff1a; 快捷鉴定手工、组队跟随兔子、九州店铺 1】快捷鉴定手工 可以把鉴定符拖到快捷技能栏&#xff0c;例如f1然后鼠标选到未鉴定手工&#xff0c;快捷键即可鉴定 2】组队跟打手&#xff0c;兔子队 队长给小号&#xff0c;组队跟随&#xff1b; 打手退跟随打…

UM2004 一款低功耗、高性能、即插即用型 OOK 射频接收器芯片

UM2004 是一款低功耗、高性能、即插即用型 OOK 射频接收器&#xff0c;该芯片具有 2.5V ~ 5.5V 较宽的输入电压范围&#xff0c;灵敏度高达到-109dBm&#xff0c;工作频段为 300MHz ~ 480MHz&#xff0c;支持 1Kbps~ 5Kbps 的数据率传输。采用 SOP8 封装类型&#xff0c;应用时…

容器扫描Trivy及Trivy-db数据库研究

trivy介绍 Trivy是一个镜像容器扫描工具&#xff0c;用于扫描漏洞和配置错误。 它是一款相当全面且多功能的安全扫描器&#xff0c;支持多种扫描目标&#xff0c;能够弥补市面上常见Web 漏洞扫描工具的不足。 Trivy 可以轻松地通过安装并将二进制文件添加到项目中&#xff0c;…

Edge浏览器设置自动刷新详细步骤分享

Edge浏览器自动刷新设置方法详细教学分享。在电脑上访问一些动态网页的时候&#xff0c;用户发现网页的内容滚动之后&#xff0c;内容无法进行刷新。这个情况是我们的浏览器没有开启自动刷新功能。那么这个功能设置怎么开启呢&#xff1f;一起来看看以下的操作方法教学吧。 操…