03-drawcommands工程分析详解
opengl編程指南第8版源碼怎么下載、編譯,請參考《opengl編程指南第8版源碼編譯詳細說明》
1 程序啟動
? ?C++的程序都是從main函數啟動的,但是這工程咋一看死活找不到main,給人的感覺是:不知道怎么被操作系統調用起來的。通過閱讀vapp.h,在99~103行發現了如下代碼塊:
即這里定義了一個宏MAIN_DECL,在Windows下該宏表示就是如下字符串:
int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow)即表示WinMain函數,搞過MFC的人都知道,這就是Windows下的圖形界面的main函數入口,該函數內部調用了main,是對main的包裝,再加了一些額外的東西。
如果當前的操作系統不是Windows,如:linux則該宏表示一般的main。
再往下看,第117~126行發現如下代碼:
上面的代碼說白了,就是通過字符串的拼接,湊齊WinMain(在Windows下)或main(在非Windows 下)的函數定義,根據上面提到的MAIN_DECL宏的定義,在Windows下WinMain函數的定義為:
int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { VermilionApplication * app = appclass::Create(); app->Initialize(title); app->MainLoop(); app->Finalize(); return 0; } void APIENTRY VermilionApplication::DebugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam) { OutputDebugStringA(message); OutputDebugStringA("\n"); }而在非Windows下main函數的定義為:
int main(int argc, char ** argv) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?VermilionApplication * app = appclass::Create(); ? ? ??app->Initialize(title); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?app->MainLoop(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?app->Finalize(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?return 0; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? }void APIENTRY VermilionApplication::DebugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam) { OutputDebugStringA(message); OutputDebugStringA("\n"); } ? ??這就是main函數即程序被操作系統調起來的入口。在03-drawcommands.cpp的第63行如下:
而DEFINE_APP宏在vapp.h的定義如下:
#define DEFINE_APP(appclass,title) \ VermilionApplication * VermilionApplication::s_app; \\ void VermilionApplication::MainLoop(void) \ { \do \{ \Display(); \glfwPollEvents(); \} while (!glfwWindowShouldClose(m_pWindow)); \ } \\ MAIN_DECL \ { \VermilionApplication * app = appclass::Create(); \\app->Initialize(title); \app->MainLoop(); \app->Finalize(); \\return 0; \ } \\ DEBUG_OUTPUT_CALLBACK綜合上面對main的分析,則DEFINE_APP宏在vapp.h展開后的代碼如下(以Windows平臺為例子講解,下同, 非Windows平臺原理一樣):
VermilionApplication * VermilionApplication::s_app; void VermilionApplication::MainLoop(void) { do { Display(); glfwPollEvents(); } while (!glfwWindowShouldClose(m_pWindow)); } int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { VermilionApplication * app = DrawCommandExample::Create(); app->Initialize(title); app->MainLoop(); app->Finalize(); return 0; } void APIENTRY VermilionApplication::DebugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam) { OutputDebugStringA(message); OutputDebugStringA("\n"); }同樣地:對03-drawcommands.cpp的如下代碼:
進行宏展開及結合前文對DEFINE_APP宏展開的分析,得出03-drawcommands.cpp中第45~63經過宏展開后代碼為:
class DrawCommandExample: public VermilionApplication { public: typedef class VermilionApplication base; static VermilionApplication * Create(void) { return (s_app = new DrawCommandExample); }};VermilionApplication * VermilionApplication::s_app; void VermilionApplication::MainLoop(void) { do { Display(); glfwPollEvents(); } while (!glfwWindowShouldClose(m_pWindow)); } int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { VermilionApplication * app = DrawCommandExample::Create(); app->Initialize(title); app->MainLoop(); app->Finalize(); return 0; } void APIENTRY VermilionApplication::DebugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam) { OutputDebugStringA(message); OutputDebugStringA("\n"); }到此類的結構就很清晰了,余下就是C++繼承、多態的知識了?
2 程序難點
? ? ? ?程序中用到glBufferData、glVertexAttribPointer、glEnableVertexAttribArray、glBindVertexArray,它們的用法在《OPenGL編程指南第八版》的第2、3章節有詳細的描述。它們之間的關系,請參考:《理解glVertexAttribPointer、glEnableVertexAttribArray、VAO、VBO的關系》、《glVertexAttribPointer第一個參數理解》。
? ? ?如下:
glDrawElements最后一個參數為何傳NULL,而不是存放頂點索引的數組,請參考:《glDrawElements參數在新舊版本傳最后一個參數的不同》。glDrawArraysInstanced和glDrawArrays的區別,請參考《OPenGL實例化繪制、普通繪制說明》。
? 下述代碼:
vmath::mat4 projection_matrix(vmath::frustum(-1.0f, 1.0f, -aspect, aspect, 1.0f, 500.0f));構建了如下的一個平頭截體:
其中:
Left Pannel = -1.0f
Right Pannel = 1.0f
Bottom Pannel =??-aspect
Top Pannel =?aspect
zNear Pannel = 1.0
zFar Pannel = 500
vmath::frustum對應OPenGL中的透視投影函數glFrustum, 至于glFrustum是怎么推導出來的,請參考:《OPengGL投影矩陣的推導(OpenGLD3D)》
如下代碼:
// Draw Arrays...model_matrix = vmath::translate(-3.0f, 0.0f, -5.0f);glUniformMatrix4fv(render_model_matrix_loc, 1, GL_FALSE, model_matrix);glDrawArrays(GL_TRIANGLES, 0, 3);我們來分析,是怎么怎么畫出來三角形的:
model_matrix = vmath::translate(-3.0f, 0.0f, -5.0f);跟進vmath::translate代碼處,發現其構建了一個如下以列為主序的矩陣,如下:
即model_matrix為上面的矩陣,如下代碼:
glUniformMatrix4fv(render_model_matrix_loc, 1, GL_FALSE, model_matrix);將model_matrix矩陣設置到頂點著色器中,頂點著色器如下:
#version 330uniform mat4 model_matrix; uniform mat4 projection_matrix;layout (location = 0) in vec4 position; layout (location = 1) in vec4 color;out vec4 vs_fs_color;void main(void) {const vec4 pos[3] = vec4[3](vec4(-0.3, -0.3, 0.0, 1.0), vec4(0.3, -0.3, 0.0, 1.0), vec4(-0.3, 0.3, 0.0, 1.0) );vs_fs_color = color;gl_Position = projection_matrix * (model_matrix * position); }可以發現著色器中的uniform mat4 model_matrix就是外層即前文所述的model_matrix矩陣,著色器中的position就是外層即主程序中即如下所示的頂點坐標數組:
請注意:OPenGL中的矩陣是以列主序的(詳細討論請參考《OpenGL列向量和OSG行向量的理解》),所以,上面的、以行為主序表示的頂點數組傳入到OPenGL后,存儲為以列為主序的矩陣了,即變為如下矩陣:
上述著色器中的:
model_matrix * position則將position矩陣做了一個model_matrix 的變換,這里其實model_matrix可以看成是視圖矩陣,position則可以看成是模型矩陣,通過這兩個矩陣相乘后,就是把模型矩陣變換到視圖即相機矩陣下了,其相乘的結果如下:
可以看到相乘后,四個點都做了一個平移,即原來的p1(-1,-1, 0, 1)變為了現在的p1(-4, -1, -5, 1), 原來的p2(1,-1, 0, 1)變為了現在的p2(-2, -1, -5, 1), 原來的p3(-1,1, 0, 1)變為了現在的p3(-4, 1, -5, 1), 原來的p4(-1,-1, 0, 1)變為了現在的p4(-4, -1, -5, 1),即上面等號后面的矩陣就是model_matrix * position的結果,這就是OPenGL中提到的模型視圖矩陣,然后?projection_matrix再與其相乘,就得到了模型視圖投影矩陣,也就是被平頭截體裁剪后的模型。需要說明的是:因為畫的是三角形,所以本例中的點P4在畫圖中未用到,同樣的,對剩余的三個三角形繪制代碼進行分析,得出最終的輸出結果如下:
至此:《opengl編程指南第8版》第3章中的第1個例子分析完成。
總結
以上是生活随笔為你收集整理的03-drawcommands工程分析详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 环回接口 环回地址 环回路由
- 下一篇: Hopsan -- 液压、电力等行业的仿