为iPhone建立一个OpenGL ES的Xcode项目是很容易的事,特别是在苹果公司的SDK发布时引入了模板的概念后。我们所需要做的只是在适当的地方快速而简单的加入代码。这就是我们今天的主题。
老实说,如果你只是想早点切入OpenGL 教程的主题,你尽可以忽略这一部分。而且这样做,你也不会错失什么,因为有关Xcode设定部分我并不准备过多涉猎。尽管进入教程的最后部分下载项目文件。
启动Xcode并创建Xcode新项目。选择模板“OpenGL ES Application”(见图1),然后将项目存储在你认为方便的地方。
图1
好,我们假定你肯能以前已经在哪看过这个模板或运行过用这个模板建立的项目。我们说要做的就是移除那个旋转的彩色正方形的有关代码并将其转换成使用深度缓冲的视图(即使其成为“真正的”3D)。
3D空间的2D
像Apple的模板一样,大部分OpenGL教程开始都忽略深度,通常都使用2维坐标系统(X,Y)而不是更复杂的3维坐标系统(X,Y,Z)。你可能已经注意到Apple模板中的正方形定点使用(X,Y),这是因为他们没有使用深度。
这叫做正交投影。本教程系列的目标是带你进入3D世界。所以我现在不会讨论正交投影;可能稍后的系列会有提及。现在我们直接向3D世界进发吧。
允许Depth Buffer(深度缓存)
我们要作的第一件事是允许深度缓存。Apple示例中的正方形只是一个2D物体,所以根本不需要深度缓存。由于我们需要深度,所以我们必须启用它。Apple已经提供了设定深度缓存的代码,我们正好可以使用。
在编辑器中打开EAGLView.m找到下面代码:
1
|
#define USE_DEPTH_BUFFER 0
|
不用多说,把0改为1。这将启用视图设定部分创建深度缓存的代码。代码在createFrameBuffer方法中。现在还不需要考虑太多这段代码。它是Apple写的,应该能正常工作。
现在我们需要启用深度来测试OpenGL。我们要建立一个新方法,它只会被调用一次以使视图正常工作。首先,建立一个新方法setupView并将下面代码加入新方法中:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
-
(
void
)setupView
{
const GLfloat zNear = 0.1, zFar = 1000.0, fieldOfView = 60.0; GLfloat size; glEnable (GL_DEPTH_TEST ); glMatrixMode (GL_PROJECTION ); size = zNear * tanf (DEGREES_TO_RADIANS (fieldOfView ) / 2.0 ); // This give us the size of the iPhone display CGRect rect = self.bounds; glFrustumf ( -size, size, -size / (rect.size.width / rect.size.height ), size / (rect.size.width / rect.size.height ), zNear, zFar ); glViewport (0, 0, rect.size.width, rect.size.height ); glClearColor (0.0f, 0.0f, 0.0f, 1.0f ); } |
上述代码通过建立一个映射到世界显示的视区设定了OpenGL操作的环境。我将稍后详细说明,现在注意下面代码:
1
|
glEnable
(GL_DEPTH_TEST
);
|
此代码启用了OpenGL中的深度测试。你需要记住在OpenGL中一旦你启用了什么,你一定要记住在你不需要时要关闭它。由于我们启用深度测试并没有关闭,所以在视图设定中仍然有效。
1
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
我们在此定义了清屏时所用的颜色。OpenGLES中所有颜色都必须是RGBA值(即红,绿,蓝和alpha),而不能像OpenGL一样采用RGB值。由于OpenGLES已经知道清屏时采用的颜色,所以清屏时使用定义的黑色。此设定一直保持到我们改变它。
当使用浮点时,颜色值为0->1,而使用无符号字节类型时,可为0->255。数值越大,所代表颜色的密度越高。
好,回到文件最开始我们先前改变的#define处。我们要在这定义一个宏,在我们刚插入的setupView方法中需要用到。
1
2 3 |
#define USE_DEPTH_BUFFER 1
#define DEGREES_TO_RADIANS(__ANGLE) ((__ANGLE) / 180.0 * M_PI) |
视图绘制 – 画图方法
现在,找到drawView方法。这是本教程的重点。Apple已经提示我们这里是视图绘制的地方。
首先,删除一切,加入下面代码:
1
2 3 4 5 6 7 8 |
-
(
void
)drawView
{
[EAGLContext setCurrentContext :context ]; glBindFramebufferOES (GL_FRAMEBUFFER_OES, viewFramebuffer ); glViewport (0, 0, backingWidth, backingHeight ); glBindRenderbufferOES (GL_RENDERBUFFER_OES, viewRenderbuffer ); [context presentRenderbuffer :GL_RENDERBUFFER_OES ]; } |
上面代码的前三行是设定作图空间。后两行是将作图空间与当前屏幕可见空间进行交换。如果你以前进行过动画或游戏编程工作,那么你应该知道这叫做 “double buffered(双重缓冲)”。
如果不知道,我们解释下:我们创建了两个完全相同的缓冲,一个用于显示给用户,另一个被清理掉然后在其上绘图。在绘制结束后,我们交换这两个视图以便用户看到新的视图。我们之所以不拍麻烦做了这么多事情是为了动画更为平滑。
我们还要加入更多的代码。
首先,在dealloc方法前插入以下代码:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
-
(
void
)checkGLError
:
(
BOOL
)visibleCheck
{
GLenum error = glGetError ( ); switch (error ) { case GL_INVALID_ENUM : NSLog ( @ "GL Error: Enum argument is out of range" ); break; case GL_INVALID_VALUE : NSLog ( @ "GL Error: Numeric value is out of range" ); break; case GL_INVALID_OPERATION : NSLog ( @ "GL Error: Operation illegal in current state" ); break; case GL_STACK_OVERFLOW : NSLog ( @ "GL Error: Command would cause a stack overflow" ); break; case GL_STACK_UNDERFLOW : NSLog ( @ "GL Error: Command would cause a stack underflow" ); break; case GL_OUT_OF_MEMORY : NSLog ( @ "GL Error: Not enough memory to execute command" ); break; case GL_NO_ERROR : if (visibleCheck ) { NSLog ( @ "No GL Error" ); } break; default : NSLog ( @ "Unknown GL Error" ); break; } |
OpenGL有一套错误检查的方法(glGetError()),但是需要将错误码转换成更容易读的格式。上面的代码就是完成这个工作的。
布尔值“visibleCheck”只用于检查当没有错误时此方法是否已被调用。
最后,我们要做的只是在EAGLView.m中找到initWithCoder方法并调用“setupView”方法。在变量“animationInterval”设定之前,加入下面代码调用setupView:
1
|
[self setupView
];
|
应注意我们只是调用在initWithCoder中setupView方法而不是创建一个新方法,它只应该被调用一次。
下面,打开EAGLView.h!
EAGLView.h
我们只需要在这里加入两个方法的原型:
1
2 3 |
-
(
void
)setupView;
- ( void )checkGLError : ( BOOL )visibleCheck; |
教程到此结束。
后续…
如果你此时按“build and run”,那么在simulator(模拟器)上只是显示一个空白屏幕。在下一个教程中,我们将在屏幕上绘制一下基本图形,基本图形是指点,线和三角形之类的元素。
代码下载:AppleCoder-OpenGLES-00.zip