IOS OpenGLES2.0 入门04 加载纹理

如果我们要在OpenGLES2.0中显示纹理的话, 要依赖于Fragment Shader.
OpenGL要有两个Shader, Vertex和Fragment
Vertex用来管理顶点, 你要绘制什么形状的图形, 都是由Vertex来控制的, 如三角形, 四边形, 立方体, 球形
Fragment用来管理颜色, 用gl_FragColor来控制输出的颜色, 这个颜色可以是自定的颜色RGBA, 如vec4(0, 0, 0,1),也可以是外部传递进来的颜色, 还可以是从一张纹理上某个点取出来的颜色texture2D(texture, point).

假如我们画一个三角形, 三个顶点A(-1, -1), B(1, -1), C(1, 1)
这时候gl_FragColor只会对ABC三个点赋颜色值, 至于其他点的颜色, 只能靠”猜”
如果是普通颜色的话, 就按照颜色插值, 按照与ABC的比重abc来计算Aa + Bb + Cc = 最终颜色, 如(1,1,1,1) * 0.5 + (0, 0, 0,1) * 0.5 = (0.5, 0.5, 0.5, 1)
如果是纹理的话, 假设A点是取纹理上的(0, 0)这个点, B点是取纹理上的(0, 1)这个点, 那么AB的中点会取纹理上的(0, 0.5)这个点

加载纹理的过程和RenderBuffer, FrameBuffer很类似
首先先创建一个纹理

glGenTextures (GLsizei n, GLuint *textures);

然后绑定

glBindTexture (GLenum target, GLuint texture);

绑定完后可以设置参数

glTexParameteri (GLenum target, GLenum pname, GLint param);

参数设置完后, 就可以给这张纹理加载数据了.

glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels);

第一个参数target, 必须为GL_TEXTURE_2D
第二个参数level为执行细节级别, 通常为0
第三个参数internalformat, 纹理的颜色格式, 必须与后面的format相同
第四个参数width, 纹理的宽度, 必须是2的n次方, 必须>=64
第五个参数width, 纹理的高度, 必须是2的n次方, 必须>=64
第六个参数border, 边框的值, 必须为0
第七个参数format, 像素数据的颜色格式, 必须与前面的internalformat一样, 常见的有GL_RGBA
第八个参数type, 像素数据的数据格式, 常见的有GL_UNSIGNED_BYTE
第九个参数pixels, 像素数据的首地址

这样我们就加载了一张纹理, 纹理的使用方式, 与[前一篇]绘制彩色三角形很像(http://blog.csdn.net/u010963658/article/details/52692201)

因为本质都是给顶点”上色”,
绘制彩色三角形是指明上什么颜色, 如RGBA(1, 0, 0, 1)
绘制纹理是指明要上纹理哪个点的颜色, 如XY(0, 0)

显示纹理的6个步骤
1.配置OpenGL

- (void) setupOpenGL {
    _eaglLayer = (CAEAGLLayer *) self.layer;
    _eaglLayer.opaque = YES;

    _eaglLayer.drawableProperties = @{kEAGLDrawablePropertyRetainedBacking: @YES,
                                      kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8};

    _eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
    [EAGLContext setCurrentContext:_eaglContext];

    glGenRenderbuffers(1, &_renderBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);

    glGenFramebuffers(1, &_frameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);

    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _renderBuffer);
    [_eaglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];

}

2.清屏

- (void) clear {

    glViewport(0, 0, self.frame.size.width, self.frame.size.height);
    glClearColor(0, 0, 1, 1);
    glClear(GL_COLOR_BUFFER_BIT);

    [EAGLContext setCurrentContext:self.eaglContext];
}

3.创建OpenGL Program

- (GLuint) compileShaderSource : (NSString *) shaderSource shaderType : (GLenum) shaderType {
    GLuint shader = glCreateShader(shaderType);
    const char *source = shaderSource.UTF8String;
    int length = (int)shaderSource.length;
    glShaderSource(shader, 1, &source, &length);

    glCompileShader(shader);

#ifdef DEBUG

    GLint compileSuccess;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compileSuccess);
    if (compileSuccess == GL_FALSE) {
        GLchar messages[256];
        glGetShaderInfoLog(shader, sizeof(messages), 0, &messages[0]);
        NSString *messageString = [NSString stringWithUTF8String:messages];
        NSLog(@"%@", messageString);
        return 0;
    }
#endif

    return shader;
}

/**
 编译Program + Vertex Shader + Fragment Shader
 */
