使用OpenGL显示UIImage

作者: saiGo | 来源:发表于2016-05-14 18:11 被阅读775次

    使用openGL显示UIImage的原理无非就是将UIImage通过openGL转换成纹理然后贴在一个空间坐标中,openGL可以将UIImage的CGImage转换成纹理,其中openGL需要使用到着色器,转换代码如下:

    import UIKit
    import GLKit
    import CoreMotion
    let TEX_COORD_MAX : Float = 1
    class myGL: UIView {
    
        struct Vertex{
            
            var Position: (CFloat, CFloat, CFloat, CFloat);
            var TexCoord: (CFloat, CFloat);
        }
        
        var Vertices : [Vertex] = [
            Vertex(Position: (1, -1, 0, 1), TexCoord: (TEX_COORD_MAX, 0)),
            Vertex(Position: (1, 1, 0, 1), TexCoord: (TEX_COORD_MAX, TEX_COORD_MAX)),
            Vertex(Position: (-1, 1, 0, 1), TexCoord: (0, TEX_COORD_MAX)),
            Vertex(Position: (-1, -1, 0, 1), TexCoord: (0, 0))
        ]
        
        var Indices: [GLubyte] = [
            0, 1, 2,
            2, 3, 0
        ]
        
        //基本属性
        private var eaglLayer : CAEAGLLayer?
        private var context : EAGLContext?
        private var colorRenderBuffer : GLuint = 0
        
        //着色器
        private var fragmentShader : GLuint = 0
        private var vertexShader : GLuint = 0
        
        //着色器变量
        private var positionSlot : GLuint = 0
        private var textCoordSlot : GLuint = 0
        private var textureUni : GLuint = 0
        private var filterUni : GLuint = 0
        
        //纹理
        private var texture : GLuint = 0
        
        //VBO
        private var vertexBuffer : GLuint = 0
        private var indexBuffer : GLuint = 0
        
        //UIImage的CGImage,从外部接收
        var spriteImage : CGImage?
    
       override class func layerClass() -> AnyClass
        {
            return CAEAGLLayer.self
         }
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            setUpLayer()
            setUpContext()
            setUpRenderBuffer()
            setUpFramebuffer()
            compileShaders()
            setUpVBOs()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }
    
        
        private func setUpLayer() {
            eaglLayer = layer as? CAEAGLLayer;
            eaglLayer!.opaque = true;
        }
        
        private func setUpContext()
        {
            context = EAGLContext(API: .OpenGLES2)
            if context == nil {
                print("fail load OpenGLES2")
                return
            }
            
            if EAGLContext.setCurrentContext(context) == false
                print("fail to set currentContext")
            }
        }
        
        private func compileShaders()
        {
           fragmentShader = compileShader("FragmentShader", type: GLenum(GL_FRAGMENT_SHADER))
           vertexShader = compileShader("VertexShader", type: GLenum(GL_VERTEX_SHADER))
           
           let programHandle = glCreateProgram()
            glAttachShader(programHandle, vertexShader);
            glAttachShader(programHandle, fragmentShader);
            glLinkProgram(programHandle);
           
            var linkSuccess : GLint = 0
            glGetProgramiv(programHandle, GLenum(GL_LINK_STATUS), &linkSuccess)
            if linkSuccess == GL_FALSE
            {
                var value: GLint = 0
                glGetProgramiv(programHandle, GLenum(GL_INFO_LOG_LENGTH), &value)
                var infoLog: [GLchar] = [GLchar](count: Int(value), repeatedValue: 0)
                var infoLogLength: GLsizei = 0
                glGetProgramInfoLog(programHandle, value, &infoLogLength, &infoLog)
                let s = NSString(bytes: infoLog, length: Int(infoLogLength), encoding: NSASCIIStringEncoding)
                print(s)
            }
            glUseProgram(programHandle)
            
            //关联shader的参数
            positionSlot = GLuint(glGetAttribLocation(programHandle, "position"))
            glEnableVertexAttribArray(GLuint(positionSlot))
    
        
            glGetAttribLocation(programHandle, "texCoordIn")
            glEnableVertexAttribArray(GLuint(textCoordSlot))
            
            textureUni = GLuint(glGetUniformLocation(programHandle, "textureImage"))
            
            filterUni = GLuint(glGetUniformLocation(programHandle, "rgbaFilter"))
            glUniformMatrix4fv(GLint(filterUni), 1, GLboolean(GL_FALSE), filter!)
        }
        
        private func compileShader(shaderName : String, type : GLenum) -> GLuint
        {
            //1
            let shaderPath = NSBundle.mainBundle().pathForResource(shaderName, ofType: "glsl")
            var shaderString : NSString?
            do{
                shaderString = try NSString(contentsOfFile: shaderPath!, encoding: NSUTF8StringEncoding)
            }
            catch{
                print("fail to compile shader")
            }
            
            //2
            let shaderHandle = glCreateShader(type)
            
            //3
            var shaderStringUTF8 = shaderString?.cStringUsingEncoding(NSUTF8StringEncoding)
            glShaderSource(shaderHandle, 1, &shaderStringUTF8!, nil)
    
            //4
            glCompileShader(shaderHandle)
            
            //5
            var compileSuccess  : GLint = 0
            glGetShaderiv(shaderHandle, GLenum(GL_COMPILE_STATUS), &compileSuccess)
            if compileSuccess == GL_FALSE
            {
                var value: GLint = 0
                glGetShaderiv(shaderHandle, GLenum(GL_INFO_LOG_LENGTH), &value)
                var infoLog: [GLchar] = [GLchar](count: Int(value), repeatedValue: 0)
                var infoLogLength: GLsizei = 0
                glGetShaderInfoLog(shaderHandle, value, &infoLogLength, &infoLog)
                let s = NSString(bytes: infoLog, length: Int(infoLogLength), encoding: NSASCIIStringEncoding)
                print(s)
            }
            return shaderHandle
        }
        
        private func setUpVBOs()
        {
            glGenBuffers(1, &vertexBuffer)
            glBindBuffer(GLenum(GL_ARRAY_BUFFER), vertexBuffer);
            glBufferData(GLenum(GL_ARRAY_BUFFER), Vertices.count * sizeofValue(Vertices[0]), Vertices, GLenum(GL_STATIC_DRAW))
            
            glGenBuffers(1, &indexBuffer);
            glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), indexBuffer);
            glBufferData(GLenum(GL_ELEMENT_ARRAY_BUFFER), Indices.count * sizeofValue(Indices[0]), Indices, GLenum(GL_STATIC_DRAW))
        }
        
        private func setUpRenderBuffer()
        {
            glGenRenderbuffers(1, &colorRenderBuffer);
            glBindRenderbuffer(GLenum(GL_RENDERBUFFER), colorRenderBuffer);
            context!.renderbufferStorage(Int(GL_RENDERBUFFER), fromDrawable: eaglLayer);
        }
        
        private func setUpFramebuffer()
        {
            var framebuffer = GLuint()
            glGenFramebuffers(1, &framebuffer)
            glBindFramebuffer(GLenum(GL_FRAMEBUFFER), framebuffer)
            glFramebufferRenderbuffer(GLenum(GL_FRAMEBUFFER), GLenum(GL_COLOR_ATTACHMENT0),
                                      GLenum(GL_RENDERBUFFER), colorRenderBuffer)
        }
    
    private func render()
        {
            glClearColor(0.0, 1.0, 0.0, 0.0)
            glClear(GLenum(GL_COLOR_BUFFER_BIT))
            
            glViewport(0, 0, GLsizei(frame.size.width), GLsizei(frame.size.height))
            
            glBindBuffer(GLenum(GL_ARRAY_BUFFER), vertexBuffer)
            glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), indexBuffer)
            
            glEnableVertexAttribArray(positionSlot)
            glVertexAttribPointer(GLuint(positionSlot), 4, GLenum(GL_FLOAT), GLboolean(GL_FALSE),
                                  GLsizei(sizeof(Vertex)), UnsafePointer<Int>(bitPattern: 0))
            
            glVertexAttribPointer(textCoordSlot, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), Int32(sizeof(Vertex)), UnsafePointer<Void>(bitPattern: sizeof(Float) * 4))
            
            glActiveTexture(GLenum(GL_TEXTURE0))
            glBindTexture(GLenum(GL_TEXTURE_2D), texture)
            glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MAG_FILTER), GL_LINEAR)
            glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MIN_FILTER), GL_LINEAR)
            glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), GL_CLAMP_TO_EDGE)
            glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), GL_CLAMP_TO_EDGE)
                  
            glDrawElements(GLuint(GL_TRIANGLES), GLsizei(Indices.count), GLenum(GL_UNSIGNED_BYTE), UnsafePointer<Int>(bitPattern: 0))
            
            glDeleteTextures(1, &texture)
            
            context!.presentRenderbuffer(Int(GL_RENDERBUFFER))
        }
        
        //暴露接口
        func setUpTexture(spriteImage : CGImage)
        {
            // 1
            self.spriteImage = spriteImage
            // 2
            let width = CGImageGetWidth(spriteImage)
            let height = CGImageGetHeight(spriteImage)
            
            
            let spriteData = UnsafeMutablePointer<Void>(calloc(width * height * 4, sizeof(GLubyte)))
            
            let spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width * 4,CGImageGetColorSpace(spriteImage), CGImageAlphaInfo.PremultipliedLast.rawValue);
            
            // 3
            CGContextDrawImage(spriteContext, CGRectMake(0, 0, CGFloat(width), CGFloat(height)), spriteImage)
            
            // 4
            glGenTextures(1, &texture)
            glBindTexture(GLenum(GL_TEXTURE_2D), texture)
            glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MIN_FILTER), GLint(GL_NEAREST))
            
            glTexImage2D(GLenum(GL_TEXTURE_2D), 0, GL_RGBA, GLsizei(width), GLsizei(height), 0, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), spriteData)
            GLenum(GL_UNSIGNED_BYTE), spriteData)
                    
            free(spriteData)
            render()
        }
    

    还有着色器相关的代码如下:
    1.顶点着色器

    attribute vec4 position;
    attribute vec2 texCoordIn;
    
    varying vec2 textureOut;
    
    void main() {
        textureOut = vec2(texCoordIn.x, 1.0 - texCoordIn.y);
        gl_Position = position;
    }
    

    2.像素着色器

    precision mediump float;
    uniform sampler2D textureImage;
    varying mediump vec2 textureOut;
    
    void main() {
        mediump vec4 RGBA;
        RGBA = texture2D(textureImage, textureOut);
        gl_FragColor = RGBA ;
    }
    

    以上代码就是通过一个集成自UIView的类,然后接收下外部传进来的CGImage,之后通过OpenGL转换成纹理,渲染到UIView上面,然后再将UIView添加到合适的地方就可以了.
    本人也是刚刚接触OpenGL,以上代码也是在网上东拼西凑弄出来的,这段代码亲测可用,很多坑都已经避免了,如果有什么疑惑的欢迎交流.
    遗留的问题: 这段代码我是用来显示接收到的视频帧数据的,因为openGL使用的是GPU,这样效率更高,但是为了更一步提高效率,应该用glTexImage2D()预渲染一个空纹理 ,然后以后用glSubTexImage2D()更新纹理.不过我这样使用的时候发现后面使用glSubTexImage2D()显示不了新的纹理,直接黑屏了,有哪位大神指导其中缘由的还望不吝赐教

    相关文章

      网友评论

        本文标题:使用OpenGL显示UIImage

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