iOS-OpenGL-ES入门教程(二)最简单的纹理Demo

前言

上一篇介绍了绘制OpenGL ES的Hello world也就是一个三角形。现在我们介绍下OpenGL ES的一个另一个基础,纹理,并且编写一个最简单的demo绘制一张图片。

纹理

初学呢,我们可以把纹理理解为一张图片,我们可以将整张图片绘制到圆形,矩形等目标图形中,既可以绘制部分图,也可以重复使用图片绘制,也就是纹理了哈。

矩形图

我们目标是绘制一个矩形图,在OpenGL ES中任何复杂的图形都是由点,线和三角形组成的哈。很简单,一个矩形就是两个三角形组成。嚯嚯。So easy。

矩形

好滴,那么矩形的六个顶点有了哈

1
2
3
4
5
6
7
8
9
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
};

很明显我们的顶点是需要和纹理坐标一一对应的,如下图,
顶点和纹理映射图

需要注意的是:纹理坐标系是左下角为坐标系顶点,而顶点坐标系屏幕中心为顶点

好滴,那么我们的数据源就有了哈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//顶点数据
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上下文

1
2
3
4
5
6
7
8
9
//新建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

1
2
3
4
5
6
7
8
9
10
11
12
- (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方便的生成图片纹理。

1
2
3
4
5
6
7
8
//获取图片
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。

绘制 & 释放

最后一步就是绘制了,这步非常简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (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了。

1
2
NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys:@(1), GLKTextureLoaderOriginBottomLeft, nil];
GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithCGImage:imageRef options:options error:NULL];

小思考: 我们这里实现了最简单的纹理图片绘制,但是由于图标本身的尺寸被全屏展示后出现了拉伸,怎么保证图片按比例的绘制在屏幕中呢?

Demo代码地址:LearnOpenGLESDemo

参考书籍:1. OpenGL ES应用开发实践指南:iOS卷