从零开始学习OpenGL ES之一基本概念 by fengliu.docx

上传人:b****7 文档编号:9413743 上传时间:2023-02-04 格式:DOCX 页数:185 大小:2.40MB
下载 相关 举报
从零开始学习OpenGL ES之一基本概念 by fengliu.docx_第1页
第1页 / 共185页
从零开始学习OpenGL ES之一基本概念 by fengliu.docx_第2页
第2页 / 共185页
从零开始学习OpenGL ES之一基本概念 by fengliu.docx_第3页
第3页 / 共185页
从零开始学习OpenGL ES之一基本概念 by fengliu.docx_第4页
第4页 / 共185页
从零开始学习OpenGL ES之一基本概念 by fengliu.docx_第5页
第5页 / 共185页
点击查看更多>>
下载资源
资源描述

从零开始学习OpenGL ES之一基本概念 by fengliu.docx

《从零开始学习OpenGL ES之一基本概念 by fengliu.docx》由会员分享,可在线阅读,更多相关《从零开始学习OpenGL ES之一基本概念 by fengliu.docx(185页珍藏版)》请在冰豆网上搜索。

从零开始学习OpenGL ES之一基本概念 by fengliu.docx

从零开始学习OpenGLES之一基本概念byfengliu

从零开始学习OpenGLES之一–基本概念byfengliu

我曾写过一些文章介绍iPhoneOpenGLES编程,但大部分针对的是已经至少懂得一些3D编程知识的人。


作为起点,请下载我的OpenGLXcode项目模板,而不要使用Apple提供的模板。

你可以解压到下面位置安装此模板:


/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/ProjectTemplates/Application/

已经有大量有关OpenGL的好教程和书籍。

但是,却没有多少是关于OpenGLES,而且没有(至少在我撰写此文时)是专门针对学习iPhone上3D编程的。

因为大部分有关学习OpenGL的材料是从所谓“直接模式(directmode)”开始的,而OpenGLES并不支持此模式,对于没有3D背景知识的iPhone开发者而言,使用现有的书籍和教程是十分困难的。

为满足一些开发者的要求,我决定撰写一个针对3D初学者的博文系列。

这是此系列的第一篇文章。

OpenGL数据类型

首先我们要讨论的是OpenGL的数据类型。

因为OpenGL是一个跨平台的API,数据类型的大小会随使用的编程语言以及处理器(64位,32位,16位)等的不同而不同,所以OpenGL定义了自己的数据类型。

当传递数据到OpenGL时,你应该坚持使用这些OpenGL的数据类型,从而保证传递数据的尺寸和精度正确。

不这样做的后果是可能会导致无法预料的结果或由于运行时的数据转换造成效率低下。

不论平台或语言实现的OpenGL都采用这种方式定义数据类型以保证在各平台上数据的尺寸一致,并使平台间OpenGL代码移植更为容易。

下面是OpenGL的各种数据类型:

•GLenum:

用于GL枚举的无符号整型。

通常用于通知OpenGL由指针传递的存储于数组中数据的类型(例如,GL_FLOAT用于指示数组由GLfloat组成)。

•GLboolean:

用于单布尔值。

OpenGLES还定义了其自己的“真”和“假”值(GL_TRUE和GL_FALSE)以避免平台和语言的差别。

