iOS OpenGL ES 笔记(二)----纹理

2019-08-06  本文已影响0人  皮皮蟹pipixie

纹理的基本概念

纹理是一个用来保存图像的颜色元素的OpenGL ES缓存。当用一个图像初始化一个纹理缓存之后,在这个图像中的每个像素变成纹理中的一个纹素。纹素存在于一个虚拟的纹理坐标系中。纹理坐标系有一个命名为S和T的2D轴,轴大小为0.0-1.0,如下图:


image.png

对齐纹理和几何图形

当绘制纹理时,我们需要把纹理坐标系与顶点的3D坐标系关联起来,所以在给顶点数据时,除了X、Y、Z坐标,还需要U和V的坐标值,U对应纹理坐标S轴的值,V对应纹理坐标T轴的值。

上代码

首先初始化数据,其中positionCoords为顶点坐标,图形由两个3角形组成,0.8是为了占整个视图的80%大小。textureCoords为纹理坐标,这里设置的是左下角(-1,-1)右上角(2,2),这样的话,总共有9个纹理坐标大小,正中间的一块坐标为左下角(0,0)右上角(1,1),为显示纹理的地方,其余边缘8块会根据拉伸样式来处理。

// 数据结构体
typedef struct {
    GLKVector3  positionCoords;
    GLKVector2  textureCoords;
}
SceneVertex;

// 要用到顶点纹理坐标
static SceneVertex vertices[] =
{
    {{-0.8f, -0.8f, 0.0f}, {-1.0f, -1.0f}},
    {{ 0.8f, -0.8f, 0.0f}, {2.0f, -1.0f}},
    {{-0.8f,  0.8f, 0.0f}, {-1.0f, 2.0f}},
    
    {{ 0.8f, -0.8f, 0.0f}, {2.0f, -1.0f}},
    {{-0.8f,  0.8f, 0.0f}, {-1.0f, 2.0f}},
    {{0.8f,  0.8f, 0.0},   {2.0f, 2.0f}},
    
};


然后是进行一些初始化操作。

- (void)viewDidLoad {
    
    [super viewDidLoad];
    
    // 设置刷新时间为1/60秒
    self.preferredFramesPerSecond = 60;
    
    GLKView *view = (GLKView *)self.view;
    NSAssert([view isKindOfClass:[GLKView class]], @"View controller's view is not a GLKView");
    view.context = [[AGLKContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
    [AGLKContext 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);
    
    ((AGLKContext *)view.context).clearColor = GLKVector4Make(0.2f, 0.2f, 0.2f, 1.0f);
    
    // 设置顶点缓存1,2,3步,生成id,绑定id,设置缓存内容
    self.vertexBuffer = [[AGLKVertexAttribArrayBuffer alloc] initWithAttribStride:sizeof(SceneVertex)
                                                                 numberOfVertices:sizeof(vertices)/sizeof(SceneVertex)
                                                                            bytes:vertices
                                                                            usage:GL_DYNAMIC_DRAW];
    
    CGImageRef imageRef = [[UIImage imageNamed:@"small32.png"] CGImage];
    // 垂直翻转图像数据
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:@(YES), GLKTextureLoaderOriginBottomLeft, nil];
    // GLKTextureInfo类的作用
    // 跟据图片获取OpenGL ES能使用的大小2的幂,向上取值,如400会变成512,最大1024
    // 生成纹理缓存标识符
    // 绑定当前上下文
    // 将图片像素的颜色数据复制到绑定的纹理缓存中
    // 其属性保存数据
    GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithCGImage:imageRef
                                                               options:options
                                                                 error:NULL];
    self.baseEffect.texture2d0.name = textureInfo.name;
    self.baseEffect.texture2d0.target = textureInfo.target;
    
    // 多层纹理
    CGImageRef imageRef1 = [[UIImage imageNamed:@"beetle.png"] CGImage];
    GLKTextureInfo *textureInfo1 = [GLKTextureLoader textureWithCGImage:imageRef1
                                                                options:options
                                                                  error:NULL];
    self.baseEffect.texture2d1.name = textureInfo1.name;
    self.baseEffect.texture2d1.target = textureInfo1.target;
    // GLKTextureEnvModeReplace, 输出颜色由从纹理获取的颜色.忽略输入的颜色
    // GLKTextureEnvModeModulate, 输出颜色是通过将纹理颜色与输入颜色来计算所得
    // GLKTextureEnvModeDecal, 输出颜色是通过使用纹理的alpha组件来混合纹理颜色和输入颜色来计算的。
    // GLKTextureEnvModeDecal颜色混合公式:
    // 1为上层纹理,2为下层纹理
    // R = A1*R1 + (1.0-A1)*R2
    // G = A1*G1 + (1.0-A1)*G2
    // B = A1*B1 + (1.0-A1)*B2
    // A = A1 + (1.0-A1)*A2
    // iOS CoreGraphics的混合方式也是如此
    self.baseEffect.texture2d1.envMode = GLKTextureEnvModeDecal;
}



