視頻地址https://www.youtube.com/watch?v=PH5kH8h82L8&list=PLv8DnRaQOs5-MR-zbP1QUdq5FL0FWqVzg&index=3
一、main類
????????接上一篇內(nèi)容,main.cpp的內(nèi)容增加了一些代碼,顯得嚴(yán)謹(jǐn)一些:
#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類的添加了很多內(nèi)容:
????????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();
}
? ? ? ?和上一篇相比,在構(gòu)造函數(shù)里面創(chuàng)建了OGraphicEngine對(duì)象,同時(shí)執(zhí)行了OWindow對(duì)象m_display的方法makeCurrentContext,這個(gè)方法也是OWindow類在本次視頻里面后加的。需要注意的是,這兩行代碼的順序不能錯(cuò):
m_graphicsEngine = std::make_unique<OGraphicsEngine>();
m_display = std::make_unique<OWindow>();
? ? ? ? ?因?yàn)镺Window構(gòu)造期間需要獲取的HDC里面的內(nèi)容,而這個(gè)HDC內(nèi)容要在OGraphicsEngine構(gòu)造期間設(shè)置,所以必定要先構(gòu)造OGraphicsEngine對(duì)象,順序顛倒了肯定不行。
????????同時(shí)OGame類里面還添加onCreate、onUpdate、onQuit方法,注意一下這三個(gè)方法的調(diào)用時(shí)機(jī)就好了。
三、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);
}
創(chuàng)建窗口
????????這里需要解釋一些創(chuàng)建窗口的問題,其實(shí)在上一篇文件里就應(yīng)該解釋,但是當(dāng)時(shí)理解的也不到位,這里算是補(bǔ)充一下。?
????????WNDCLASSEX創(chuàng)建一個(gè)名稱為wc的結(jié)構(gòu)體實(shí)例,這個(gè)實(shí)例相當(dāng)于我們事先寫一份要?jiǎng)?chuàng)建窗口的類型申請(qǐng)表,RegisterClass這個(gè)函數(shù)就是把wc這個(gè)申請(qǐng)表提交(注冊(cè))給Window系統(tǒng),然后當(dāng)需要?jiǎng)?chuàng)建窗口時(shí),Window系統(tǒng)就會(huì)按這個(gè)申請(qǐng)表來創(chuàng)建。
????????RegisterClass的形參是指向WNDCLASSEX結(jié)構(gòu)體的指針,這個(gè)指針會(huì)加入到system atom table即SAT中,這樣系統(tǒng)就可以通過查找這張表來找到用戶自定義的窗口類,window預(yù)定義的窗口類指針也在SAT中。
????????SAT實(shí)際上實(shí)現(xiàn)了一種用于查詢的映射,ATOM(翻譯過來叫“原子”)實(shí)際類型是short,即16整數(shù)。ATOM表(原子表)是一個(gè)系統(tǒng)定義的用于存放字符串和相應(yīng)的標(biāo)識(shí)符的表。程序把一個(gè)字符串放入ATOM表,獲得一個(gè)相應(yīng)的16位整數(shù),這個(gè)整數(shù)就叫原子,可以用來訪問該字符串。一個(gè)被放進(jìn)原子表的字符串叫做原子名稱。
????????只有系統(tǒng)才可直接訪問這張表,但在調(diào)用某些api函數(shù)時(shí),如Registerclass,可以告知系統(tǒng)來存取這張表。當(dāng)然,還有本地原子表和全局原子表,這些表應(yīng)用程序是可以直接訪問的。
????????MAKEINTATOM 宏 (位于winbase.h)將指定的原子轉(zhuǎn)換為對(duì)應(yīng)的字符串地址,以便可以將其傳遞給接受原子或字符串的函數(shù)。調(diào)用CreateWindowEx就用到了這個(gè)宏。
獲取繪制上下文?
????????后面補(bǔ)充的代碼是從
HDC hDC = GetDC(m_handle);
這行代碼開始,一直到
assert(m_context);
這行代碼結(jié)束,其核心的目的是獲取繪制上下文,就是 m_context 。而獲取上下文的代碼時(shí)使用的代碼是:
m_context = wglCreateContextAttribsARB(hDC, 0, openAttributes);
? ? ? ? 總體來說對(duì)我是個(gè)暈頭轉(zhuǎn)向的過程,不過大概就是先要獲取形參,第一個(gè)是hDC,也是唯一需要說明一下的,這個(gè)內(nèi)容有點(diǎn)兒多,查了老半天也是很糊涂的,先能明白多少算多少。
? ? ? ? HDC——Handle of the Device Context,中文意思是“設(shè)備上下文句柄”,這個(gè)東西會(huì)在后面多次用到。HDC是一種包含有關(guān)某個(gè)設(shè)備(如顯示器或打印機(jī))的繪制屬性信息的 Windows 數(shù)據(jù)結(jié)構(gòu)。所有繪制調(diào)用都通過設(shè)備上下文對(duì)象進(jìn)行,這些對(duì)象封裝了用于繪制線條、形狀和文本的 Windows API。
? ? ? ? 雖然說GetDC函數(shù)很容易就獲取了這個(gè)hDC,但是對(duì)其像素格式進(jìn)行設(shè)置。就是下面的代碼:
SetPixelFormat(hDC, iPixelFormat, &pixelFormatDesc);
????????這個(gè)過程設(shè)計(jì)好幾個(gè)函數(shù),基本沒看明白,估計(jì)照著寫就行。
? ? ? ? 就說一個(gè)wglChoosePixelFormatARB函數(shù)吧,它能夠找到合適的像素格式。
????????Windows下要通過程序設(shè)置多重采樣,必須使用wglChoosePixelFormatARB這個(gè)函數(shù)。正確使用這個(gè)函數(shù)的關(guān)鍵,就是需要?jiǎng)?chuàng)建一個(gè)臨時(shí)窗體,通過這個(gè)窗體,我們可以獲取必須的基礎(chǔ)像素格式,然后再使用wglChoosePixelFormatARB這個(gè)函數(shù),得到可用的多重采樣像素格式,最后,對(duì)渲染窗口設(shè)置這個(gè)像素格式即可。
四、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中選擇最匹配的內(nèi)容,返回一個(gè)索引(iPixelFormat,開頭的i應(yīng)該就是index)。
????????SetPixelformat函數(shù)將指定設(shè)備上下文(HDC)的像素格式設(shè)置為索引(iPixelFormat)指定的格式。
????????這一通操作意思就是你開始定義的PIXELFORMATDESCRIPTOR只是你自己一廂情愿的想法,設(shè)備(顯示器或者打印機(jī))有可能支持(這當(dāng)然最好),也有可能不支持,一旦不支持,就給你找個(gè)最接近你要求的參數(shù)湊合用用。
????????wglCreateContext(為什么是wgl開頭呢?應(yīng)該是來源于Wingdi.h文件的GL函數(shù),wingdi應(yīng)該就是Window Graphic Deviec Interface)函數(shù)創(chuàng)建一個(gè)呈現(xiàn)上下文,該上下文適用于在?hdc?引用的設(shè)備上繪圖。?
????????其返回值是HGLRC類型(the Handle of GL Rendering Context,GL渲染上下文句柄)
????????wglMakeCurrent就是指示后續(xù)在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拋出異常會(huì)終止線程。
? ? ? ? 這兩句也都是對(duì)hDC狀態(tài)進(jìn)行設(shè)置。
五、補(bǔ)充
????????補(bǔ)充兩個(gè)文件,一個(gè)是OVec4.h文件,另一個(gè)是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文件:文章來源:http://www.zghlxwxcb.cn/news/detail-782657.html
#pragma once
typedef float f32;
六、后記
????????說實(shí)話,Window編程真的很繞,很晦澀(其實(shí)本來想說很**,但我要保持優(yōu)雅的底線),也許這就是壟斷的結(jié)果吧。其實(shí)以前開發(fā)蘋果App的時(shí)候,感覺蘋果對(duì)開發(fā)者就很不友好(對(duì)玩家用戶倒是友好得很?。蛔』叵朐赨nity環(huán)境下用C#編程,似乎真的很美好(Long Live Unity?。?。不過話說回來,開發(fā)游戲引擎的通常比使用引擎做游戲的工資更高,算是對(duì)被這種代碼折磨的補(bǔ)償吧 :D? ? ? ?文章來源地址http://www.zghlxwxcb.cn/news/detail-782657.html
到了這里,關(guān)于C++ OpenGL 3D Game Tutorial 2: Making OpenGL 3D Engine學(xué)習(xí)筆記的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!