前言
上一篇介绍了绘制OpenGL ES的Hello world也就是一个三角形。现在我们介绍下OpenGL ES的一个另一个基础,纹理,并且编写一个最简单的demo绘制一张图片。
纹理
初学呢,我们可以把纹理理解为一张图片,我们可以将整张图片绘制到圆形,矩形等目标图形中,既可以绘制部分图,也可以重复使用图片绘制,也就是纹理了哈。
矩形图
我们目标是绘制一个矩形图,在OpenGL ES中任何复杂的图形都是由点,线和三角形组成的哈。很简单,一个矩形就是两个三角形组成。嚯嚯。So easy。
好滴,那么矩形的六个顶点有了哈
const GLfloat vertices[] = {
1, -1, 0.0f, //D
1, 1, 0.0f, //B
-1, 1, 0.0f, //A
1, -1, 0.0f, //D
-1, 1, 0.0f, //A
-1, -1, 0.0f, //C
};
很明显我们的顶点是需要和纹理坐标一一对应的,如下图,
需要注意的是:纹理坐标系是左下角为坐标系顶点,而顶点坐标系屏幕中心为顶点
好滴,那么我们的数据源就有了哈。
//顶点数据
typedef struct {
GLKVector3 positionCoords;
GLKVector2 textureCoords;//纹理
}SceneVertex;
//矩形的六个顶点
static const SceneVertex vertices[] = {
{{1, -1, 0.0f,},{1.0f,0.0f}}, //右下
{{1, 1, 0.0f},{1.0f,1.0f}}, //右上
{{-1, 1, 0.0f},{0.0f,1.0f}}, //左上
{{1, -1, 0.0f},{1.0f,0.0f}}, //右下
{{-1, 1, 0.0f},{0.0f,1.0f}}, //左上
{{-1, -1, 0.0f},{0.0f,0.0f}}, //左下
};
textureCoords即是纹理位置。
设置OpenGLES上下文
//新建OpenGLES 上下文
GLKView *view = (GLKView *)self.view;
view.context = [[EAGLContext alloc]initWithAPI: kEAGLRenderingAPIOpenGLES2];
//设置当前上下文
[EAGLContext setCurrentContext:view.context];
self.baseEffect = [[GLKBaseEffect alloc]init];
self.baseEffect.useConstantColor = GL_TRUE;
self.baseEffect.constantColor = GLKVector4Make(1.0f, 1.0f, 1.0f, 1.0f);
这是第一步。
设置顶点缓存buffer
- (void)fillVertexArray{
glGenBuffers(1, &vertextBufferID);
glBindBuffer(GL_ARRAY_BUFFER, vertextBufferID); //绑定指定标识符的缓存为当前缓存
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribPosition); //顶点数据缓存
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(SceneVertex), NULL + offsetof(SceneVertex, positionCoords));
glEnableVertexAttribArray(GLKVertexAttribTexCoord0); //纹理
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(SceneVertex), NULL + offsetof(SceneVertex, textureCoords));
}
这里我们生成绑定了顶点和纹理buffer,并且设置了对应的指针偏移量。
生成纹理
这里我们使用GLkit中的GLKTextureInfo方便的生成图片纹理。
//获取图片
CGImageRef imageRef = [[UIImage imageNamed:@"Demo.jpg"] CGImage];
//通过图片数据产生纹理缓存
//GLKTextureInfo封装了纹理缓存的信息,包括是否包含MIP贴图
GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithCGImage:imageRef options:nil error:NULL];
self.baseEffect.texture2d0.name = textureInfo.name;
self.baseEffect.texture2d0.target = textureInfo.target;
GLKBaseEffect让我们避开了写shader Language。
绘制 & 释放
最后一步就是绘制了,这步非常简单
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
//清除背景色
glClearColor(0.0f,0.0f,0.0f,1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
[self.baseEffect prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, 6);
}
- (void)dealloc{
GLKView *view = (GLKView *)self.view;
[EAGLContext setCurrentContext:view.context];
if ( 0 != vertextBufferID) {
glDeleteBuffers(1,
&vertextBufferID);
vertextBufferID = 0;
}
[EAGLContext setCurrentContext:nil];
}
小思考:因为这次绘制的是静态图,所以我们直接在viewdidload里面就生成绑定好了顶点数据缓存,而不是在drawInRect方法中。如果要做一些动态变化,就需要在drawInRect方法中动态刷新缓存数据
看下最后运行结果
图片正常显示了,但是倒立了,这个是因为CoreGraphics的坐标系问题。我们生成纹理的时候option加个坐标系变换就OK了。
NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys:@(1), GLKTextureLoaderOriginBottomLeft, nil];
GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithCGImage:imageRef options:options error:NULL];
小思考: 我们这里实现了最简单的纹理图片绘制,但是由于图标本身的尺寸被全屏展示后出现了拉伸,怎么保证图片按比例的绘制在屏幕中呢?
Demo代码地址:LearnOpenGLESDemo
参考书籍:1. OpenGL ES应用开发实践指南:iOS卷