OpenGL 绘制图形

745 阅读4分钟

注:本文旨在记录笔者的学习过程,仅代表笔者个人的理解,如果有表述不准确的地方,欢迎各位指正!因为涉及到的概念来源自网络,所以如有侵权,也望告知!

前言

在理解了OpenGL的基本概念之后,笔者尝试通过一个小案例进一步了解OpenGL绘制图形的过程。如果还有对基本概念不够了解的同学,可以参看笔者的另一篇文章:OpenGL 常⻅的专业名词

正文

效果概览


整体流程

首先来看一下main函数实现,这部分的内容主要是初始化窗口、注册回调、设置渲染环境、开启事件循环。

                 

int main(int argc,char *argv[]) {    
    /*     `GLTools`函数`glSetWorkingDrectory`用来设置当前工作目录。实际上在Windows中是不必要的,因为工作目录默认就是与程序可执行执行程序相同的目录。但是在Mac OS X中,这个程序将当前工作文件夹改为应用程序捆绑包中的`/Resource`文件夹。`GLUT`的优先设定自动进行了这个中设置,但是这样中方法更加安全。     */
    gltSetWorkingDirectory(argv[0]);

    //初始化GLUT库,这个函数只是传说命令参数并且初始化glut库    
    glutInit(&argc, argv);

    /*     初始化双缓冲窗口,其中标志GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL分别指     
           双缓冲窗口、RGBA颜色模式、深度测试、模板缓冲区     
           --GLUT_DOUBLE`:双缓存窗口,是指绘图命令实际上是离屏缓存区执行的,然后迅速转换成窗口视图,这种方式,经常用来生成动画效果;     
           --GLUT_DEPTH`:标志将一个深度缓存区分配为显示的一部分,因此我们能够执行深度测试;     
           --GLUT_STENCIL`:确保我们也会有一个可用的模板缓存区。     */    
    glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);

    //GLUT窗口大小、窗口标题    
    glutInitWindowSize(600, 600);    
    glutCreateWindow("Triangle");    
    /*     GLUT 内部运行一个本地消息循环,拦截适当的消息。然后调用我们不同时间注册的回调函数。我们一共注册2个回调函数:     
           1)为窗口改变大小而设置的一个回调函数     
           2)包含OpenGL 渲染的回调函数     */    
    //注册重塑函数    
    glutReshapeFunc(changeSize);

    //注册显示函数
    glutDisplayFunc(RenderScene);    

    //注册特殊函数
    glutSpecialFunc(SpecialKeys);

    /*     初始化一个GLEW库,确保OpenGL API对程序完全可用。     
           在试图做任何渲染之前,要检查确定驱动程序的初始化过程中没有任何问题
     */    
    GLenum status = glewInit();    
    if (GLEW_OK != status) {                
        printf("GLEW Error:%s\n",glewGetErrorString(status));        
        return 1;    
    }

    //设置我们的渲染环境    
    setupRC();

    glutMainLoop();

    return  0;
}


设置渲染环境

这个部分主要是初始化一个渲染管理器,采用固定管线渲染。

//定义一个,着色管理器GLShaderManager shaderManager;
//简单的批次容器,是GLTools的一个简单的容器类。GLBatch triangleBatch;
//blockSize 边长GLfloat blockSize = 0.1f;
//正方形的4个点坐标
GLfloat vVerts[] = {        
    -blockSize,-blockSize,0.0f,        
    blockSize,-blockSize,0.0f,        
    blockSize,blockSize,0.0f,        
    -blockSize,blockSize,0.0f};

void setupRC() {    
    //设置清屏颜色(背景颜色)    
    glClearColor(1.0f, 1.0f, 1.0f, 1);

    //没有着色器,在OpenGL 核心框架中是无法进行任何渲染的。初始化一个渲染管理器。    
    shaderManager.InitializeStockShaders();

    //修改为GL_TRIANGLE_FAN ,4个顶点    
    triangleBatch.Begin(GL_TRIANGLE_FAN, 4);    
    triangleBatch.CopyVertexData3f(vVerts);    
    triangleBatch.End();
}


事件循环

GLUT 内部运行一个本地消息循环,拦截适当的消息,然后调用我们不同时间注册的回调函数,与iOS中的runloop机制类似。

重塑事件:当窗口大小变化时,同步调整OpenGL的视口大小。

/* 在窗口大小改变时,接收新的宽度&高度。 */
void changeSize(int w,int h) {    
    /*      
    x,y 参数代表窗口中视图的左下角坐标,而宽度、高度是像素为表示,通常x,y 都是为0    
     */    
    glViewport(0, 0, w, h);
}

显示事件:清除缓存区状态,通过固定着色器绘制图形数据,执行缓存区的切换。

void RenderScene(void) {
    //清除一个或者一组特定的缓存区    
    /*     缓冲区是一块存在图像信息的储存空间,红色、绿色、蓝色和alpha分量通常一起分量通常一起作为颜色缓存区或像素缓存区引用。     
    OpenGL 中不止一种缓冲区(颜色缓存区、深度缓存区和模板缓存区)     
    清除缓存区对数值进行预置     
    参数:指定将要清除的缓存的     
    GL_COLOR_BUFFER_BIT :指示当前激活的用来进行颜色写入缓冲区     
    GL_DEPTH_BUFFER_BIT :指示深度缓存区     
    GL_STENCIL_BUFFER_BIT:指示模板缓冲区    
    */    
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);    
    GLfloat vRed[] = {1.0f,0.0f,0.0f,0.0f};    
    //平移    
    M3DMatrix44f mTransfromMatrix;    
    m3dTranslationMatrix44(mTransfromMatrix, xPos, yPos, 0.0f);    
    //将矩阵结果 提交给固定着色器(平面着色器)中绘制    
    shaderManager.UseStockShader(GLT_SHADER_FLAT,mTransfromMatrix,vRed);    
    //提交着色器    
    triangleBatch.Draw();    
    //执行交换缓存区    
    glutSwapBuffers();
}

特殊事件:注册键盘响应,可以控制图形进行位移变换,最后触发glutPostRedisplay()进行重绘,类似iOS的setNeedsDisplay。

GLfloat xPos = 0.0f;
GLfloat yPos = 0.0f;
void SpecialKeys(int key, int x, int y) {    
    GLfloat stepSize = 0.025f;    
    if (key == GLUT_KEY_UP) {        
        yPos += stepSize;    
    }    
    if (key == GLUT_KEY_DOWN) {        
        yPos -= stepSize;    
    }    
    if (key == GLUT_KEY_LEFT) {        
        xPos -= stepSize;    
    }    
    if (key == GLUT_KEY_RIGHT) {        
        xPos += stepSize;    
    }        
    //碰撞检测    
    if (xPos < (-1.0f + blockSize)) {        
        xPos = -1.0f + blockSize;    
    }    
    if (xPos > (1.0f - blockSize)) {        
        xPos = 1.0f - blockSize;    
    }    
    if (yPos < (-1.0f + blockSize)) {        
        yPos = -1.0f + blockSize;    
    }    
    if (yPos > (1.0f - blockSize)) {        
        yPos = 1.0f - blockSize;    
    }    
    glutPostRedisplay();
}