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 330

uniform 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个例子分析完成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值