http://www.linuxidc.com/Linux/2011-09/43747p2.htm
现在你已经知道OpenGL是怎样绘图的了,让我们回头谈谈一个很重要的概念:OpenGL视口(viewport)。 许多人对3D编程还很陌生,那些使用过像Maya, Blender, 或 Lightwave之类3D图形程序的人都试图在OpenGL虚拟世界中找到“摄像机”。但OpenGL并不存在这样的东西。它所有的是在3D空间中定义可见的物体。虚拟世界是没有边界的,但计算机不可能处理无限的空间,所以OpenGL需要我们定义一个可以被观察者看到的空间。
如果我们从大部分3D程序具有的摄像机对象的角度出发来考虑,视口端点的中心就是摄像机。也就是观察者站的位置。它是一个观察虚拟世界的虚拟窗口。观察者可见的空间有一定限制。她看不见她身后的东西。她也看不见视角之外的东西。而且她还不能看见太远的东西。可以认为视口是通过“观察者可见”参数所确定的形状。很简单,对吗?
不幸的是,并非如此。要解释原因,我们首先需要讨论的是在OpenGL ES中具有的两种不同的视口类型:正交和透视。
正交和透视
为更好地理解,我们先看看铁路轨道,好吗?要正常工作,铁路的两条铁轨之间必须具有固定的距离。其固定的距离是由铁轨根据承载什么样的火车而决定。重要的是铁轨(以及火车的轮子)必须具有相同的距离。如果不是这样,火车根本不可能运行。
如果我们从上方观察铁轨,这个事实很明显。
但是如果你站在铁轨上向下观察会怎么样。不要说“你会被火车撞”,我假设你会足够聪明,会在没有火车开动时进行观察。
是的,铁轨看上去越远与靠近。感谢二年级美术老师,可能你已经知道这就是所谓透视(perspective)。
OpenGL可以设定的视口中的一种就是使用透视。当你这样设置视口时,物体会随着移远而越来越小,视线会在物体移离观察者时最终交汇。这是对真实视觉的模拟;人们就是以这种方式观察世界的。
另一种看设置的视口称为正交(orthogonal) 视口。这种类型的视口,视线永远不会交汇而且物体不会改变其大小。没有透视效果。对于CAD程序以及其他各种目的是十分方便的,但因为它不像人们眼睛观察的方式所以看上去是不真实的,通常也不是你所希望的。
对于正交视口,你看将摄像机置于铁轨上,但这些铁轨永远不会交汇。它们将随着远离你的视线而继续保持等距。即使你定义了一个无限大的视口(OpenGL ES并不支持),这些线仍保持等距。
正交视口的优点是容易定义。因为线永不交汇,你只需定义一个像箱子一样的3D空间,像这样:
设置正交视口在使用glViewport()函数定义视口前,你可以通过glOrthof()通知OpenGL ES你希望使用正交视口。下面是一个简单的例子:
CGRect rect = view.bounds; glOrthof(-1.0, // Left 1.0, // Right -1.0 / (rect.size.width / rect.size.height), // Bottom 1.0 / (rect.size.width / rect.size.height), // Top 0.01, // Near 10000.0); // Far glViewport(0, 0, rect.size.width, rect.size.height);
这不难理解。首先我们获取视窗的尺寸。然后设定视口空间的宽度为两个单位,沿x轴从 -1.0 到 +1.0。很容易吧。
接着怎样设定底部和顶部?我们希望我们定义空间的X和Y坐标的宽高比与视窗的宽高比(也就是iPhone全屏时的宽高比)一样。由于iPhone的宽度与高度不同,我们需要确保视口的x和y坐标不同,但遵循一样的比例。
之后,我们定义了 near(远)和 far(近) 范围来描述观察的深度。 near 参数说明了视口开始的位置。如果我们站在原点处,视口就位于我们的面前,所以习惯上使用 .01 或 .001 作为正交视口的起点。这使得视口处于原点“前方”一点点。far 可以根据你程序的需要来设定。如果你程序中的物体永远不会远过20个单位,那么你不需要将 far设置为20,000 个单位。具体的数字随程序的不同而不同。
调用 glOrthof()之后,我们使用视窗矩形来调用 glViewport()。
这是比较简单的情况。
另一种情况就不那么简单,这里是原因。如果物体随着远离观察者而变小,那么它和你定义的可见空间的形状有什么关系。随着视线越来越远,你可以看到更广阔的世界,所以如果你使用透视,那么你定义的空间将不是一个立方体。是的,当使用透视时可见空间的形状称为锥台(frustum)。 是的,我知道,奇怪的名字。但却是真实的。我们的锥台看上去像这样:
请注意当我们离视口越来越远时(换句话说,当z值减小时),观察体的x和y坐标都会越来越大。
要设置透视视口,我们不使用 glOrthof(),我们使用一个不同的函数 glFrustumf()。此函数使用同样的六个参数。很容易理解,但我们应该怎样确定传递给 glFrustumf()的参数?
near 和 far 容易理解。你可以同样方式理解它们。near 使用类似 .001的数值,然后根据不同程序的需要确定 far 值。
但是 left, right, bottom, 和 top 呢? 为设置这些值,我们需要一点点数学计算。
要计算锥台,我们首先要理解视野(field of vision)的概念,它是由两个角度定义的。让我们这样做:伸出双臂手掌合拢伸向前方。你的手臂现在指向你自己锥台的z轴,对吗?好,现在慢慢分开你的双臂。由于在你双臂展开时肩膀保持不动,你定义了一个逐渐增大的角度。这就是用于定义观察锥台的两个角度之一。它定义了视野的宽度。另一个角度的定义原理一样,只是这次你向上下展开你的双臂。如果你的双手间距只有三英寸,那么x角度将非常小。
这称为窄视野。
如果你双手分开两英尺,视野的宽度变得很大。
这就是所谓 宽视角(广角)。
如果用摄影术语描述,你可将视野当作虚拟相机的虚拟光圈的焦距。窄视野很像摄远镜头,它造就了一个缓慢增长的长锥台。宽视角就像广角镜,它造就了一个增长很快的锥台。
我们选择一个中间值,例如45°。 使用这个值,我们怎样计算我们的观察锥台?我们先看下两个角度中的一个。想象一下,从顶部看锥台是什么样子。下面是示意图:
从上向下看,它就像一个砍掉一个点的三角形。但对我们而言,它已经足够接近一个三角形。你还记得三角课上的正切吗?正切函数定义为直角对边与相邻边的比率。
但是,我们没有直角,是吗?
实际上,我们有两个直角… 如果我们沿z轴向下画一条直线的话:
中心虚线就是两个直角的“相邻边”。所以,锥台远端宽度的一半就是视野角度正切的一半。如果我们将此值乘以 near值,就可以得到 right值。right值取反就是 left。
我们希望视野具有与屏幕一样的长宽比,所以按照 glOrthof()中相同的方法(将 right 乘以屏幕的长宽比)来计算top 和 bottom 值。代码如下:
CGRect rect = view.bounds; GLfloat size = .01 * tanf(DEGREES_TO_RADIANS(45.0) / 2.0); glFrustumf(-size, // Left size, // Right -size / (rect.size.width / rect.size.height), // Bottom size / (rect.size.width / rect.size.height), // Top .01, // Near 1000.0); // Far
注意:关于 glFrustum() 怎样使用传递的参数计算锥台的形状将在我们讨论矩阵时讨论。现在,我们暂且相信计算是正确的,好吗?
让我们运用到程序中。我修改了上篇文章中最终的 drawView:方法,我们将沿z轴向下显示了三十个二十面体。下面是新的 drawView: 方法:
- (void)drawView:(GLView*)view;
{ static GLfloat rot = 0.0; static const Vertex3D vertices[]= { {0, -0.525731, 0.850651}, // vertices[0] {0.850651, 0, 0.525731}, // vertices[1] {0.850651, 0, -0.525731}, // vertices[2] {-0.850651, 0, -0.525731}, // vertices[3] {-0.850651, 0, 0.525731}, // vertices[4] {-0.525731, 0.850651, 0}, // vertices[5] {0.525731, 0.850651, 0}, // vertices[6] {0.525731, -0.850651, 0}, // vertices[7] {-0.525731, -0.850651, 0}, // vertices[8] {0, -0.525731, -0.850651}, // vertices[9] {0, 0.525731, -0.850651}, // vertices[10] {0, 0.525731, 0.850651} // vertices[11] }; static const Color3D colors[] = { {1.0, 0.0, 0.0, 1.0}, {1.0, 0.5, 0.0, 1.0}, {1.0, 1.0, 0.0, 1.0}, {0.5, 1.0, 0.0, 1.0}, {0.0, 1.0, 0.0, 1.0}, {0.0, 1.0, 0.5, 1.0}, {0.0, 1.0, 1.0, 1.0}, {0.0, 0.5, 1.0, 1.0}, {0.0, 0.0, 1.0, 1.0}, {0.5, 0.0, 1.0, 1.0}, {1.0, 0.0, 1.0, 1.0}, {1.0, 0.0, 0.5, 1.0} }; static const GLubyte icosahedronFaces[] = { 1, 2, 6, 1, 7, 2, 3, 4, 5, 4, 3, 8, 6, 5, 11, 5, 6, 10, 9, 10, 2, 10, 9, 3, 7, 8, 9, 8, 7, 0, 11, 0, 1, 0, 11, 4, 6, 2, 10, 1, 6, 11, 3, 5, 10, 5, 4, 11, 2, 7, 9, 7, 1, 0, 3, 9, 8, 4, 8, 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); glVertexPointer(3, GL_FLOAT, 0, vertices); glColorPointer(4, GL_FLOAT, 0, colors); for (int i = 1; i <= 30; i++) { glLoadIdentity(); glTranslatef(0.0f,-1.5,-3.0f * (GLfloat)i); glRotatef(rot, 1.0, 1.0, 1.0); glDrawElements(GL_TRIANGLES, 60, GL_UNSIGNED_BYTE, icosahedronFaces); } glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); static NSTimeInterval lastDrawTime; if (lastDrawTime) { NSTimeInterval timeSinceLastDraw = [NSDate timeIntervalSinceReferenceDate] - lastDrawTime; rot+=50 * timeSinceLastDraw; } lastDrawTime = [NSDate timeIntervalSinceReferenceDate]; }
如果你把上述代码加入OpenGL Xcode项目模板 项目中(它使用glFrustumf()设置了一个具有45°视野的透视视口),你将看到下面图形:
很好。随着几何体远离你,它们会变得越来越小,正像火车铁轨一样。
如果你只是将 glFrustumf()改为 glOrthof(),看上去就完全不同了:
没有透视,第一个二十面体后面的二十九个二十面体完全被第一个挡住了。因为没有透视,后面的各几何体的形状完全取决于其前方的物体。
好了,这是一个很沉闷的主题,事实上你现在可以完全忘却三角课学的知识了。只要复制基于视野角度计算锥台的两行代码就好,而且你可能再也不需要记住它的原理了。
继续下一次激动人心的冒险旅程…下一篇文章,我们将为二十面体增加光效,使它看上去更真实。
Himi 原创, 转载请注明出处,谢谢!
原文地址:http://blog.csdn.net/xiaominghimi/article/details/6913967
这里Himi给出对于开发iOS的朋友们整理一个指南集合,其中主要包括申请IDP需要注意的地方、有了开发者证书如何真机调试、在自己的游戏应用中如何接入GameCenter以及如何在游戏接入OpenFeint;
-----------申请企业级IDP,或者个人IDP
通过Himi的申请经验,直接打苹果在中国的客服,按照步骤一步一步详细的让客服进行指导,可能很多童鞋说我这句跟没说一样,呵呵,如果真的你是第一次申请IDP那么如果你不打客服,N多细节都会造成你1~15天耐心等待,Himi申请过程中由于一个名称和一个勾选错误整整耽误一个月的时间;最后仍是不停的跟客服交涉终于Ok顺利申请到;
这里Himi给出苹果在中国的电话:400---670---1855 (建议拨打之前大致的先百度google下申请IDP的流程,网上一大堆,这里Himi不赘述了)
---------------申请到IDP后如何真机调试
1.制作证书的过程Himi这里不多赘述,百度、google下N多文章呢;制作证书连接(前提是申请IDP成功):http://developer.apple.com/membercenter/index.action
2.正确制作证书后,有个这样的文件:
双击此文件,弹出Organizer-Devices界面,连接你的真机iphone、ipad或者touch,然后左侧可以看到如下图:
右侧的绿色小灯表示可以正常使用,这个小灯如果是黄色,那就说明你的证书有问题,可能是此手机的UDID没有在证书内等原因;
3.确保真机正常后,点击你的项目,右侧点击PROJECT点击Build Settings页面,然后Code Signing下设置Code Signing Identity为你的证书,如下图:
4.点击你的项目,右侧点击TARGETS,点击Info页面下的设置Bundle identifier,这个Bundle identifier在你制作证书的过程中就会了解到,如下图:
OK,可以编译运行你的项目到真机中了;
---------------游戏接入GameCenter 指南
1. iTunes Connect 设置
首先,申请一个应用程序,不必提交.目地是为了得到Bundle ID.
然后设置一下工程中Info.plist的Bundle identifier使之与iTunes Connect中的Bundle ID相同,否则当你尝试登录GameCenter的时候,会提示一个不支持GameCenter的错误.
申请完毕,打开你刚申请的application,点击Manage Game Center选项.
进入后点击Enable Game Center使你的Game Center生效.
接下来就可以设置自己的Leaderboard和Achievements.
2. Leaderboard设置
Leaderboard纵观图如下所示.
1.sort Order: Leaderboard中的内容是以升序还是降序排列.
2.Score Format Type:分数的类型.
3.*Categories:Leaderboard的一个分数榜,这个可以创建多个,比如游戏可以分为Easy,Normal,Hard三个难度,每个难度一个榜.
*设置完成后保存,完成了一个 Leaderboard的设置.我们可以根据需要添加多个 leaderboard.
4.**Score Format Location: leaderboard支持的语言.
**可以支持多种语言,每支持一种语言,需要完成一个上述操作.
这个时候右下角会出现save change按钮,点击完成leaderboard的设置.
你可以根据需要随时更改你的leaderboard,操作与上述内容类似.
3. Achievements设置
Achievements界面内容比较少,点击左上角的Add New Achievement,打开如下图所示的Achievements创建界面.
Hidden:表示该成就为解锁前玩家是否可见.
Achievement ID:程序通过这个属性来识别成就.
*Achievement Localization:该成就支持的语言.
*Achievement Localization设置如下图所示.
*其中,成就的Image必须是512X512,72DPI的.
一切设置完成后,点击save change按钮即完成一个成就的设置.
4.总体功能
在使用各个功能前,你需要了解一下块函数。传送门: https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Blocks/Articles/00_Introduction.html
4.1 对Game Center支持判断
4.2用户登录
对于开发者来说,Game Center必须经过测试才能上线,没有上线的程序在测试环境中登录时会出现sandbox提示.如图.
4.3用户变更检测
由于4.0以后系统支持多任务,玩家的机器有可能被不同的玩家接触,导致Game Center上的用户发生变化,当发生变化的时候,程序必须在游戏中通知玩家.
5.对Leaderboard进行操作
5.1上传一个分数
当上传分数出错的时候,要将上传的分数存储起来,比如将SKScore存入一个NSArray中.等可以上传的时候再次尝试.
5.2下载一个分数
说明:
1) playerScope:表示检索玩家分数范围.
2) timeScope:表示某一段时间内的分数
3) range:表示分数排名的范围
4) category:表示你的Leaderboard的ID.
5.3玩家信息交互
Game Center最重要的一个功能就是玩家交互.所以,必须检索已经登录玩家的好友信息.根据自己的需要做出设置,比如,可以与好友比较分数,或者好友排行榜等.
//检索已登录用户好友列表
上面的friends得到的只是一个身份列表,里面存储的是NSString,想要转换成好友ID,必须调用- (void) loadPlayerData: (NSArray *) identifiers方法,该方法得到的array里面存储的才是GKPlayer对象.如下
至此,leaderboard功能介绍完毕
6.对Achievement进行操作
这一部分内容比较多,而且有的地方有点重复的感觉.
6.1汇报一个成就的进度
对于一个玩家可见的成就,你需要尽可能的报告给玩家解锁的进度;对于一个一部完成的成就,则不需要,当玩家的进度达到100%的时候,会自动解锁该成就.
其中该函数的参数中identifier是你成就的ID, percent是该成就完成的百分比
6.2读取一个成就
方法一:得到所有的成就
函数中NSArray返回的是你的所有成就ID.
方法二:根据ID获取成就
6.3获取成就描述和图片
在自定义界面中,玩家需要一个成就描述,以及该成就的图片,Game Center提供了该功能.当然,你也可以自己在程序中完成,毕竟玩家不可能时刻处于在线状态.
如果你不主动使用注释中的方法,那么你得到的description中不会有图片,这样可以减少网络的使用,尽量少下载东西.当使用注释中的代码时,如果成就已经解锁,则返回该成就的图标,如果没有解锁,则返回一个大问号,至于未解锁图标是否可以自定义,我找寻的结果好像是不可以.
---------------游戏接入OpenFeint指南
OpenFeint 是很多iPhone游戏开发者都要用到的社区功能;
一、openfeint中的LeaderBoards 和Achievement的一点体会
1.数据提交的格式
最近想向自己 的游戏中添加点openfeint功能,使用的时候发现,openfeint的功能虽然比较多,也比较强大,但是,有些地方还是不太如人意。我游戏中的分 数有一项是float型的数据,可是当我提交的时候,发现openfeint的在线排名只支持整数形式的数据,改变了官方的api提交之后,服务器那边仍 自动转换成了整型的数据。我在论坛上求证了一下,虽然没有结论,但我认为openfeint高分排行榜仅支持整型的数据。
2.数据提交的方法
[OFHighScoreService setHighScore:你提交的分数 forLeaderboard:@"分数项的ID" onSuccess:OFDelegate() onFailure:OFDelegate()]; //提交高分,如果函数无效,请引入#import "OFHighScoreService.h"
通过上面那个函数,就可以向服务器提 交数据,其中你要提交的分数,无论是什么类型,最后都会转换成整型的数据,可以参见上一条信息。而分数项ID,则是你在申请LaderBoards的时候 openfeint分配给你的一个数字。后面两个参数,应该不需要改变,我没有尝试过做其他的动作,有兴趣和想法的朋友,可以尝试象@selector那 样使用它。
[OFAchievementService unlockAchievement: @"成就ID" onSuccess: OFDelegate() onFailure: OFDelegate()];//解锁成就,如果函数无效,请引入#import "OFAchievementService.h"
这个函数的功能是解锁成就,当你在游戏过程中达到某一个要求时,就可以解锁你在openfeint上预设的成就。
例如:
生存游戏中:if (生存时间 > 100s ) { 调用上面的函数解锁你自己预设的成就; }
jump游戏中:if ( 高度 > 10000m ) { 调用上面的函数解锁你自己预设的成就; }
3.网络对分数提交的影响
网络畅通的情况下,调用上述函数提交分数(最高分数被刷新时) 可以成功,并且解锁成就并不会反复出现解锁提示。好吧,既然这个可以完成我们的要求,那么这里就不是重点。
网络不通的情况下,就会出现一点问题:
在阐明问 题之前,我想先说一下我对openfeint的数据存储的理解或者说感觉。使用penfeint的时候,在documents目录下会生成两个文件,一个 FakeKeyChain.plist,据我观察,这里面存放的就是我们在openfeint里为这个游戏申请的Product Key和Product Secret,而且Secret经过了加密处理。另一个文件则是feint-offline, 这个文件是无法打开的,在windows用记事本打开也是一堆乱码,也许有其他的办法,不过我没有找到。我对于这个文件的用途猜测是,这个文件用来存档玩 家的一些信息,比如玩家名和分数等,这个文件我暂时叫它为“本地隐 藏信息表”吧。
问题来鸟,在没有网络的情况下,取得了一个分数,然后第一次调用分数提交函数,会提示你得 到了一个高分,存储在本地(我感觉就在本地隐藏信息表中), 问题出现了!当你这时连接网络,分数并不会自动提交,而你自己手动提交(比如点击一个按钮,按钮的功能是提交最高分数)也没有任何的效果。
而 在官方文档中有这样一Q&A:
Q:what happens to a high score when a player is offline?
A:as os openfeint 2.1 high scoreare queued for submission when the player is offline and submitted when next he's online again.
Q:if a user says no to using openfeint the first time,is there a way that user can change his or her mind to allow openfeint in the future?
A:when you deny openfeint it will prompt you to approve/deny again when you open the dashboard([OpenFeint launchDashboard]).it will not prompt you on the next app bootup,or submitting any requests.only when you open the dashboard.
也就是说,提交失败,于是我做了个试验,在有网络的情况下,提交一个分数100,只显示 一次,第二次提交100时,没有提示。然后提交101,有提示,第二次提交101,没有提示。说明了本地隐藏信息表中还存储了一个最高分数的提交次数和提交许可,使用一次提交分数的函数,这些内 容就会改变,只有新提交的分数比原来存储的分数大时,本地隐藏信息 表才会允许你向openfeint正式提交,否则,无效,感觉上和retain与release有点像。也就时说,最高的分数在提交的时 候,没有网络,就等于失败,这里应该算是openfeint的一个小bug吧。也是我遇到问题的所在,没找到什么解决办法,大伙有经验的可以提出来。
用 个简单的图来形容下吧。
无网络->得到新高分->存储在本地->联网后->不自动上传最高分。
顺 便说下成就的提交,没有网络,不可解锁成就,也没有存在本地的提示,联网后,同样也不自动解锁,只有再一次达到条件(方才例子中的if成立)时,才会再次 解锁。
以上,是我的部分openfeint基础使用的经验,也许是我的方法不正确,也许有别的解决办法,我能提供给大家的帮助, 先这么多了。
二、 openfeint的设置(2.4.8版)
以下步骤是假设你从没安装过openfeint,如果有,请将以 前老版本的openfeint从机器中删除,并从project中删除所有与openfeint有关的东西,然后,你可以按下面的步骤来做了。
1. 从官网下载一个最新版本的没有解压的openfeint SDK。
2.将openfeint文件夹拖入你的project中。
3. 设置info
a.打开project的info,选中build栏,将configuration设置成All configuration
b.将Other Linker Flags一项的值设置成 -Objc 区分大小写
c.将Call C++ Default Ctors/Dtors in Objective-c项的选成yes(这一步我没有设置,不知道是什么意思,英文原文如下:Ensure 'Call C++ Default Ctors/Dtors in Objective-c' is checked under the 'GCC 4.2 - Code Generation' section)
d.设置一个默认的值GCC_OBJC_CALL_CXX_CDTORS 为 YES(这一步我也没有设置)
4.引入frameworks
需要引入的frameworks 有,Foundation,UIKit,CoreGraphics,QuartCore,Security,SystemConfiguration,libsql3.0 dylie,libz.1.2.3.dylib这些是官方给出需要引入的frameworks,根据帮我搭建工程的前辈说,必须要引入 CoreLocation.framework CFNetwork.framework MapKit.framework
5.必须在你的 .pch 文件中引入#import “OpenFeintPrefix.pch”
6.将你所有使用openfeint功能的函数改为 .mm 文件
我能想到的就这些了,还有什么问题,大家可以互相讨论。
三、openfeint的注册
openfeint的注册并不难,能看懂 文档的水平基本就可以了,也可以配合翻译软件来弄。
1.打开官网 http://www.openfeint.com
2.选右上角的 Developers一项,跳转到的新界面。
3.点击本页面的右上角的login会进入登陆界面,选择右上角的 sign up进行一个简单注册,本页右下角有一个教学的视频,告诉你如何使用openfeint的基础功能。
4.简单注册界面,填写完成后跳转到一个新的界面,点击Dive in 进入你自己的openfeint里。
5.进入自己的openfeint了,需要进行一个prepare for submission的申请,这个可以让你的openfeint有效,否则,你只能使用test user 进行测试。在App Home中,可以看到自己的client Id 这个是用来提问用的,以及最重要的Product Key和Product Secret,这两项是用来识别你的程序独有的openfeint的。
6.还需要一个你注册时使用的邮箱认证。进入自己的邮箱就能看到 了。
7.想通过openfeint的审核,还需要完善一个ipurchase的填写,在basic features/iPurchase里面填写,*项必须要有内容。
8.完成上面这些,你就可以设置自己的LaderBoards和 Achievement了,还有更多的challenge等。