iOS技术IOS开发者心得程序员

iOS开发之OpenGL ES(三)—纹理

2017-03-20  本文已影响442人  绿豆粥与茶叶蛋

前言:

这篇文章是作者iOS开发之OpenGL ES系列文章的第三篇,首先将把之前的GLKit的案例进行一下简单的重构以简化代码,提高代码的复用率,然后主要深入探讨一下纹理的底层概念和常用选项。
注:(如果你觉得我的文章有所帮助,点个喜欢或关注。您的每一份鼓励都是我前进的动力😝)

本文系列第一篇:初见篇已经完结,感兴趣可以看看:
iOS开发之OpenGL ES—初见
iOS开发之OpenGL ES—GLKit

正文:

GLKit案例的重构:
#import <GLKit/GLKit.h>
@interface AGLKContext : EAGLContext
{
    GLKVector4 _clearColor;
}
@property (nonatomic,assign)GLKVector4 clearColor;

- (void)clear:(GLbitfield)mask;
@end

#import "AGLKContext.h"
@implementation AGLKContext

//设置当前OpenGL ES的上下文的“清除颜色”(set存方法)
- (void)setClearColor:(GLKVector4)clearColor{
    _clearColor = clearColor;
    
    glClearColor(clearColor.r,
                 clearColor.g,
                 clearColor.b,
                 clearColor.a);
    
}
//获得当前的OpenGL ES的上下文的“清除颜色”(get取方法)
- (GLKVector4)clearColor{
    return _clearColor;
}
//清除颜色缓冲
- (void)clear:(GLbitfield)mask{
    glClear(mask);
}
@end
//下面的实现在三个方法中封装了7个缓存管理步骤
#import <GLKit/GLKit.h>
#import <Foundation/Foundation.h>

@interface AGLKVertexAttribArrayBuffer : NSObject

- (id)initWithAttribStride:(GLsizei)stride numberOfVertices:(GLsizei)count data:(const GLvoid *)dataPtr usage:(GLenum)usage;

- (void)prepareToDrawWithAttrib:(GLint)index numberOfCoordinates:(GLint)count attribOffset:(GLsizei)offset shouldEnable:(BOOL)shouldEnable;

- (void)drawArrayWithMode:(GLenum)mode startVertexIndex:(GLint)first numberOfVertices:(GLsizei)count;

//.m文件实现
- (id)initWithAttribStride:(GLsizei)stride numberOfVertices:(GLsizei)count data:(const GLvoid *)dataPtr usage:(GLenum)usage{
    NSParameterAssert(0 < stride);
    NSParameterAssert(0 < count);
    NSParameterAssert(NULL != dataPtr);
    
    self = [super init];
    if (self) {
        _stride = stride;
        _bufferSizeBytes = _stride*count;
        
        glGenBuffers(1, &_glName);//1、为缓存生成一个独一无二的标识符
        glBindBuffer(GL_ARRAY_BUFFER, _glName);//2、为接下来的应用绑定缓存
        glBufferData(GL_ARRAY_BUFFER, _bufferSizeBytes, dataPtr, usage);//3、复制数据到缓存中

        
        NSAssert(0 != _glName, @"生成唯一标识失败");
    }
    return self;
}

- (void)prepareToDrawWithAttrib:(GLint)index numberOfCoordinates:(GLint)count attribOffset:(GLsizei)offset shouldEnable:(BOOL)shouldEnable{
    NSParameterAssert((0<count) && (count<=4));
    NSParameterAssert(offset < self.stride);
    
    if (shouldEnable) {
        glEnableVertexAttribArray(index);//4、启动顶点缓存渲染操作
    }
    
    glVertexAttribPointer(index, count, GL_FLOAT, GL_FALSE, self.stride, NULL + offset);//5、告诉OpenGL ES顶点数据在哪里,以及解释为每个顶点保存的数据
    
}

- (void)drawArrayWithMode:(GLenum)mode startVertexIndex:(GLint)first numberOfVertices:(GLsizei)count{
    NSAssert(self.bufferSizeBytes >= (first+count)*self.stride, @"试图渲染多于可用数量的顶点");
    glDrawArrays(mode, first, count);//6、绘图
}