当向OpenGL传递布尔值时,请使用这些值而不是使用YES或NO(尽管由于它们的定义实际没有区别,即使你不小心使用了YES或NO。

但是,使用GL-定义值是一个好的习惯。

•GLbitfield:

用于将多个布尔值(最多32个)打包到单个使用位操作变量的四字节整型。

我们将在第一次使用位域变量时详细介绍,请参阅 wikipedia

•GLbyte:

有符号单字节整型,包含数值从-128到127

•GLshort:

有符号双字节整型,包含数值从−32,768到32,767

•GLint:

有符号四字节整型,包含数值从−2,147,483,648到2,147,483,647

•GLsizei:

有符号四字节整型,用于代表数据的尺寸(字节),类似于C中的size_t

•GLubyte:

无符号单字节整型,包含数值从0到255。

•GLushort:

无符号双字节整型,包含数值从0到65,535

•GLuint:

无符号四字节整型,包含数值从0到4,294,967,295

•GLfloat:

四字节精度IEEE754-1985 浮点数

•GLclampf:

这也是四字节精度浮点数,但OpenGL使用GLclampf特别表示数值为0.0到1.0

•GLvoid:

void值用于指示一个函数没有返回值,或没有参数

•GLfixed:

 定点数 使用整型数存储实数。

由于大部分计算机处理器在处理整型数比处理浮点数快很多,这通常是对3D系统的优化方式。

但因为iPhone具有用于浮点运算的矢量处理器,我们将不讨论定点运算或GLfixed数据类型。

•GLclampx:

另一种定点型,用于使用定点运算来表示0.0到1.0之间的实数。

正如GLfixed,我们不会讨论或使用它。

OpenGLES(至少iPhone目前所使用的版本)不支持8字节(64位)数据类型,如long或double。

OpenGL其实具有这些大型数据类型,但考虑到大部分嵌入式设备屏幕尺寸以及可能为它们所写的程序类型而且使用它们有可能对性能造成不利的影响,最后的决定是在OpenGLES中排除这些数据类型。

点或顶点

3D图像的最小单位称为 点(point) 或者 顶点vertex。

它们代表三维空间中的一个点并用来建造更复杂的物体。

多边形就是由点构成,而物体是由多个多边形组成。

尽管通常OpenGL支持多种多边形,但OpenGLES只支持三边形(即三角形)。

如果你回忆高中学过的几何学,你可能会记得所谓笛卡尔坐标。

基本概念是在空间中任选一点,称作原点。

然后你可以通过参照原点并使用三个代表三维的数值指定空间中的任意一点,坐标是由三个想象的通过原点线表示的。

从左至右的想象直线叫x-轴。

沿着x-轴从左至右数值变大,向左移动数值变小。

原点左方x为负值,右边为正值。

另外两轴同理。

沿y轴向上,y值增加,向下y值减小。

原点上方y为正,原点下方为负。

对于z轴,当物体离开观察者,数值变小,向观察者移动(或超出观察者),数值变大。

原点前方z值为正,原点之后为负。

下图帮助说明了这一点:

Note:

 iPhone上另一种绘图框架CoreGraphics使用了稍微不同的坐标系统,当向屏幕上方移动时y值减小,而向下移动y值增加。

沿各轴增加或减小的数值是以任意刻度进行的–它们不代表任何真实单位,如英尺,英寸或米等。

你可以选择任何对你的程序有意义的刻度。

如果你想设计的游戏以英尺为单位,你可以那样做。

如果你希望单位为毫米,同样可行。

OpenGL不管它对最终用户代表什么,只是将它作为单位处理,保证它们具有相同的距离。

由于任何物体在三维空间中的方位可以由三个数值表示,物体的位置通常在OpenGL中由使用一个三维数组的三个GLfloat变量表示,数组中的第一项(索引0)为x位置,第二项(索引1)为y位置,第三项(索引2)为z位置。

下面是一个创建OpenGLES顶点的简单例子:

GLfloatvertex[3];

vertex[0]=10.0;//x

vertex[1]=23.75;//y

vertex[2]=-12.532;//z

在OpenGLES中,通常将场景中所有构成所有或部分物体的提交为顶点数组。

一个顶点数组是包括场景中部分或所有顶点数据的简单数组。

我将在系列的下一篇教程中讨论,有关顶点数组要记住的是它们的大小是基于呈现的顶点数乘以三(三维空间绘图)或二(二维空间绘图)。

所以一个包含六个三维空间中的三角形的顶点数组由54个GLfloat组成,因为每个三角形有三个顶点,而每个顶点有三个坐标,即6x3x3=54。

处理所有这些GLfloat是很痛苦的事情。

幸运的是,有一个容易的方法。

我们可以定义一个数据结构了保存多个顶点,像这样:

typedefstruct{

GLfloatx;

GLfloaty;

GLfloatz;

}Vertex3D;通过这样做,我们的代码可读性更强:

Vertex3Dvertex;
vertex.x=10.0;
vertex.y=23.75;
vertex.z=-12.532;

 

现在由于Vertex3D由三个GLfloat组成,向Vertex3D传递指针与向数组传递一个包含三个GLfloat的数组的指针完全一样。

对于电脑而言毫无分别;两者具有同样的尺寸和同样的字节数以及OpenGL需要的同样的顺序。

将数据分组到数据结构只是让程序员感到更容易,处理起来更方便。

如果你下载了文章开头处的Xcode模板,你会发现此数据结构以及我后面将讨论的各种函数都定义在文件OpenGLCommon.h中。

还有一个内联函数用于创建单个顶点:

staticinlineVertex3DVertex3DMake(CGFloatinX,CGFloatinY,CGFloatinZ)
{
Vertex3Dret;
ret.x=inX;
ret.y=inY;
ret.z=inZ;
returnret;
}

如果你回忆起几何学(如果不记得也不要紧)的内容,你会知道空间中两点间的距离是使用下面公式计算的:

我们可以在一个简单的内联函数中实现这个公式来计算三维空间中任何两点间的直线距离:

staticinlineGLfloatVertex3DCalculateDistanceBetweenVertices(Vertex3Dfirst,Vertex3Dsecond)
{
GLfloatdeltaX=second.x–first.x;
GLfloatdeltaY=second.y–first.y;
GLfloatdeltaZ=second.z–first.z;
returnsqrtf(deltaX*deltaX+deltaY*deltaY+deltaZ*deltaZ);
};

 

三角形

由于OpenGLES仅支持三角形,因此我们可以通过创建一个数据结构将三个顶点组合成一个三角形物体。

typedefstruct{

Vertex3Dv1;

Vertex3Dv2;

Vertex3Dv3;

}Triangle3D;

一个 Triangle3D实际上与一个九个GLfloat构成的数组是完全一样的,因为我们通过顶点和三角形而不是GLfloat数组来构建物体,所以它能帮助我们更容易地处理我们的代码。

然而关于三角形你需要知道更多的事情。

在OpenGL中有一个概念叫卷绕(winding),它表示顶点绘制的次序是重要的。

不像真实世界中的物体,OpenGL中的多边形通常都不会有两面。

它们只有一面,被当做frontface(前面), 三角形只有其frontface面对观察者时才可见。

可以设置OpenGL将多边形作为两面处理,但默认状态下,三角形只有一个可见面。

通过知道哪一个面是多边形的前面或可见面,才能使OpenGL只做一半的计算。

尽管有时多边形也可以独立存在,需要绘制其背面,但通常三角形是一个大物体的一部分,其面对物体内部的一面永远也不可见。

不被绘制的一面称为backface(背面),OpenGL是通过观察顶点的绘制次序来确定frontface和backface的。

以反时针次序绘制顶点的构成的面是frontface(默认,可以改变)。

由于OpenGL可以很容易确定哪个三角形对用户可见,所以它使用了一种称为BackfaceCulling(隐面消除) 的技术来避免绘制视窗中多边形的不可见面。

下一篇文章将讨论视窗,现在你可将其想象成一个虚拟摄像或观察OpenGL世界的虚拟窗口。

上图中,左边青色的三角形是backface,因此将不可见。

而右方的三角形是frontface,所以将被绘制。

本系列的下一篇文章将设定一个OpenGL的虚拟世界并使用Vertex3D 和 Triangle3D进行一些基本绘图。

再后,我们将讨论变换,它使用线性代数在虚拟世界中移动物体。

从零开始学习OpenGLES之二–简单绘图概述byfengliu

还有许多理论知识需要讨论,但与其花许多时间在复杂的数学公式或难以理解的概念上,还不如让我们开始熟悉OpenGLES的基本绘图功能。


请下载OpenGLXcode项目模板。

我们使用此模板而不是Apple提供的模板。

你可以解压到下面目录来安装它:

/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/ProjectTemplates/Application/

此模板用于全屏OpenGL程序,它具有一个OpenGL视图以及相应的视图控制器。

大部分时候你不需要动到此视图。

此视图用于处理一些诸如缓存切换之类的事物,但在两处调用了其控制器类。

首先,当设定视图时,调用了一次控制器。

调用视图控制器的 setupView:

 方法使控制器有机会增加所需的设定工作。

这里是你设定视口,添加光源以及进行其他项目相关设定的地方。

现在我们将忽略此方法。

此方法中已经有非常基本的设定以允许你进行简单地绘图。

控制器的 drawView:

 方法根据常数kRenderingFrequency的值定期地被调用。

kRenderingFrequency的初始值为15.0,表示  drawView:

 方法每秒钟被调用15次。

如果你希望改变渲染的频率,你可以在ConstantsAndMacros.h中找到此常数的定义。

首先加入下列代码到GLViewController.m的drawView:

 方法中:

•- (void)drawView:

(GLView*)view;

•{

•    Vertex3D    vertex1 = Vertex3DMake(0.0, 1.0, -3.0);

•    Vertex3D    vertex2 = Vertex3DMake(1.0, 0.0, -3.0);

•    Vertex3D    vertex3 = Vertex3DMake(-1.0, 0.0, -3.0);

•    Triangle3D  triangle = Triangle3DMake(vertex1, vertex2, vertex3);

• 

•    glLoadIdentity();

•    glClearColor(0.7, 0.7, 0.7, 1.0);

•    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

•    glEnableClientState(GL_VERTEX_ARRAY);

•    glColor4f(1.0, 0.0, 0.0, 1.0);

•    glVertexPointer(3, GL_FLOAT, 0, >triangle);

•    glDrawArrays(GL_TRIANGLES, 0, 9);

•    glDisableClientState(GL_VERTEX_ARRAY);

•}

在讨论我们到底做了什么之前,先运行一下,你应该看到以下画面:

这是个简单的方法;如果你试过了,你可能已经知道我们到底做了什么,但这里我们还是一起过一遍。

因为我们的任务是画三角形,所以需要三个顶点,因此我们创建三个上一篇文章中讨论过的Vertex3D对象:

Vertex3Dvertex1=Vertex3DMake(0.0,1.0,-3.0);

Vertex3Dvertex2=Vertex3DMake(1.0,0.0,-3.0);

Vertex3Dvertex3=Vertex3DMake(-1.0,0.0,-3.0);

你应该注意到了三个顶点的z值是一样的,其值(-3.0)是处于原点“之后”的。

因为我们还没有做任何改变,所以我们是站在原点上观察虚拟世界的,这是默认的起点位置。

将三角形放置在z值为-3处,可以保证我们可以在屏幕上看到它。

随后,我们创建一个由这三个顶点构成的三角形。

Triangle3Dtriangle=Triangle3DMake(vertex1,vertex2,vertex3);

这些代码很容易理解,对吗?

但是,在幕后,电脑是将其视为一个包含9个 GLfloat 的数组。

如下:

GLfloattriangle[]={0.0,1.0,-3.0,1.0,0.0,-3.0,-1.0,0.0,-3.0};

并不是完全相同–这里有一个很小但很重要的区别。

在我们的示例中,我们传递给OpenGL的是 Triangle3D对象的地址(即 &triangle),但在第二个使用数组的示例中,由于C数组是指针,我们传递的是数组。

现在不需要考虑太多,因为这将是最后一次我用这种方式(第二种方法)定义一个 Triangle3D对象。

等一下我将解释原因,但现在让我们先过一遍代码。

下一步我们做的是加载单位矩阵。

我将花至少一整篇文章讨论变换矩阵。

我们暂且将其视为OpenGL的“复位开关”。

它将清除虚拟世界中的一切旋转,移动或其他变化并将观察者置于原点。

glLoadIdentity();

之后,我们告诉OpenGL所有的绘制工作是在一个灰色背景上进行的。

OpenGL通常需要用四个钳位值来定义颜色。

上一篇文章中有提过,钳位浮点数是0.0到1.0之间的浮点数。

我们通过定义红,绿,蓝以及alpha元素来定义颜色,alpha值定义了颜色之后物体的透视程度。

现在暂时不用管它,将其设为1.0,代表完全不透明。

glClearColor(0.7,0.7,0.7,1.0);

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

在OpenGL中要定义白色,我们需要将四个元素全部设为1.0。

要定义不透明的黑色,则定义红,绿,蓝为0.0,alpha为1.0。

上例代码的第二行是通知OpenGL清除以前的一切图形并将其设为clear颜色。

你可能想知道 glClear()的两个参数是什么意思。

简单地说,它们是存储与位域中的常量。

OpenGL保存了一系列 缓存(buffers),即用于绘图各方面的内存块。

将这两个值进行逻辑或是通知OpenGL清除两个不同的缓存– 颜色缓存(colorbuffer) 和 深度缓存(depthbuffer)。

颜色缓存保存当前帧各像素的颜色。

基本上就是你在屏幕上看到的。

深度缓存(有时也称为“z-buffer”)保存每个潜在像素离观察者距离的信息。

使用此信息可以确定一个像素是否需要被绘制出来。

这两个缓存是OpenGL中最常见的缓存。

还有其他一些缓存,如模板缓存(stencilbuffer)和累积缓存(accumulationbuffer),但现在我们暂时不讨论这些。

我们现在只需记住在绘制一帧之前,必须清除这两个缓存以保证不会和以前的内容混杂。

然后,我们要启动OpenGL的一项称为vertexarrays(顶点数组)的特性。

此特性可能只需要setupView:

方法中启动一次,但作为基本准则,我喜欢启动和禁止我使用的功能。

你永远也不会知道是否另一段代码会做不同处理。

如果你打开你需要的功能然后关闭它,产生问题的几率将大为减小。

就本例来说,如果另一个类不使用顶点数组而使用顶点缓存的话,任何一段代码遗留了启动了的特性或没有显性启动其需要的特性,这一段或两段代码都会导致不可预知的结果。

glEnableClientState(GL_VERTEX_ARRAY);

接下来我们设置了绘图时所需的颜色。

此行代码将绘图颜色设为鲜艳的红色。

glColor4f(1.0,0.0,0.0,1.0);

现在,直到下次调用 glColor4f()前所有的图形都是以红色绘制。

有一些例外的情况,例如绘制纹理形状时,但基本上,这样设定颜色可以使颜色保持。

由于我们使用顶点数组,我们必须通知OpenGL顶点的数组在什么地方。

记住,顶点数组只是一个GLfloat的C数组,每三个值代表一个顶点。

我们创建了Triangle3D 对象,但在内存中,它完全等同于9个连续的GLfloat,所以我们可以传递此三角形对象的地址。

glVertexPointer(3,GL_FLOAT,0,&triangle);

glVertexPointer() 的第一个参数指示了多少个GLfloat代表一个顶点。

根据你是在进行二维或三维绘图,你可以传递2或者3。

尽管我们的物体是存在于一个平面的,我们仍然将其绘制在三维虚拟世界中,因此每个顶点用三个数值表示,所以我们传递3给函数。

然后,我们传递一个枚举值告诉OpenGL顶点是由GLfloat构成。

OpenGLES允许你在当地数组中使用大部分的数据类型,但除GL_FLOAT外,其他都很少见。

下一个参数…现在不需要考虑下一个参数。

那是以后讨论的主题。

现在,它始终为0。

在以后的文章中,我将讨论怎样使用此参数将同一对象以不同的数据类型混杂在一个数据结构中。

随后,我们通知OpenGL通过刚才提交的顶点数组来绘制三角形。

glDrawArrays(GL_TRIANGLES,0,9);

你可能已经可以猜到,第一个枚举值是告诉OpenGL绘制什么。

尽管OpenGLES不支持绘制三角形之外的四边形或其他多边形,但它仍然支持一些其他绘图模式,如绘制点,线,线回路,三角形条和三角形扇。

稍后我们将讨论这些绘图模式。

最后,我们要禁止先前启动了的特性以保证不会被其他地方的代码弄混。

本例中没有其他的代码了,但通常你可以使用OpenGL绘制多个物体。

glDisableClientState(GL_VERTEX_ARRAY);

好了,我们的代码可以工作了尽管它不是那么引人入胜而且不是十分高效,但它确确实实可以工作。

每秒钟我们的代码被调用数次。

不相信?

加入下列黑体代码再次运行:

1.- (void)drawView:

(GLView*)view;

2.{

3.    static GLfloat rotation = 0.0;

4. 

5.    Vertex3D    vertex1 = Vertex3DMake(0.0, 1.0, -3.0);

6.    Vertex3D    vertex2 = Vertex3DMake(1.0, 0.0, -3.0);

7.    Vertex3D    vertex3 = Vertex3DMake(-1.0, 0.0, -3.0);

8.    Triangle3D  triangle = Triangle3DMake(vertex1, vertex2, vertex3);

9. 

10.    glLoadIdentity();

11.    glRotatef(rotation, 0.0, 0.0, 1.0);

12.    glClearColor(0.7, 0.7, 0.7, 1.0);

13.    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

14.    glEnableClientState(GL_VERTEX_ARRAY);

15.    glColor4f(1.0, 0.0, 0.0, 1.0);

16.    glVertexPointer(3, GL_FLOAT, 0, >triangle);

17.    glDrawArrays(GL_TRIANGLES, 0, 9);

18.    glDisableClientState(GL_VERTEX_ARRAY);

19. 

20.    rotation+= 0.5;

21.}

当你再次运行时,三角形将沿着原点缓缓转动。

先不需要关注太多旋转的逻辑。

我只是想告诉你我们的代码每秒钟被调用了多次。

如果你想画正方形怎么办?

OpenGLES并不支持正方形,所以我们只能通过三角形来定义正方形。

这很简单–一个正方形可以通过两个三角形构成。

我们要怎样调整上叙代码来绘制两个三角形?

是不是可以创建两个Triangle3D?

是的,你可以这样做,但那没有效率。

我们最好将两个三角形置入同一个顶点数组中。

我们可以通过定义一个包含

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > PPT模板 > 其它模板

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1