• OpenGL开发之旅基础知识介绍


      最近由于手机项目中需要用到OpenGL ES的知识,所以这段时间正在研究OpenGL的相关知识。因为OpenGL ES是OpenGL的剪裁版本,所以我直接从OpenGL入手,然后再去看OpenGL ES就很容易上手。从此篇开始,我将发表一系列文章来逐步深入介绍OpenGL的相关知识,开发我们可以使用VC6.0或.NET。

      那么今天我要介绍的是OpenGL开发之旅基础知识介绍,这很重要,会让我们从整体上熟悉OpenGL的工作原理及过程。

       

      1. 保持模式与立即模式:
      通常情况下我们编写3D图形时可使用两种不同的方法:
      一种方法我们称之为保持模式。在保持模式中,我们可以向编写的API或是工具箱提供物体及场景的描述,然后图形包就会在屏幕上创建这个图像,我们需要做的就是提供命令去改变照相机或场景中其他物体的位置和观察方向。对于我们开发者而言,我们创建的能够对物体及场景的描述称为场景图,场景图是什么呢?大家可能对这个名称比较熟悉但却不能说出它的准确含义。在通常情况下,场景图是个有向无环图的数据结构,它包含了场景中的所有物体以及这些物体之间相互的关系,实现了对物体及场景的描述。据了解,现在许多的游戏引擎及高层工具箱都使用了这种方法。这使得我们开发人员不需要对其渲染过程进行特别精细的控制,它只需要向图形函数库提供一个模型或是场景,图形函数库就会负责进行渲染,大大减轻了我们开发人员的工作量。

      另一种方法我们称之为立即模式。在立即模式中,我们不需要像保持模式一样去提供一个模型或是场景,而是向图形处理器发送命令,图形处理器就会根据它的状态及发送的命令产生立即的效果。查询图形学书籍得知,大多数保持模式中的API或场景图在其内部使用一个立即模式的API执行实际的渲染任务。

      2. OpenGL是什么?

      OpenGL是一套应用程序编程接口(API),借助这个API我们开发人员就可以开发出对图形硬件具有访问的能力的程序。我们可以使用OpenGL开发出运行效率较高的图形程序或游戏,因为OpenGL非常接近底层硬件并且OpenGL使得我们不必去关注图形硬件的细节。既然我们开发人员不必关注图形硬件的细节,那么我们需要关注什么呢?我们需要关注OpenGL如何绘制,按照专业术语就是根据物体的规格参数及相关属性,借助虚拟照相机和光照生成一幅该物体的图像。OpenGL程序与平台是无关的,所以OpenGL API中不包含任何输入函数或窗口函数,原因是因为这两种函数都要依赖于特定的平台,例如Windows,Linux或是其他系统。

      OpenGL API是过程性的,不是描述性的,即OpenGL不是面向对象的,所以OpenGL无法利用面向对象的特性,例如重载,继承等,但是我们可以使用面向对象的程序与OpenGL的实现进行链接就可以了。作为开发人员来说,我们不需要去描述场景的性质和外观,而是去确定一些操作步骤,为些操作步骤是为实现一定图形或图像所服务的。我们在实现这些步骤时可以调用OpenGL中的一些命令,可以利用这些命令绘制点、直线、多边形或是其它图形,还可以调用这些命令实现光照、着色,动画等各种效果。

      OpenGL的实现可以是软件实现,也可以是硬件实现。软件实现是对OpengGL函数调用时作出的响应并创建二维或三维图像的函数库,那么硬件实现则是通过设置能够绘制图形或图像的图形卡驱动程序。一般来说,硬件实现要比软件实现快得多。我们都应该熟悉,在Windows上,是由图形设备接口将图形或图像显示在屏幕上或是其他显示设备上的。OpenGL的实现就软件实现来说,在Windows上会根据程序命令的要求,生成相应的图形或图像,然后会将这个图形或图像移交给图形设备接口,由图形设备接口将图形或是图像显示在我们的屏幕上或是其他显示设备。这样一说,我们可能会明白一点OpengGL原来是在应用程序和图形设备接口之间运作,但我感觉还不能准确地这样说。大家看下下面的图对OpenGL的工作原理可能会理解得更明白一点: 

      

      上图是OpenGL的软件实现的工作原理。需要注意的是上图中的构造图形是通过软件进行构造的。

      OpenGL的硬件实现与软件实现稍微有些不同,硬件实现是将OpenGL的调用传递给硬件驱动程序,而硬件驱动程序不会将生成的图形或图像传递给图形设备接口,而是直接与显示设备通信,直接将图形或图像结果传递给显示器或其他显示设备。如下图所示:

      

      

      OpenGL在绘制图形时是基于一个被称为流水线模型的模式。也就是说其中的几何图形在程序中通过描述空间位置或顶点来指定其形状并由程序生成,这些顶点在流经一系列模块时,每个模块在图形的基本组成部分(在这里称为图元)经过时对其实施一种或多种操作。模块负责对流经的图元实施一种或多种操作变换,例如:旋转、平移、缩放及对摄像机进行定位等。

      

      3. OpenGL的组成

      OpenGL中包含许多对图形图像处理的函数,主要包括以下几种:

      图元函数:指定要生成图形或图像的图元。主要有两种类型,一种是绘制二维或三维的几何图元,如点,线,多边形等;另一种是离散型的实体,例如:位图。

      属性函数:属性函数主要是控制图元的外观及样式,例如:对图元的颜色、线型、光照及纹理等效果处理。

      观察函数:观察函数主要是对摄像机属性的操作。我们可以操作摄像机显示图形或图像近距或是远距效果。

      控制函数:能够让我们启用或是彬各种OpenGL的特性。

      查询函数:可以让我们查询OpenGL状态变量的值。

      输入与窗口控制函数:这个本身不属于OpenGL,但是由于我们会经常在程序中输入输出或是窗口控制操作,所以,这些函数还是比较重要的。

      OpenGL函数库一般包含在两个库中,分别称为GL或GLU。GL是OpenGL的核心库,包含必需的OpenGL函数。GLU是OpenGL的实用库,包含许多的新函数。下面的代码显示了许多的Windows程序包含的典型头文件:

    #include <GL/gl.h>
    #include <GL/glu.h>
    #include <windows.h>

      但是为了实现和窗口系统的交互,一般使用如下代码引用头文件:

    #include <GL/glut.h>

      GLUT表示OpenGL工具箱,体现了现代窗口系统所共有的功能函数库。GLUT的目的就是隐藏平台的细节,glut.h已经包含了gl.h和glu.h。使用GLUT是因为OpenGL没有包含输入和窗口命令,而输入和窗口命令是由平台所决定的,与平台的相关性较大。但是前面说过,OpenGL是与平台无关的,也就是说OpenGL是跨平台的。这样设计人员就需要专门设计一个需要和窗口系统进行交互的函数库。

      为了能使OpenGL代码更易于从一个平台移植到另一个平台,OpenGL定义了它自己的数据类型,这些数据类型都可以映射到相应的C语言数据类型中。下图显示其映射关系:

      

      

      4. 开发语言与编程约定

      我们开发OpenGL目前最流行的做法是OpenGL的C语言绑定,当然也可以使用其它平台或语言,例如.NET、JAVA、Python、Perl等。我会在下一篇文章中介绍如何配置相应的环境。

      我们以后会见到OpenGL的函数多是以gl开头,因为OpenGL的函数遵循一定的命名约定,它可以告诉我们这个函数来自哪个函数库,并且还可以告诉我们这个函数的参数个数和类型。

      OpenGL的函数是采用以下的书写格式:

      <函数库前缀><根命令><可选的参数数量><可选的参数类型>

      以下是一个函数标注图:

      

      5. 坐标系与变换

      在开发OpenGL程序时,需要用到两个坐标系。一个称为对象坐标系,另一个称为世界坐标系。

      第一个坐标系是我们在开发中使用的坐标系;第二个坐标系又称为窗口坐标系或屏幕坐标系,在这个坐标系中的单位是像素。

      在绘制的过程中,OpenGL会自动实现从对象到窗口坐标系的转换,所需要的信息是屏幕中显示窗口的尺寸和用户希望显示对象空间的大小。OpenGL中所需要的坐标系变换由两个矩阵决定,即模型视图矩阵和投影矩阵,这些矩阵是OpenGL的状态的一部分。设置这两种矩阵的典型步骤包括以下三个步骤:

      (1) 指定我们希望修改的矩阵。

      (2) 将矩阵设为单位矩阵。

      (3) 修改当前矩阵为用户期望的矩阵。

      以上三个步骤分别对应以下代码:

    glMatrixMode(GL_PROJECTION)
    glLoadIdentity();
    gluortho2D(-1.0,1.0,-1.0,1.0)

      6. 图元及属性

      图元是图形系统中常用的基本实体,主要是指:点,线,直线,多边形,位图和像素,我们的2D或3D图形都可由这几个基本的图元来绘制。前面四个称为几何图元,主要是构建几何图形;后面二个称为非几何图元。OpenGL在处理几何图元和非几何图元的方式差别比较大。

      每个图元都有自己的属性,属性决定了由OpenGL显示的方式。比如多边形的颜色,形状,线条的粗细等。

      我们在绘制基本的图形时,总是以glBegin()函数开始,而以glEnd()函数结束,针对不同的图形,glBegin()函数中的参数不一样。如下所示是绘制两条直线段:

    glBegin(GL_LINES);
        glVertex2f(-0.5, -0.5);
        glVertex2f(-0.5, 0.5);
        glVertex2f(0.5, 0.5);
        glVertex2f(0.5, -0.5);
    glEnd();

      OpenGL在绘制图形时有很多功能,比如:光照、消隐、纹理映射等,每一种功能都将影响绘制处理的速度,在我们的程序中可单独的启用或是禁用某些功能,在不使用时要将其禁用掉以使我们的程序更加高效。以下是启用或是禁用某项功能的代码:

    void glEnable(GLenum feature)
    void glDisable(GLenum feature)
    
    //启用点划模式
    glEnable(GL_LINE-STIPPLE)

      7. 状态的保存

      OpenGL在内部就是一个状态机,函数调用会修改其内部的状态,OpenGL的状态决定了图元的行为和绘制方式。我们对图元的属性和其他状态变量所进行的全部修改,例如模型视图矩阵和投影矩阵,都会改变当前的状态。在OpenGL中提供了两种类型的堆栈,可将当前状态保存在堆栈中,以便以后使用。

      矩阵堆栈可用于保存投影矩阵和模型视图矩阵。每种类型的堆栈只能用来容纳相应类型的矩阵。所使用的矩阵由当前矩阵模式(GL_MODELVIEW或GL_PROJECTION)所决定的。可用函数glPushMatrix()和glPopMatrix()使矩阵入栈或出栈。

      矩阵堆栈的主要作用:一是在构建层次模型时,使用堆栈来遍历这些层次模型的树型数据结构;二是在进行绘制时可以回到先前的视图,而不需要我们重新计算绘制。我们会在开发过程中常看到以下代码:

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glPopMatrix();

      需要注意的是,入栈操作和出栈操作必须成对使用;一次出栈必须与一次入栈对应。在层次系统中,如果这对操作没有正确的成对出现的话,将使堆栈处于一种不可预知的状态。

      

      8. 单缓冲与双缓冲

      我们在构建图形窗口时经常也会经常看到以下的一行代码:

    glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB); 

      这行代码告诉我们在创建时使用何种类型的显示模式。GLUT_SINGLE表示使用一个单缓冲的窗口;GLUT_RGBA表示使用RGBA颜色模式。

      单缓冲窗口意味着所有的绘图命令都是在被显示的窗口上执行的。另一种显示模式是双缓冲窗口,绘图命令实际上是在一个屏幕之外的缓冲区中执行的,然后快速反应的交换到窗口的视图上进行显示。我们经常用双缓冲模式开发具有动画效果的程序,这样会提高我们程序的执行效率。

      9. 一个简单的示例

      好了,上面说了那么多,有的人可能想知道OpenGL的程序到底是什么样的,主框架是什么?大家如果有C语言编程经验的一眼就能看出来它的结构。

      接下来让我们看一个运行VC++环境中的OpenGL示例,代码如下:

    #include "StdAfx.h"
    #ifdef __APPLE__
    #include <GLUT/glut.h>
    #else
    #include <GL/glut.h>         /* glut.h 包含 gl.h和glu.h*/
    #endif
    
    void display(void)
    {
        // 清除颜色缓冲区
        glClear(GL_COLOR_BUFFER_BIT); 
    
        // 绘制矩形
        glBegin(GL_POLYGON);
             glVertex2f(-0.5, -0.5);
             glVertex2f(-0.5, 0.5);
             glVertex2f(0.5, 0.5);
             glVertex2f(0.5, -0.5);
        glEnd();
    
        // 执行缓冲区
        glFlush(); 
    }
    
    int main(int argc, char** argv)
    {
        // 初始化窗口
        glutInit(&argc,argv); 
        glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);  
        glutInitWindowSize(500,500);
        glutInitWindowPosition(0,0); 
        glutCreateWindow("绘制矩形"); 
        glutDisplayFunc(display);
        glutMainLoop();
    
        return 0;
    }

      这是运行在VC6.0中的一个示例,在创建项目时需要注意创建的是控制台项目。

      怎么样?main函数中的代码你能看懂它的意思吗?我相信对大家不是难事。下面看来简单的解释。

      (1) glutCreateWindow("绘制矩形")是创建一个标题是"绘制矩形"的窗口。

      (2) glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB)函数在上面已经说过了。

      (3) glutDisplayFunc(display); 这行代码是将前面定义的display()函数确定为显示回调函数。也就是说在窗口需要被绘制时,GLUT将会调用这个函数。比如说,当窗口第一次显示或是窗口大小改变的时候,或是窗口从被覆盖的状态中恢复时,就会发生这个调用。这也是我们放置OpenGL渲染函数调用的地方。

      (4) glutInitWindowSize(500,500)和glutInitWindowPosition(0,0)则是分别设置窗口的大小和位置。

      (5) glutMainLoop() 这个函数很重要。这个函数启动了GLUT框架的运行。该函数一经调用便不再返回,直到程序的结束。所以,该函数在我们的程序中只调用一次,它能处理操作系统中特定的消息及击键等事件操作,直到我们的程序结束。

      以上示例的效果是在窗体中绘制一个白色的矩形,效果图如下所示:

      

      该程序演示了在OpenGL中使用GLUT创建窗口的基本原理。

      以上便是OpenGL开发中的基本原理和基础知识。

      最后,希望转载的朋友能够尊重作者的劳动成果,加上转载地址:http://www.cnblogs.com/hanyonglu/archive/2012/05/13/2498110.html 谢谢。

     

      示例下载:/Files/hanyonglu/OpenGL/MyOpenGLDemo4.rar

      完毕。^_^

  • 相关阅读:
    struts.xml 配置
    result重定向到一个action
    Action类中通过ServlexxxAware接口的方式来获取来获取web资源
    Action类中通过ServletActionContext来获取web资源
    Action类中通过继承xxxAware接口来获取web资源
    Action类中通过ActionContext来获取web资源
    java中日期格式转换
    java类的执行顺序
    批量删除Redis数据库中的Key
    Python等同于PHP的 strip_tags?
  • 原文地址:https://www.cnblogs.com/hanyonglu/p/2498110.html
Copyright © 2020-2023  润新知