使用OpenGL显示UIImage
2016-05-14 本文已影响775人
saiGo
使用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()显示不了新的纹理,直接黑屏了,有哪位大神指导其中缘由的还望不吝赐教