OpenGL ES 2.0基础知识

1. OpenGL ES 2.0可编程管道

    OpenGL负责把三维空间中的对象通过投影、光栅化转换为二维图像,然后呈现到屏幕上。


       上图黄色部分(Vertex Shader和Fragment Shader)为此管道的可编程部分。整个管道包含以下两个规范:

        1) OpenGL ES 2.0 API specification

        2) OpenGL ES Shading Language Specification (OpenGL ES SL)

       详细流程图如下:

        此流程把三维数据变换为可以显示的二维数据。

2. Shader(就是一段程序Program)

        Shader是一段执行在GPU上的程序(所以Shader也可以叫做Program),此程序使用OpenGL ES SL语言来编写。它是一个描述顶点或像素特性的简单程序。

2.1 Pixel Shader

       Pixel Shader(像素着色器)就是众所周知的Fragment Shader,它计算每个像素的颜色和其它属性。它通过应用光照值、凹凸贴图,阴影,镜面高光,半透明等处理来计算像素的颜色并输出。它也可改变像素的深度(z-buffering)或在多个渲染目标被激活的状态下输出多种颜色。一个Pixel Shader不能产生复杂的效果,因为它只在一个像素上进行操作,而不知道场景的几何形状。

2.2 Vertex Shader

         对于发送给GPU的每一个Vertex(顶点),都要执行一次Vertex Shader(顶点着色器)。其功能是把每个顶点在虚拟空间中的三维坐标变换为可以在屏幕上显示的二维坐标,并带有用于z-buffer的深度信息。Vertex Shader可以操作的属性有:位置、颜色、纹理坐标,但是不能创建新的顶点。

       Vertex Shader输入数据如下:

       1)Attributes: 通过顶点数组(vertex arrays)提供的每个顶点数据 

       2)Uniforms: Vertex Shader使用的常量数据
       3)Samplers: Vertex Shader使用的一个表示纹理类型的Uniforms,在Vertex Shader中是可选的

       4)Shader program: Vertex Shader程序源码或可执行文件,它描述了将在顶点上执行的操作

       Vertex Shader输出为:varying variables(Vertex Shader的输出变量,作为Fragment Shader的输入。必须与Fragment Shader中的输入Varying一一对应。)

       gl_Position: 是每个点固有的Varying,表示点的空间位置。

 A Vertex Shader Example:

 // uniforms used by the vertex shader
 uniform mat4 u_mvpMatrix; // matrix to convert P from model
                                               // space to normalized device space.
 // attributes input to the vertex shader
attribute vec4 a_position;   // position value
attribute vec4 a_color;       // input vertex color
// varying variables – input to the fragment shader
varying vec4 v_color;        // output vertex color
 void  main()
 {
      v_color = a_color;
      gl_Position = u_mvpMatrix * a_position;
}  

2.3 Geometry Shader

       Geometry Shader是一个相对新的Shader类型。它在Driect3D 10和OpenGL 3.2中开始引入,在OpengGL 2.0+中作为扩展使用。它的输入为:点、线和三角形;其输出为点、线带和三角形带。

       Geometry Shader程序在Vertex Shader程序执行之后执行。

3. Shading Language

       GLSL : Shading language developed for use with OpenGL
       HLSL : Shading language developed for use with the Direct3D API

       Shading Language仅适合于为GPU编程,编程工具为Cg(Nvidia开发),Cg编译器可以输出Direct3X或OpenGL Shader程序。

4.  Shading 算法

4.1 插值技术(可以和任何照明模型组合):
     • Flat shading 
     • Gouraud shading 
     • Phong shading 
4.2 照明模型 (可以和任何插值技术组合):

     • Blinn–Phong
     • Cook–Torrance (microfacets) 
     • Lambert 
     • Minnaert 
     • Oren–Nayar (Rough opaque diffuse surfaces) 
     • Phong 
     • Ward anisotropic

5. 原语汇编(Primitive Assembly)

       在管道(pipeline)中,执行完Vertex Shader之后,就执行原语汇编。一个原语(primitive)就是一个可以使用OpenGL ES画图命令进行绘制的几何对象

       在原语汇编阶段,被Vertex Shader处理输出的顶点被组合进行一个单独可被绘制的几何原语(如:点、线和三角形)。然后对几何原语执行以下两种操作:

       • Clipping(裁剪): 只保留在可视空间范围内的原语

       • Culling(选择): 基于面向前或后,进行原语的选择

       在裁剪和选择之后,其下一个阶段为进行Geometry Shader(如果存在)或光栅化处理。
6. 光栅(Rasterization)

       光栅处理的目标:把原语转换为一系列二维fragments,这些二维fragments将被Fragment Shader处理,且这些二维fragments表示可被绘制在屏幕上的像素。

7. Fragment Shader
     对每个光栅化产生的fragment执行一次Fragment Shader,其输入内容如下:

     • Varying variables:Vertex Shader的输出,且由光栅化单元对每个fragment使用插值方法产生
     • Uniforms:Fragment Shader使用的常量数据
     • Samplers:一个表示纹理的常量数据
     • Shader program:Fragment shader程序源码或可执行文件,它描述了将在fragment上执行的操作。

 

       Fragment Shader要么丢弃此fragment,要么为此fragment产生对应的color,并保存在gl_FragColor中.

        A Fragment Shader Example:
precision mediump float;
varying vec4 v_color; // input vertex color from vertex shader
void
main(void)
{
     gl_FragColor = v_color;
}

8. Per-Fragment Operations

      在执行Fragment Shader之后,其下一步处理为Per-Fragment Operation。其处理流程如下:

 

       执行Per-Fragment Operation的结果为此Fragment被丢弃,或在framebuffer对应位置(Xw,Yw)写入fragment颜色值深度值stencil值

 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颜色模式。

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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值