- (void) createProgram {

    NSString *vertexPath = [[NSBundle mainBundle] pathForResource:@"VertexShader" ofType:@"glsl"];
    NSString *vertexShaderSource = [NSString stringWithContentsOfFile:vertexPath encoding:NSUTF8StringEncoding error:nil];

    NSString *fragmentPath = [[NSBundle mainBundle] pathForResource:@"FragmentShader" ofType:@"glsl"];
    NSString *fragmentShaderSource = [NSString stringWithContentsOfFile:fragmentPath encoding:NSUTF8StringEncoding error:nil];

    //编译Vertex Shader和Fragment Shader
    _vertexShader = [self compileShaderSource:vertexShaderSource shaderType:GL_VERTEX_SHADER];
    _fragmentShader = [self compileShaderSource:fragmentShaderSource shaderType:GL_FRAGMENT_SHADER];

    //创建program并与Vertex Shader和Fragment Shader关联
    _program = glCreateProgram();
    glAttachShader(_program, _vertexShader);
    glAttachShader(_program, _fragmentShader);

    //链接program
    glLinkProgram(_program);

#ifdef DEBUG
    GLint linkSuccess;
    glGetProgramiv(_program, GL_LINK_STATUS, &linkSuccess);
    if (linkSuccess == GL_FALSE) {
        GLchar messages[256];
        glGetProgramInfoLog(_program, sizeof(messages), 0, &messages[0]);
        NSString *messageString = [NSString stringWithUTF8String:messages];
        NSLog(@"%@", messageString);
        return;
    }
#endif
    //开始使用program
    glUseProgram(_program);

    _positionAttribute = glGetAttribLocation(_program, "position");
    _textureCoordAttribute = glGetAttribLocation(_program, "inputTexCoord");

    _textureUnifrom = glGetUniformLocation(_program, "texture");
}

4.创建顶点与纹理显示位置的数据

static int vertexCount = 6;
static GLfloat vertexData[] = {
    -0.9,   -0.9,
    0.9,    -0.9,
    0.9,    0.9,
    0.9,    0.9,
    -0.9,   0.9,
    -0.9,   -0.9
};

static int textureCoordCount = 6;
static GLfloat textureCoordData[] = {
    //纹理的范围是[0, 1]
    0, 0,
    1, 0,
    1, 1,
    1, 1,
    0, 1,
    0, 0
};

//Vertex And TextureCoord Data
- (void) createData {
    glGenBuffers(1, &_vertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);

    //激活_positionAttribute
    glEnableVertexAttribArray(_positionAttribute);
    //因为上面激活了_positionAttribute, 所以这边是告诉OpenGL这些数据怎么用, 给_positionAttribute用
    glVertexAttribPointer(_positionAttribute, 2, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT) * 2, NULL);

    glGenBuffers(1, &_textureCoordBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, _textureCoordBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(textureCoordData), textureCoordData, GL_STATIC_DRAW);

    glEnableVertexAttribArray(_textureCoordAttribute);
    glVertexAttribPointer(_textureCoordAttribute, 2, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT) * 2, NULL);
}

5.加载纹理

- (GLuint) loadTextureFromCGImage : (CGImageRef) imageRef {

    //copy rgba data from imageRef
    int width = (int)CGImageGetWidth(imageRef);
    int height = (int)CGImageGetHeight(imageRef);
    GLubyte * imageData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte));

    CGContextRef spriteContext = CGBitmapContextCreate(imageData, width, height, 8, width * 4, CGImageGetColorSpace(imageRef), kCGImageAlphaPremultipliedLast);

    CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), imageRef);
    CGContextRelease(spriteContext);

    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
    free(imageData);

    return texture;
}

- (void) createTexture {
    UIImage *image = [UIImage imageNamed:@"finsh.png"];
    GLuint finshTexture = [self loadTextureFromCGImage:image.CGImage];

    //激活GL_TEXTURE0
    glActiveTexture(GL_TEXTURE0);
    //将finshTexture绑定到GL_TEXTURE_2D上, 因为这边激活的是GL_TEXTURE0, 所以finshTexture是绑定到GL_TEXTURE0上
    glBindTexture(GL_TEXTURE_2D, finshTexture);

    //让_textureUnifrom这个使用GL_TEXTURE0, 如果使用GL_TEXTURE1, 对应的参数为1, 使用GL_TEXTUREi对应的参数为i
    glUniform1i(_textureUnifrom, 0);
}

6绘制

- (void) render {

    //绑定_vertexBuffer到GL_ARRAY_BUFFER
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
    //上面绑定了_vertexBuffer, 所以这边的DrawArrays是针对_vertexBuffer的操作
    glDrawArrays(GL_TRIANGLES, 0, vertexCount);

    //绑定_textureCoordBuffer到GL_ARRAY_BUFFER
    glBindBuffer(GL_ARRAY_BUFFER, _textureCoordBuffer);
    //上面绑定了_textureCoordBuffer, 所以这边的DrawArrays是针对_textureCoordBuffer
    glDrawArrays(GL_TRIANGLES, 0, textureCoordCount);

    [self.eaglContext presentRenderbuffer:GL_RENDERBUFFER];
}

最后的代码在这

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值