美文网首页
iOS:OpenGL加载纹理

iOS:OpenGL加载纹理

作者: 豆浆油条cc | 来源:发表于2020-02-28 16:48 被阅读0次

    参看资料:
    https://juejin.im/post/5cfa680b6fb9a07ef63fced8

    OpenGL加载纹理

    //
    //  ViewController.m
    //  openGL-加载图片
    //
    //  Created by mac on 2020/2/25.
    //  Copyright © 2020 cc. All rights reserved.
    //
    
    #import "ViewController.h"
    #import <GLKit/GLKit.h>
    
    typedef struct {
        GLKVector3 positionCoord;
        GLKVector2 textureCoord;
    } SenceVertex;
    
    
    @interface ViewController ()
    
    @property (nonatomic,strong) UIImageView *imageView;
    
    @property (nonatomic, assign) SenceVertex *vertices;
    @property (nonatomic, strong) EAGLContext *context;
    
    @property (nonatomic, strong) CAEAGLLayer* myLayer;
    // 用于刷新屏幕
    @property (nonatomic, strong) CADisplayLink *displayLink;
    // 开始的时间戳
    @property (nonatomic, assign) NSTimeInterval startTimeInterval;
    // 着色器程序
    @property (nonatomic, assign) GLuint program;
    // 顶点缓存
    @property (nonatomic, assign) GLuint vertexBuffer;
    // 纹理 ID
    @property (nonatomic, assign) GLuint textureID;
    
    
    @end
    
    @implementation ViewController
    
    -(void)viewDidLoad{
            
        [self initFilter];
    }
    
    - (void)initFilter{
        
        [self setupCoord];
        [self setupContext];
        [self setupLayer];
        [self bindRenderLayer:self.myLayer];
        [self bindImage:[[NSBundle mainBundle] pathForResource:@"image01.jpg" ofType:nil]];
        [self setupPort];
        [self bindTopBuffer];
        [self setupShaderProgramWidthName:@"Normal"];
        [self startTimer];
    }
    
    - (void)removeTimer{
        if (self.displayLink) {
            [self.displayLink invalidate];
            self.displayLink = nil;
        }
    }
    
    - (void)startTimer{
        
        [self removeTimer];
        
        self.startTimeInterval = 0;
        self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(timeAction)];
        
        [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
        
    }
    
    - (void)timeAction {
        //DisplayLink 的当前时间撮
        if (self.startTimeInterval == 0) {
            self.startTimeInterval = self.displayLink.timestamp;
        }
        //使用program
        glUseProgram(self.program);
        //绑定buffer
        glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer);
        
        // 传入时间
        CGFloat currentTime = self.displayLink.timestamp - self.startTimeInterval;
        GLuint time = glGetUniformLocation(self.program, "Time");
        glUniform1f(time, currentTime);
        
        // 清除画布
        glClear(GL_COLOR_BUFFER_BIT);
        glClearColor(1, 1, 1, 1);
        
        // 重绘
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
        //渲染到屏幕上
        [self.context presentRenderbuffer:GL_RENDERBUFFER];
        
        [self imageFromTextureWithWidth:[self drawableWidth] height:[self drawableHeight]];
        
    }
    
    //渲染上下文
    - (void)setupContext{
        self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
        if (!self.context) {
            self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
        }
        
        [EAGLContext setCurrentContext:self.context];
    }
    
    //设置图层(CAEAGLLayer)
    - (void)setupLayer{
        self.myLayer = [[CAEAGLLayer alloc] init];
        self.myLayer.frame = CGRectMake(0, 50, self.view.frame.size.width, self.view.frame.size.width);
        self.myLayer.contentsScale = [UIScreen mainScreen].scale;
        self.myLayer.opaque = NO; //CALayer默认是透明的,透明的对性能负荷大,故将其关闭
        self.myLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                       // 由应用层来进行内存管理
                                       @(NO),kEAGLDrawablePropertyRetainedBacking,
                                       kEAGLColorFormatRGBA8,kEAGLDrawablePropertyColorFormat,
                                       nil];
        
        
        [self.view.layer addSublayer:self.myLayer];
    }
    
    //绑定渲染缓冲区
    - (void)bindRenderLayer:(CAEAGLLayer*)layer{
        // 渲染缓冲区、帧缓冲区对象
        GLuint renderBuffer;
        GLuint frameBuffer;
        
        // 获取渲染缓冲区,绑定渲染缓存区以及将渲染缓冲区与layer建立连接
        glGenRenderbuffers(1, &renderBuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);
        [self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer];
        
        // 获取帧缓冲区名称, 绑定帧缓冲区以及将渲染缓冲区附着到帧缓冲区上
        glGenFramebuffers(1, &frameBuffer);
        glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderBuffer);
    }
    
    //设置顶点缓冲区
    - (void)bindTopBuffer{
        // 设置顶点缓存区
        GLuint vertexBuffer;
        glGenBuffers(1, &vertexBuffer);
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
        GLsizeiptr bufferSizeBytes = sizeof(SenceVertex) * 4;
        glBufferData(GL_ARRAY_BUFFER, bufferSizeBytes, self.vertices, GL_DYNAMIC_DRAW);
        self.vertexBuffer = vertexBuffer;
    }
    
    //初始化坐标
    - (void)setupCoord{
        // 开辟顶点数组内存空间
        self.vertices = malloc(sizeof(SenceVertex) * 4);
        // 初始化顶点数据以及纹理坐标
        self.vertices[0] = (SenceVertex){{-1, 1, 0}, {0, 1}};
        self.vertices[1] = (SenceVertex){{-1, -1, 0}, {0, 0}};
        self.vertices[2] = (SenceVertex){{1, 1, 0}, {1, 1}};
        self.vertices[3] = (SenceVertex){{1, -1, 0}, {1, 0}};
    }
    
    //读取图片+设置纹理ID
    - (void)bindImage:(NSString*)imagePath{
        // 读取图片
        UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
        // 将图片转换成纹理图片
        GLuint textureID = [self createTextureWithImage:image];
        // 设置纹理ID
        self.textureID = textureID;
    }
    
    // 设置视口
    - (void)setupPort{
        glViewport(0, 0, self.drawableWidth, self.drawableHeight);
    }
    
    // 从图片中加载纹理
    - (GLuint)createTextureWithImage:(UIImage *)image {
        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.0f, -1.0f);
        CGColorSpaceRelease(colorSpace);
        CGContextClearRect(context, rect);
        CGContextDrawImage(context, rect, cgImageRef);
        
        GLuint textureID;
        glGenTextures(1, &textureID);
        glBindTexture(GL_TEXTURE_2D, textureID);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
        
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        
        glBindTexture(GL_TEXTURE_2D, 0);
        
        CGContextRelease(context);
        free(imageData);
        
        return textureID;
    }
    
    //设置着色器
    - (void)setupShaderProgramWidthName:(NSString *)name {
        GLuint program = [self programWithShaderName:name];
        
        glUseProgram(program);
        
        GLuint positionSlot = glGetAttribLocation(program, "Position");
        GLuint textureSlot = glGetAttribLocation(program, "Texture");
        GLuint textureCoordSlot = glGetAttribLocation(program, "TextureCoord");
        
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, self.textureID);
        
        glUniform1i(textureSlot, 0);
        
        glEnableVertexAttribArray(positionSlot);
        glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(SenceVertex), NULL + offsetof(SenceVertex, positionCoord));
        
        glEnableVertexAttribArray(textureCoordSlot);
        glVertexAttribPointer(textureCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(SenceVertex), NULL + offsetof(SenceVertex, textureCoord));
        
        self.program = program;
    }
    
    - (GLuint)programWithShaderName:(NSString *)shaderName {
        GLuint vertexShader = [self compileShaderWithName:shaderName type:GL_VERTEX_SHADER];
        GLuint fragmentShader = [self compileShaderWithName:shaderName type:GL_FRAGMENT_SHADER];
        
        GLuint program = glCreateProgram();
        glAttachShader(program, vertexShader);
        glAttachShader(program, fragmentShader);
        
        glLinkProgram(program);
        
        GLint linkSuccess;
        glGetProgramiv(program, GL_LINK_STATUS, &linkSuccess);
        if (linkSuccess == GL_FALSE) {
            GLchar messages[250];
            glGetProgramInfoLog(program, sizeof(messages), 0, &messages[0]);
            NSString *messageString = [NSString stringWithUTF8String:messages];
            NSAssert(NO, @"program link 失败 %@", messageString);
            exit(1);
        }
        
        return program;
    }
    
    - (GLuint)compileShaderWithName:(NSString *)name type:(GLenum)shaderType {
        NSString *shaderPath = [[NSBundle mainBundle] pathForResource:name ofType:shaderType == GL_VERTEX_SHADER ? @"vsh" : @"fsh" ];
        NSError *error;
        NSString *shaderString = [NSString stringWithContentsOfFile:shaderPath encoding:NSUTF8StringEncoding error:&error];
        if (!shaderString) {
            NSAssert(NO, @"读取shader失败");
            exit(1);
        }
        
        GLuint shader = glCreateShader(shaderType);
        
        const char *shaderStringUTF8 = [shaderString UTF8String];
        int shaderStringLength = (int)[shaderString length];
        glShaderSource(shader, 1, &shaderStringUTF8, &shaderStringLength);
        
        glCompileShader(shader);
        
        GLint compileSuccess;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compileSuccess);
        if (compileSuccess == GL_FALSE) {
            GLchar messages[250];
            glGetShaderInfoLog(shader, sizeof(messages), 0, &messages[0]);
            NSString *messageString = [NSString stringWithUTF8String:messages];
            NSAssert(NO, @"shader编译失败: %@", messageString);
            exit(1);
        }
        return shader;
    }
    
    
    // 返回某个纹理对应的 UIImage,调用前先绑定对应的帧缓存
    - (UIImage *)imageFromTextureWithWidth:(int)width height:(int)height {
        int size = width * height * 4;
        GLubyte *buffer = malloc(size);
        glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
        CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer, size, NULL);
        int bitsPerComponent = 8;
        int bitsPerPixel = 32;
        int bytesPerRow = 4 * width;
        CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
        CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
        CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
        CGImageRef imageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
        
        // 此时的 imageRef 是上下颠倒的,调用 CG 的方法重新绘制一遍,刚好翻转过来
        UIGraphicsBeginImageContext(CGSizeMake(width, height));
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        
        free(buffer);
        
        self.imageView.image = image;
        
        return image;
    }
    
    // 获取渲染缓存区的宽
    - (GLint)drawableWidth {
        GLint backingWith;
        glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWith);
        return backingWith;
    }
    
    // 获取渲染缓存区的高
    - (GLint)drawableHeight {
        GLint backingHeight;
        glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
        return backingHeight;
    }
    
    @end
    
    

    Github:
    https://github.com/qw9685/OpenGL-image

    相关文章

      网友评论

          本文标题:iOS:OpenGL加载纹理

          本文链接:https://www.haomeiwen.com/subject/xsvlhhtx.html