OpenGL ES iOS笔记02 使用纹理

上一篇笔记

纹理是OpenGL很重要的一部分,处理视频图像,基本就是在使用纹理做各种处理,本文会实现将一张图片加载到纹理中,再由OpenGL渲染到屏幕上。

加载与创建纹理

一般的图片格式都是压缩的,使用UIImage加载后,需要转换成位图(bitmap),一般使用RGBA格式,本次使用的图片带有alpha通道,如果使用不带alpha通道的图片,转换位图的代码也需要相应作改动。

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
-(void)render {
	 //加载图片并转换为rgba,存放到imageData中
    NSString*imagePath = [[NSBundle mainBundle]pathForResource:@"container" ofType:@"png"];
    UIImage*image = [UIImage imageWithContentsOfFile:imagePath];
    CGImageRef cgImageRef = [image CGImage];
    GLuint width = (GLuint)CGImageGetWidth(cgImageRef);
    GLuint height = (GLuint)CGImageGetHeight(cgImageRef);
    CGRect rect = CGRectMake(0, 0, width, height);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    void *imageData = malloc(width * height * 4);
    CGContextRef context = CGBitmapContextCreate(imageData, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

	CGContextTranslateCTM(context, 0, height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGColorSpaceRelease(colorSpace);
    CGContextDrawImage(context, rect, cgImageRef);
    
    //创建纹理
    unsigned int texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    
    //设置一些边缘的处理
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    
    //将图片数据加载到纹理中
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.size.width, image.size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
    
    //释放图片数据
    CGContextRelease(context);
    free(imageData);
    
    ...
}

修改shader程序

这里给顶点着色器添加一个参数输入纹理坐标,然后增加一个传给片段着色器的纹理坐标参数,然后片段着色器将顶点着色器传来的坐标用于获取纹理上的颜色。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//顶点着色器
NSString *const vertexShaderString = SHADER_STRING
(
 //attribute 关键字用来描述传入shader的变量
 attribute vec4 vertexPosition; //传入的顶点坐标
 attribute vec2 textureCoords;//要获取的纹理坐标
 //传给片段着色器参数
 varying  vec2 textureCoordsOut;
 void main(void) {
     gl_Position = vertexPosition; // gl_Position是vertex shader的内建变量,gl_Position中的顶点值最终输出到渲染管线中
     textureCoordsOut = textureCoords;
 }
 );
//片段着色器
NSString *const fragmentShaderString = SHADER_STRING
(
 varying highp vec2 textureCoordsOut;
 
 uniform sampler2D Texture;
 void main(void) {
     //gl_FragColor是fragment shader的内建变量,gl_FragColor中的像素值最终输出到渲染管线中
     gl_FragColor = texture2D(Texture, textureCoordsOut);
 }
 );

设置纹理坐标

因为使用GL_TEXTURE_2D,所以纹理坐标是二维向量,在OpenGL中纹理坐标是以左下角为原点的。这里将之前的顶点坐标数组扩充,每个顶点后面跟着一个纹理坐标点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-(void)render {
	...
 	float vertices[] = {
        // positions           // texture coords
        0.5f,  0.5f, 0.0f,    1.0f, 1.0f, // top right
        0.5f, -0.5f, 0.0f,    1.0f, 0.0f, // bottom right
        -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, // bottom left
        -0.5f,  0.5f, 0.0f,   0.0f, 1.0f  // top left
    };
    
    const GLint Indices[] = {
        0, 1, 3,
        1, 2, 3
    };
    ...
}

将纹理坐标作为数据传给shader

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
-(void)render {
	...
	//顶点坐标对象
    GLuint vertexBuffer;
    glGenBuffers(1, &vertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    //将顶点坐标写入顶点VBO
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    
    
    //索引
    GLuint indexBuffer;
    glGenBuffers(1, &indexBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
    //将顶点索引数据写入索引缓冲对象
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
    
    
    GLuint vertexPosition = glGetAttribLocation(_glprogram, "vertexPosition");
    glVertexAttribPointer(vertexPosition, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(vertexPosition);
    
    
    GLuint textureCoords = glGetAttribLocation(_glprogram, "textureCoords");
    //vertices数组中,每五个元素取后两个作为纹理坐标
    glVertexAttribPointer(textureCoords, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(textureCoords);
    ...
}

渲染显示

之后再调用glDrawElements函数渲染即可显示。

<img src=”https://i.loli.net/2019/06/15/5d047c7d7651684651.png” width = “36%” height = “” div align= center/>

demo地址

参考:
纹理
IOS 中openGL使用教程3(openGL ES 入门篇 | 纹理贴图(texture)使用)