- (void)dealloc{
    if (0 != _glName) {
        glDeleteBuffers(1, &_glName);//7、删除不再需要的顶点缓存
        _glName = 0;
    }
}
纹理
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);//告诉OpenGL ES无论何时出现多个纹素对应一个片元时,与片元的U、V坐标最接近的纹素颜色会被取样
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);//告诉OpenGL ES无论何时出现多个纹素对应一个片元时,从相配的多个纹素中取样颜色,然后使用线性内插法来混合这些颜色以得到片元的颜色。
    
    //GL_TEXTURE_MAG_FILTER参数用于没有足够的可用纹素来唯一性的映射一个或者多个纹素到每个片元时配置取样
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);//会有一个放大纹理的效果,并让它模糊的出现在渲染的三角形上
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//仅仅会拾取与片元的U、V位置接近的纹素的颜色,并放大纹理,这会使它有点像素化地出现在渲染的三角形上

//下一篇文章会演示一些由这些纹理参数值产生的效果
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);//取样纹理边缘的纹素
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);//重复纹理填满
    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);//取样纹理边缘的纹素
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);//重复纹理填满

//下一篇文章会演示一些由这些纹理参数值产生的效果
纹理Demo

在上一篇文章Demo的基础上先简单的用纹理控制一个渲染的图形的每个像素的颜色。如图1-1:

图1-1.png
typedef struct{
    GLKVector3 positionCoords;//GLKVector3类型的positionCoords
    GLKVector2 textureCoords;//GLKVector2类型的纹理坐标
}SceneVertex;
//初始化位置坐标和纹理坐标
static const SceneVertex vertices[] = {
    {{-0.5f,-0.4f,0.0},{0.0f,0.0f}},//前面三个数字是位置坐标,后面2个数字是纹理坐标
    {{0.5f,-0.4f,0.0},{1.0f,0.0f}},
    {{-0.5f,0.4f,0.0},{0.0f,1.0f}},
    {{0.5f,0.4f,0.0},{1.0f,1.0f}}
};
    CGImageRef imageRef = [[UIImage imageNamed:@"butterfly"] CGImage];
    GLKTextureInfo *info = [GLKTextureLoader textureWithCGImage:imageRef options:nil error:NULL];//接受一个CGImageRef并创建一个新的包含CGImageRef的像素数据的OpenGL ES纹理缓存,options参数接受一个存储了用于指定GLKTextureLoader怎么解析加载的图像数据的键值对的NSDictionary。可用选项之一是指示GLKTextureLoader为加载的图像生成MIP贴图。
    self.baseEffect.texture2d0.name = info.name;//设置baseEffect的texture2d0属性和使用一个新的纹理缓存。GLKTextureInfo类封装了与刚创建的纹理缓存相关的信息,包含他的尺寸、是否包含MIP贴图、OpenGL ES标识符、名字以及用于纹理的OpenGL ES目标等。
    self.baseEffect.texture2d0.target = info.target;
    /*4、启动纹理缓存渲染操作
     *5、告诉OpenGL ES纹理数据在哪里,以及解释为每个纹理保存的数据
     */
    [self.vertexBuffer prepareToDrawWithAttrib:GLKVertexAttribTexCoord0 numberOfCoordinates:2 attribOffset:offsetof(SceneVertex, textureCoords) shouldEnable:YES];
 /*
 *指示OpenGL ES去渲染图形
 */
[self.vertexBuffer drawArrayWithMode:GL_TRIANGLE_STRIP startVertexIndex:0 numberOfVertices:4];

源码已上传至fenglinyunshi-git,欢迎下载,并提出宝贵意见。

结语:

本文是iOS开发之OpenGL ES的第三篇文章,主要关于GLKit的重构以及引入了一种纹理的技术,它可以控制一个渲染的图形的每个像素的颜色。介绍了一下它的底层概念,后序还会介绍一些关于它的一些常用选项。如果看后你有所收获,那么我会非常高兴,如果文中有不准确的地方还望提出指证,笔者将非常感谢!
未完待续 ...

有志者事竟成,破釜沉舟,百二秦关终属楚

上一篇 下一篇

猜你喜欢

热点阅读