从零开始学习OpenGLES之二简单绘图概述.docx
- 文档编号:10567373
- 上传时间:2023-02-21
- 格式:DOCX
- 页数:18
- 大小:110.56KB
从零开始学习OpenGLES之二简单绘图概述.docx
《从零开始学习OpenGLES之二简单绘图概述.docx》由会员分享,可在线阅读,更多相关《从零开始学习OpenGLES之二简单绘图概述.docx(18页珍藏版)》请在冰豆网上搜索。
从零开始学习OpenGLES之二简单绘图概述
在一些地方找到该资料整理了哈希望对新手有用。
还有许多理论知识需要讨论,但与其花许多时间在复杂的数学公式或难以理解的概念上,还不如让我们开始熟悉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;
{
Vertex3Dvertex1=Vertex3DMake(0.0,1.0,-3.0);
Vertex3Dvertex2=Vertex3DMake(1.0,0.0,-3.0);
Vertex3Dvertex3=Vertex3DMake(-1.0,0.0,-3.0);
Triangle3Dtriangle=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);
好了,我们的代码可以工作了尽管它不是那么引人入胜而且不是十分高效,但它确确实实可以工作。
每秒钟我们的代码被调用数次。
不相信?
加入下列黑体代码再次运行:
-(void)drawView:
(GLView*)view;
{
staticGLfloatrotation=0.0;
Vertex3Dvertex1=Vertex3DMake(0.0,1.0,-3.0);
Vertex3Dvertex2=Vertex3DMake(1.0,0.0,-3.0);
Vertex3Dvertex3=Vertex3DMake(-1.0,0.0,-3.0);
Triangle3Dtriangle=Triangle3DMake(vertex1,vertex2,vertex3);
glLoadIdentity();
glRotatef(rotation,0.0,0.0,1.0);
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);
rotation+=0.5;
}
当你再次运行时,三角形将沿着原点缓缓转动。
先不需要关注太多旋转的逻辑。
我只是想告诉你我们的代码每秒钟被调用了多次。
如果你想画正方形怎么办?
OpenGLES并不支持正方形,所以我们只能通过三角形来定义正方形。
这很简单–一个正方形可以通过两个三角形构成。
我们要怎样调整上叙代码来绘制两个三角形?
是不是可以创建两个Triangle3D?
是的,你可以这样做,但那没有效率。
我们最好将两个三角形置入同一个顶点数组中。
我们可以通过定义一个包含两个Triangle3D对象的数组,或分配大小等于两个Triangle3D对象或18个GLfloat的内存.
这是一种方法:
-(void)drawView:
(GLView*)view;
{
Triangle3Dtriangle[2];
triangle[0].v1=Vertex3DMake(0.0,1.0,-3.0);
triangle[0].v2=Vertex3DMake(1.0,0.0,-3.0);
triangle[0].v3=Vertex3DMake(-1.0,0.0,-3.0);
triangle[1].v1=Vertex3DMake(-1.0,0.0,-3.0);
triangle[1].v2=Vertex3DMake(1.0,0.0,-3.0);
triangle[1].v3=Vertex3DMake(0.0,-1.0,-3.0);
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,18);
glDisableClientState(GL_VERTEX_ARRAY);
}
运行,你将看到如下屏幕:
由于Vertex3DMake()方法在堆中创建新的Vertex3D然后复制其值到数组中而造成额外的内存被占用,因此上述代码并不理想。
TheUnrealUniverse
ABookonPhysicsandPhilosophy
Pages:
292
(282ineBook)
Trimsize:
6"x9"
Illustrations:
34
(9incolorineBook)
Tables:
8
Bibliography:
Yes
Index:
Yes
ISBN:
9789810575946
对于这样简单的情况是没有什么问题的,但是在一个更为复杂的情况下,如果定义的3D物体很大时,那么你不会希望将其分配在堆中而且你不会希望对一个顶点不止一次地进行内存分配,所以最好是养成习惯通过我们的老朋友malloc()(我更喜欢用calloc(),因为它会将所有值设为0,比较易于查找错误)将顶点分配在栈中。
首先我们需要一个函数设定现存顶点的值而不是像Vertex3DMake()一样创建一个新对象。
如下:
staticinlinevoidVertex3DSet(Vertex3D*vertex,CGFloatinX,CGFloatinY,CGFloatinZ)
{
vertex->x=inX;
vertex->y=inY;
vertex->z=inZ;
}
现在,我们使用新方法将两个三角形分配在栈中,重写代码:
-(void)drawView:
(GLView*)view;
{
Triangle3D*triangles=malloc(sizeof(Triangle3D)*2);
Vertex3DSet(&triangles[0].v1,0.0,1.0,-3.0);
Vertex3DSet(&triangles[0].v2,1.0,0.0,-3.0);
Vertex3DSet(&triangles[0].v3,-1.0,0.0,-3.0);
Vertex3DSet(&triangles[1].v1,-1.0,0.0,-3.0);
Vertex3DSet(&triangles[1].v2,1.0,0.0,-3.0);
Vertex3DSet(&triangles[1].v3,0.0,-1.0,-3.0);
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,triangles);
glDrawArrays(GL_TRIANGLES,0,18);
glDisableClientState(GL_VERTEX_ARRAY);
if(triangles!
=NULL)
free(triangles);
}
好了,我们已经讨论了许多基础知识,我们现在更深入一点。
记住我说过OpenGLES不止一种绘图方式吗?
现在正方形需要6个顶点(18个GLfloat),实际上我们可以使用trianglestrips(GL_TRIANGLE_STRIP)方法通过四个顶点(12个GLfloat)来绘制正方形。
这里是三角形条的基本概念:
第一个三角形条是由前三个顶点构成(索引0,1,2)。
第二个三角形条是由前一个三角形的两个顶点加上数组中的下一个顶点构成,继续直到整个数组结束。
看下图更清楚–第一个三角形由顶点1,2,3构成,下一个三角形由顶点2,3,4构成,等等:
所以,我们的正方形是这样构成的:
代码如下:
-(void)drawView:
(GLView*)view;
{
Vertex3D*vertices=malloc(sizeof(Vertex3D)*4);
Vertex3DSet(&vertices[0],0.0,1.0,-3.0);
Vertex3DSet(&vertices[1],1.0,0.0,-3.0);
Vertex3DSet(&vertices[2],-1.0,0.0,-3.0);
Vertex3DSet(&vertices[3],0.0,-1.0,-3.0);
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,vertices);
glDrawArrays(GL_TRIANGLE_STRIP,0,12);
glDisableClientState(GL_VERTEX_ARRAY);
if(vertices!
=NULL)
free(vertices);
}
我们再返回到第一段代码看看。
记住我们是怎样绘制第一个三角形吗?
我们使用glColor4f()设置颜色并且说设置的颜色一直适用于随后的代码。
那意味着定义于顶点数组中的物体必须用同一种颜色绘制。
很有局限性,对吗?
并非如此。
正如OpenGLES允许你将所有顶点置于一个数组中,它还允许你将每个顶点使用的颜色置于一个颜色数组(colorarray)中。
如果你选择使用颜色数组,那么你需要为每个顶点设置颜色(四个GLfloat值)。
通过下面方法启动颜色数组:
glEnableClientState(GL_COLOR_ARRAY);
我们可以象顶点数组一样定义一个包含四个GLfloat成员的Color3D结构。
下面是怎样为原始三角形分配不同颜色的示例:
-(void)drawView:
(GLView*)view;
{
Vertex3Dvertex1=Vertex3DMake(0.0,1.0,-3.0);
Vertex3Dvertex2=Vertex3DMake(1.0,0.0,-3.0);
Vertex3Dvertex3=Vertex3DMake(-1.0,0.0,-3.0);
Triangle3Dtriangle=Triangle3DMake(vertex1,vertex2,vertex3);
Color3D*colors=malloc(sizeof(Color3D)*3);
Color3DSet(&colors[0],1.0,0.0,0.0,1.0);
Color3DSet(&colors[1],0.0,1.0,0.0,1.0);
Color3DSet(&colors[2],0.0,0.0,1.0,1.0);
glLoadIdentity();
glClearColor(0.7,0.7,0.7,1.0);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glColor4f(1.0,0.0,0.0,1.0);
glVertexPointer(3,GL_FLOAT,0,&triangle);
glColorPointer(4,GL_FLOAT,0,colors);
glDrawArrays(GL_TRIANGLES,0,9);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
if(colors!
=NULL)
free(colors);
}
运行,屏幕如下:
今天我
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 从零开始 学习 OpenGLES 简单 绘图 概述