美文网首页
OpenGL ES渲染图片

OpenGL ES渲染图片

作者: 我的大好时光 | 来源:发表于2018-03-13 08:47 被阅读96次

    思维导图

    openGLES渲染一张图片的思维导图


    OpenGL ES渲染图片.png

    代码

    //粘出主要部分代码

    - (void)layoutSubviews
    {
        //设置图层
        [self setupLayer];
        
        //设置上下文
        [self setupContext];
        
        //清空缓冲区
        [self clearBufferBit];
        
        //设置renderBuffers
        [self setRenderBuffers];
        
        //设置frameBuffers
        [self setFrameBuffers];
        
        //开始绘制
        [self renderLayer];
        
    }
    
    #pragma mark - 加载shader
    - (GLuint)loadShader:(NSString *)vert withFragment:(NSString *)frag
    {
        //定义两个临时着色器对象标识
        GLuint verShader, fragShader;
        
        GLuint program = glCreateProgram();
        
        //编译顶点着色器和片源着色器
        [self compileShader:&verShader type:GL_VERTEX_SHADER file:vert];
        [self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:frag];
    
        //创建最终的程序
        glAttachShader(program, verShader);
        glAttachShader(program, fragShader);
        
        //删除无用的对象标识
        glDeleteShader(verShader);
        glDeleteShader(fragShader);
        
        return program;
    }
    
    //编译GLSL文件
    - (void)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file
    {
        //读取GLSL文件内容
        NSString *content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
        const GLchar * source = [content UTF8String];
        
        //根据内容创建一个shader,着色器标识
        *shader = glCreateShader(type);
        
        //把着色器源码附着到着色器对象上
        glShaderSource(*shader, 1, &source, NULL);
        
        //把着色器代码编译成目标代码
        glCompileShader(*shader);
    
    }
    
    //设置纹理
    - (GLuint)setupTexture:(NSString *)fileName
    {
        CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
        
        if (!spriteImage)
        {
            NSLog(@"failed to load Image : %@", fileName);
            exit(1);
        }
        
        size_t width = CGImageGetWidth(spriteImage);
        size_t height = CGImageGetHeight(spriteImage);
        //图像存储空间 = 图像width * 图像height * 每个像素的字节数(kEAGLColorFormatRGBA8)
        //1byte = 8位 , 其中一个像素包含RGBA, 每个颜色占8位,也就是1byte。所以RGBA所占byte = 4 * 1 = 4
        //所以说:图像的存储空间 = width * height * 4
        GLubyte *spriteData = (GLubyte *)calloc(width * height * 4, sizeof(GLubyte));
        
        //创建上下文
        CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width * 4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
        
        
        //坐标反转(UIKit的原点坐标在屏幕左上角, Core Graphics的原点在屏幕的左下角)
        CGRect contextFrame = CGRectMake(0, 0, width, height);
        CGContextTranslateCTM(spriteContext, contextFrame.origin.x, contextFrame.origin.y);
        CGContextTranslateCTM(spriteContext, 0, contextFrame.size.height);
        CGContextScaleCTM(spriteContext, 1.0, -1.0);
        CGContextTranslateCTM(spriteContext, -contextFrame.origin.x, -contextFrame.origin.y);
        //绘制上下文
        CGContextDrawImage(spriteContext, contextFrame, spriteImage);
        
        CGContextRelease(spriteContext);
        
        
        //绑定纹理到默认的纹理ID(这里只有一张图片,故而相当于默认于片元着色器里面的colorMap,如果有多张图不可以这么做)
        glBindTexture(GL_TEXTURE_2D, 0);
        
        //设置纹理参数
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        
        //载入纹理
        GLfloat fw = width, fh = height;
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fw, fh, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
        
        //绑定纹理
        glBindTexture(GL_TEXTURE_2D, 0);
        
        free(spriteData);
        
        return 0;
    }
    
    //开始绘制
    - (void)renderLayer
    {
        glClearColor(0.0f, 1.0f, 1.0f, 1.0f);
        
        glClear(GL_COLOR_BUFFER_BIT);
        
        //设置视口大小
        GLfloat scale = [UIScreen mainScreen].scale;
        glViewport(self.frame.origin.x * scale,
                   self.frame.origin.y * scale,
                   self.frame.size.width * scale,
                   self.frame.size.height * scale);
        
        //读取顶点着色器和片源着色器
        NSString *vertFile = [[NSBundle mainBundle] pathForResource:@"shaderv" ofType:@"vsh"];
        NSLog(@"vertFile : %@", vertFile);
        NSString *fragFile = [[NSBundle mainBundle] pathForResource:@"shaderf" ofType:@"fsh"];
        NSLog(@"fragFile : %@", fragFile);
        
        //加载shader, 获取执行程序
        self.myProgram = [self loadShader:vertFile withFragment:fragFile];
        
        //链接
        glLinkProgram(self.myProgram);
        
        //获取链接状态
        GLint linkStatus;
        glGetProgramiv(self.myProgram, GL_LINK_STATUS, &linkStatus);
        
        if (linkStatus == GL_FALSE)
        {
            GLchar message[256];
            glGetProgramInfoLog(self.myProgram, sizeof(message), 0, message);
            NSString *messageString = [NSString stringWithUTF8String:message];
            
            NSLog(@"message String : %@", messageString);
            return;
        }
        
        NSLog(@"link Program success");
        
        //5.使用program
        glUseProgram(self.myProgram);
        
        //6.加载顶点数据
        //前3个是顶点坐标,后2个是纹理坐标
        GLfloat attrArr[] =
        {
            0.5f, -0.5f, -1.0f,     1.0f, 0.0f,
            -0.5f, 0.5f, -1.0f,     0.0f, 1.0f,
            -0.5f, -0.5f, -1.0f,    0.0f, 0.0f,
            0.5f, 0.5f, -1.0f,      1.0f, 1.0f,
            -0.5f, 0.5f, -1.0f,     0.0f, 1.0f,
            0.5f, -0.5f, -1.0f,     1.0f, 0.0f,
        };
    
        //处理顶点数据
        GLuint attriBuffer;
        //申请一个缓存区标识符
        glGenBuffers(1, &attriBuffer);
        //把缓存标识符绑定GL_ARRAY_BUFFER上
        glBindBuffer(GL_ARRAY_BUFFER, attriBuffer);
        //把顶点数据从cpu拷贝到gpu(GL_DYNAMIC_DRAW参考图1-1)
        glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);
        //将顶点数据通过myPrograme中的传递到顶点着色程序的position
        //1.glGetAttribLocation,用来获取vertex attribute的入口的.2.告诉OpenGL ES,通过glEnableVertexAttribArray,3.最后数据是通过glVertexAttribPointer传递过去的。
        //注意:第二参数字符串必须和shaderv.vsh中的输入变量:position保持一致
        GLuint position = glGetAttribLocation(self.myProgram, "position");
        //2.设置合适的格式从buffer里面读取数据
        glEnableVertexAttribArray(position);
        //3.设置读取方式
        //参数1:index,顶点数据的索引
        //参数2:size,每个顶点属性的组件数量,1,2,3,或者4.默认初始值是4.
        //参数3:type,数据中的每个组件的类型,常用的有GL_FLOAT,GL_BYTE,GL_SHORT。默认初始值为GL_FLOAT
        //参数4:normalized,固定点数据值是否应该归一化,或者直接转换为固定值。(GL_FALSE)
        //参数5:stride,连续顶点属性之间的偏移量,默认为0;
        //参数6:指定一个指针,指向数组中的第一个顶点属性的第一个组件。默认为0
        glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, NULL);
        
        //7.处理纹理数据
        GLuint textureCoordinate = glGetAttribLocation(self.myProgram, "textureCoordinate");
        
        glEnableVertexAttribArray(textureCoordinate);
        
        glVertexAttribPointer(textureCoordinate, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)  *5,  (GLfloat *)NULL + 3);
        
        //加载纹理
        [self setupTexture:@"timg-3"];
        
        GLuint rotate = glGetUniformLocation(self.myProgram, "rotateMatrix");
    
        //获取渲染的弧度
        float radians = 10 * 3.14159f / 180.0f;
    
        //求得弧度对于的sin\cos值
        float s = sin(radians);
        float c = cos(radians);
    
        //z轴旋转矩阵 参考3D数学第二节课的围绕z轴渲染矩阵公式
        GLfloat zRotation[16] = {
            c,  -s, 0,  0,
            s,  c,  0,  0,
            0,  0,  1,  0,
            0,  0,  0,  1,
        };
    //    GLfloat zRotation[16] = {
    //        1,  0,  0,  0,
    //        0,  1,  0,  0,
    //        0,  0,  1,  0,
    //        0,  0,  0,  1,
    //    };
    
        glUniformMatrix4fv(rotate, 1, GL_FALSE, zRotation);
        
        glDrawArrays(GL_TRIANGLES, 0, 6);
        
        [self.context presentRenderbuffer:GL_RENDERBUFFER];
    }
    
    //设置frameBuffers
    - (void)setFrameBuffers
    {
        GLuint buffer;
        
        glGenFramebuffers(1, &buffer);
        
        self.myColorFrameBuffers = buffer;
        
        glBindFramebuffer(GL_FRAMEBUFFER, self.myColorFrameBuffers);
        
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.myColorRenderBuffers);
    }
    
    //设置renderBuffers
    - (void)setRenderBuffers
    {
        //声明一个buffer标识
        GLuint buffer;
        
        //申请标识
        glGenRenderbuffers(1, &buffer);
        
        //赋值自己的属性标识
        self.myColorRenderBuffers = buffer;
        
        //绑定缓冲区
        glBindRenderbuffer(GL_RENDERBUFFER, self.myColorRenderBuffers);
        
        //分配缓冲区空间, 由图形上下文完成
        [self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.eaglLayer];
        
    }
    
    //清空缓冲区
    - (void)clearBufferBit
    {
        glDeleteRenderbuffers(1, &_myColorRenderBuffers);
        self.myColorRenderBuffers = 0;
        
        glDeleteFramebuffers(1, &(_myColorFrameBuffers));
        self.myColorFrameBuffers = 0;
    }
    
    //设置上下文
    - (void)setupContext
    {
        //初始化
        self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
        
        if (self.context == nil)
        {
            NSLog(@"创建图形上下文失败");
            return;
        }
        
        //设置当前的图形上下文
        if (![EAGLContext setCurrentContext:self.context])
        {
            NSLog(@"设置图形上下文失败");
            self.context = nil;
        }
    }
    
    //设置图层
    - (void)setupLayer
    {
        //重写layerClass方法,返回CAEAGLLayer图层
        self.eaglLayer = (CAEAGLLayer *)self.layer;
        
        //设置屏幕的scale
        [self setContentScaleFactor:[UIScreen mainScreen].scale];
        
        //CALayer默认是透明的,需要设置为不透明
        self.eaglLayer.opaque = YES;
        
        //设置描述的属性,这里设置不维持渲染内容,以及颜色格式为RGBA8
        NSDictionary *properties = @{
                                     kEAGLDrawablePropertyRetainedBacking : @(false),
                                     kEAGLDrawablePropertyColorFormat : kEAGLColorFormatRGBA8,
                                     };
        self.eaglLayer.drawableProperties = properties;
        
    }
    
    
    + (Class)layerClass
    {
        return CAEAGLLayer.class;
    }
    

    图1-1(把顶点数据从cpu拷贝到gpu)

    glbufferData-usage读取方式.png

    Demo下载地址

    点击下载

    相关文章

      网友评论

          本文标题:OpenGL ES渲染图片

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