设置纹理填充样式
GL_TEXTURE_WRAP_S:S轴上填充样式
GL_TEXTURE_WRAP_T:T轴上填充样式
GL_REPEAT: 超出纹理范围的坐标整数部分被忽略,形成重复效果。
GL_MIRRORED_REPEAT: 超出纹理范围的坐标整数部分被忽略,但当整数部分为奇数时进行取反,形成镜像效果。
GL_CLAMP_TO_EDGE:超出纹理范围的坐标被截取成0和1,形成纹理边缘延伸的效果。

GL_TEXTURE_MAG_FILTER:放大过滤(图片小于实际使用像素时)
GL_TEXTURE_MIN_FILTER:缩小过滤(图片大于实际使用像素时)
GL_NEAREST:最靠近像素中心的那个纹理单元将用于放大和缩小,这可能导致锯齿状的人工痕迹(会出现马赛克)
GL_LINEAR:会对靠近像素中心的一块2×2的纹理单元去加权平均值,用于放大和缩小(会模糊)
以下为不同填充过滤方式的效果图:


image.png
// 设置纹理填充样式(重复加马赛克方式)
- (void)update {
    [self.baseEffect.texture2d0 aglkSetParameter:GL_TEXTURE_WRAP_S
                                           value:GL_REPEAT];
    [self.baseEffect.texture2d0 aglkSetParameter:GL_TEXTURE_WRAP_T
                                           value:GL_REPEAT];
    [self.baseEffect.texture2d0 aglkSetParameter:GL_TEXTURE_MAG_FILTER
                                           value:GL_NEAREST];
}



绘制视图

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    
    [self.baseEffect prepareToDraw];
    [(AGLKContext *)view.context clear:GL_COLOR_BUFFER_BIT];
    
    // 顶点4.5步,启用缓存,传达缓存格式
    [self.vertexBuffer prepareToDrawWithAttrib:GLKVertexAttribPosition
                           numberOfCoordinates:3
                                  attribOffset:offsetof(SceneVertex, positionCoords)
                                  shouldEnable:YES];
    // 猫纹理4.5步,启用缓存,传达缓存格式
    [self.vertexBuffer prepareToDrawWithAttrib:GLKVertexAttribTexCoord0
                           numberOfCoordinates:2
                                  attribOffset:offsetof(SceneVertex, textureCoords)
                                  shouldEnable:YES];
    // 虫子纹理4.5步,启用缓存,传达缓存格式
    [self.vertexBuffer prepareToDrawWithAttrib:GLKVertexAttribTexCoord1
                           numberOfCoordinates:2
                                  attribOffset:offsetof(SceneVertex, textureCoords)
                                  shouldEnable:YES];
    
    [self.vertexBuffer drawArrayWithMode:GL_TRIANGLES
                        startVertexIndex:0
                        numberOfVertices:6];
}

释放资源

-(void)dealloc {
    
    GLKView *view = (GLKView *)self.view;
    [AGLKContext setCurrentContext:view.context];
    self.vertexBuffer = nil;
    ((GLKView *)self.view).context = nil;
    [EAGLContext setCurrentContext:nil];
}
上一篇 下一篇

猜你喜欢

热点阅读