3.2、OpenGLES_Ch3_1示例
纹理涉及很多潜在的复杂选项,但是苹果的GLKit极大地简化了常见纹理的配置。本例是基于第2章的OpenGLES_Ch2_3示例建立的。在OpenGLES_Ch3_1中的一个单独的文件,OpenGLES_Ch3_1ViewController 类的实现文件,包含了对OpenGLES_Ch2_3的所有修改代码。下面的代码块用粗体标识了变化部分。首先,纹理坐标被加人到SceneVertex类型的声明中:
// This data type is used to store information for each vertex
typedef struct {
GLKVector3 positionCoords;
GLKVector2 textureCoords;
}
SceneVertex;
紋理坐标定义了几何圏形中的毎个頂点的紋理映射。为例子保存頂点数据的vertices数組初始化了紋理坐示和位置坐标。
/////////////////////////////////////////////
// Define vertex data for a triangle to use in example
static const SceneVertex vertices[] =
{
{{-0.5f, -0.5f, 0.0f}, {0.0f, 0.0f}},// lower left corner
{{ 0.5f, -0.5f, 0.0f}, {1.Of, 0.Of}}, // lower right corner
{{-0.5f,0.5f, 0.of}, {0.of, 1.0f}}, // upper left corner
};
“-viewDidLoad"方法扩展了示例OpenGLES_Ch2_3中的代碣来影呵GLKit提供的GLKTextureLoader炎,这个类用于將一个紋理圏像加載到一个OpenGL ES紋理緩存中。
////////////////////////////////////////////
// called when the view controller's view is loaded
// Perform initialization before the view is asked to draw
- (void)viewDidload
{
[super viewDidload];
// Verify the type of view created automatically by the
//1 Interface Builder storyboard
GLKview *view = (GLKView *)self.view;
NSAssert( [view isKindOfClass:[GLKView class],@"view controller's view is not a GLKView");
// Create an OpenGL Es 2.0 context and provide it to the
// view
view.context = [[AGLKContext alloc] initWithAPI:kBAGLRenderingAPIOpenGLES2];
// Make the new context current
[AGLKContext setCurrentContext:view.context];
// Create a base effect that provides standard OpenGL ES 2.0
// shading language programs and set constants to be used for
// all subsequent rendering
self.baseBffect = [[GLKBaseEffect alloc] init];
self.baseBffect.useConstantColor = GL_TRUB;
self.baseEffect.constantColor = GLKVector4Make(
1.0f,// Red ;
1.0f,// Green
1.0f, // Blue
1.0f);// Alpha
// Set the background color stored in the current context
((AGLKContext * )view.context).clearcolor = GLKVector4Make(
0.0f, // Red
0.0f, // Green
0.0f, // Blue
1.0f);// Alpha
// Create vertex buffer containing vertices to draw
self.vertexBuffer = [[AGLKVertexAttribArrayBuffer alloc]
initWithAttribStride:sizeof(SceneVertex)
number0fVertices:sizeof(vertices) /sizeof(SceneVertex)
data:vertices
usage:GL_STATIC_DRAW];
// Setup texture
CGImageRef imageRef = [[UIImage imageNamed:e"leaves.gif"] CGImage];
GLKTextureInfo *textureInfo = [GLKTextureLoader
textureWithCGImage:imageRef
options:ni1
error: NULL] ;
self.baseEffect.texture2d0.name = textureInfo.name;
self.baseEffect.texture2d0.target = textureInfo.target;
}
CGImageRef是一个在苹果的Core Graphics框架中定义的C数据类型。Core Graphics包含很多强大的2D图像处理和绘制函数。UlImage的“+imageNamed:”方法会返回一个初始化自图形文件的Ullmage实例。很多不同的图像文件格式都支持。命名的图像必须被包含为应用的一部分, 以便“+imageNamed:" 可以找到它。
GLKTextureL oader的“-textureWithCGImage:options:error:" 方法会接受个CGImageRef并创建一个新的包 含CGlmageRef的像素数据的OpenGL ES纹理缓存。这个方法看似强大。接受一个CGlmageRef使得图像数据的源可以是任何Core Graphics支持的形式,从一个电影的单个帧到由一个应用绘制的自定义2D图像,再到一个图像文件的内容。“ options:" 参数接受一个存储了用于指定GLKTextureLoader怎么解析加载的图像数据的键值对的NSDictionary。可用选项之一是指示 GLKTextureLoader为加载的图像生成MIP贴图。
GLKTextureLoader会自动调用glTexParameteriO方法来为创建的纹理缓存设置OpenGL ES取样和循环模式。如果使用了MIP贴图,并且GLTEXTURE_MIN_FILTER被设置成GL_LINEAR_MIPMAP_LINEAR,这会告诉OpenGL ES使用与
被取样的S、T坐标最接近的纹素的线性插值取样两个最合适的MIP贴图图像尺寸(细节级别)。然后,来自MIP贴图的两个样本被线性差值来产生最终的片元颜色。GL_LINEAR_MIPMAP_LINEAR
过滤器通常会产生高质量的渲染输出,但是会比其他模式需要更多的GPU计算。如果没有使用MIP贴图,GLKTextureLoader会自动设置GL_TEXTURE_MIN_FILTER
为GL LINEAR。GL_TEXTURE_WRAP_S和GL_TEXTURE_WRAP_T都会被设置为GL_CLAMP_TO_EDGE。
第2章例子中引人的GLKBaseEffect对象提供了对于使用纹理做渲染的内建的支持。在OpenGL ES纹理缓存被创建以后,这个例子设置baseEffect的texture2d0属性使用一个新的纹理缓存。
GLKTextureInfo类封装了与刚创建的纹理缓存相关的信息,包括它的尺寸以及它是否包含MIP贴图,但是这个例子只需要缓存的OpenGLES标识符、名字和用于纹理的OpenGL ES目标。OpenGL ES上下文会为各种缓存分别保存配置信息。帧缓存会被单独从顶点属性数组缓存或者纹理缓存配置。事实上, OpenGL ES的上下文支持多种纹理缓存。例如,一种纹理缓存会包含普通的2D图像数据,然后另一种会保存一个用于特殊效果的特别形状的图像,这些内容会在第8章中介绍。GLKTextureInfo 的target属性指定被配置的纹理缓存的类型。一些OpenGL ES实现还会为1D和3D纹理保持独立 的纹理缓存目标。
// GLKView delegate method: Called by the view controller's view
// whenever cocoa Touch asks the view controller's view to
// draw itself. (In this case, render into a frame buffer that
// shares memory with a Core Animation Layer)
- (void)glkView:(GLKView * )view drawInRect:(CGRect)rect {
[self.baseEffect prepareToDraw];
// Clear back frame buffer ( erase previous drawing )
[(AGLKContext *)view.context clear:GL_COLOR_BUFFER_BIT];
[self.vertexBuffer prepareToDrawWithAttrib:GLKVertexAttribPosition
numberOfCoordinates:3
attriboffset:offsetof(SceneVertex, positionCoords )
shouldEnable:YES];
[se1f.vertexBuffer prepareToDrawwithAttrib:GLKVertexAttribTexCoordo
numberOfCoordinates:2
attriboffset:offsetof(SceneVertex, textureCoords)
shouldEnable:YES];
// Draw triangles using the first three vertices in the
// currently bound vertex buffer
[self.vertexBuffer
drawArraywithMode:GL_TRIANGLES
startVertexIndex:0
numberOfVertices:3];
}
在OpenGLES_Ch2_3例子中创建并在这里重用的AGLKVertexAttribArrayBuffer类隐式地支持每个顶点的属性的任意组合。OpenGLES_Ch3_1ViewController 的 “-glkView:drawInRect:” 方法的实现首先告诉vertexBuffer让OpenGL ES为渲染顶点位置做好准备(就像例子OpenGLES_Ch2_3),然后添加第二个对于“-prepareToDraw-WithAttrib:numberOfCoordinatesattribOffset:shouldEnable:”方法的调用。第二个调用告诉vertexBuffer让OpenGLES为每个顶点的两个纹理坐标的渲染做好准备。编译器会用纹理坐标开始的每个SceneVertex结构体内的内存偏移来代替ANSIC的offsetof()宏。
在一个纹理被赋给baseEffect,同时OpenGLES为使用位置和纹理坐标属性做好准备之后,调用AGLKVertexAttribArrayBuffer的“-draw ArrayWithMode:startVertexIndex: numberOfVertices:”方法来指示OpenGL ES去渲染有纹理的三角形。
例子OpenGLES_Ch3_1 对于“-viewDidUnload”方法的实现与例子OpenGLES_Ch2_3的实现保持不变。