OpenGL ES 3.0 入门之画一个三角形

原文地址: https://lm1024.xyz/archives/93

前面对 OpenGL ES3.0做了个基本的介绍,这节我们尝试着来写一个OpenGL ES 的 "hello word"程序 --- 画一个三角形

效果

OpenGL ES 3.0 入门之画一个三角形_第1张图片
image

EAGLContext 和 CAEAGLLayer

  • EAGLContext对象是管理OpenGL ES渲染上下文,若想使用OpenGL ES 进行绘制工作,则必须一个上下文对象。该对象管理者openGL ES 绘制使用的上下文对象

  • CAEAGLLayer 将OpenGL ES 渲染结果显示的屏幕上的layer (TODO:开一篇单独介绍)

流程

输入三角形顶点和色值 --> openGL 处理 --> CAEAGLLayer 显示

初始化 OpenGL 环境

    var context: EAGLContext? = EAGLContext(api: .openGLES3)
    var glLayer: CAEAGLLayer { return layer as! CAEAGLLayer }
    open override class var layerClass: Swift.AnyClass {
        return CAEAGLLayer.self
    }
    private func setupLlayer() {
        glLayer.opacity = 1.0
        glLayer.drawableProperties = [
            kEAGLDrawablePropertyRetainedBacking: true,
            kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8,
        ]
    }
    
    private func setupContext() {
        if context == nil {
            context = EAGLContext(api: .openGLES2)
        }
        assert(context != nil && EAGLContext.setCurrent(context), "初始化环境失败")
    }

初始化 frameBuffer 和 renderBuffer

为什么要用两个buffer,这个跟OpenGL 内部优化有关系(TODO:另外开一篇博客,写好了贴链接)
frameBuffer 负责管理buffer缓冲区的和显示buffer切换。
renderBuffer 负责将渲染结果推到CAEAGLLayer显示。

//初始化 frameBuff
    private func setupFrameBuffer() {
        deleteFrameBuff()
        glGenFramebuffers(1, &frameBuffer)
        glBindFramebuffer(GLenum(GL_FRAMEBUFFER), frameBuffer)
        glFramebufferRenderbuffer(
            GLenum(GL_FRAMEBUFFER),
            GLenum(GL_COLOR_ATTACHMENT0),
            GLenum(GL_RENDERBUFFER),
            renderBuffer)
    }
    private func deleteFrameBuff() {
        if frameBuffer != 0 {
            glDeleteBuffers(1, &frameBuffer)
            frameBuffer = 0
        }
    }
//初始化 renderBuffer
   private func setupRenderBuffer() {
        deleteRenderBuffer()
        glGenRenderbuffers(1, &renderBuffer)
        glBindRenderbuffer(GLenum(GL_RENDERBUFFER), renderBuffer)
        context?.renderbufferStorage(Int(GL_RENDERBUFFER), from: glLayer)
    }
  private func deleteRenderBuffer() {
        if renderBuffer != 0 {
            glDeleteBuffers(1, &renderBuffer)
            renderBuffer = 0
        }
    }
  • glGenFramebuffers(_ n: GLsizei, _ framebuffers: UnsafeMutablePointer!) 申请 n 个 frameBuffer 并返回申请的 frameBuffer的id列表
  • glBindRenderbuffer(_ target: GLenum, _ renderbuffer: GLuint) 将申请到的 frameBuffer 绑定到 OpenGL ES 中
  • glFramebufferRenderbuffer 将frameBuffer 和 renderbuffer关联
  • glGenRenderbuffers:申请n个renderBuffer
  • glBindRenderbuffer:将申请到的 renderBuffer 绑定到 OpenGL ES 中
  • glDeleteBuffers: 删除指定的buffer

检查 frameBuffer 状态

    private func checkFrameBuffer() throws -> Bool {
        let status: GLenum = glCheckFramebufferStatus(GLenum(GL_FRAMEBUFFER))
        var result: Bool = false
        var errorMessage: String = ""
        print("\(GLenum(GL_FRAMEBUFFER_UNSUPPORTED))," + "\(GLenum(GL_FRAMEBUFFER_COMPLETE))," + "\(GLenum(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT))," + "\(GLenum(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS)),")
        switch status {
        case GLenum(GL_FRAMEBUFFER_UNSUPPORTED):
            errorMessage = "framebuffer不支持该格式"
            result = false
        case GLenum(GL_FRAMEBUFFER_COMPLETE):
            print("frame buffer 创建成功")
            result = true
        case GLenum(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT):
            errorMessage = "Framebuffer不完整 缺失组件"
            result = false
        case GLenum(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS):
            errorMessage = "Framebuffer 不完整, 附加图片必须要指定大小"
            result = false
        default:
            errorMessage = "未知错误"
            result = false
        }
        if errorMessage.isEmpty == false {
            let error: NSError = NSError(domain: "com.colin.error", code: Int(status), userInfo: ["errorMSG": errorMessage])
            throw error
        }
        return result
    }

编写shader脚本

  • 顶点着色器脚本
attribute vec4 position;
attribute vec4 color;

varying vec4 colorVarying;

void main(void) {
    colorVarying = color;
    gl_Position = position;
}
  • 片段着色器脚本
varying lowp vec4 colorVarying;
void main(void) {
    gl_FragColor = colorVarying;
}

编译、链接shader脚本

open class ShaderCompiler{
    open class func loadShader(_ vertPath:String,fragPath:String)throws -> GLuint {
        var verShader:GLuint = 0,fragShader:GLuint = 0
        do {
            verShader = try self.compileShader(vertPath, type:GLenum(GL_VERTEX_SHADER))
            fragShader = try self.compileShader(fragPath, type: GLenum(GL_FRAGMENT_SHADER))
            //创建着色程序
            let program:GLuint = glCreateProgram()
            //装配 着色程序
            glAttachShader(program, verShader)
            glAttachShader(program, fragShader)
            glLinkProgram(program)
            programLog(GLenum(GL_LINK_STATUS), program: program)
            return program
        } catch let error {
            print(error)
            throw error
        }

    }
    open class func compileShader(_ shaderPath:String,type:GLenum)throws -> GLuint{
        do {
            let shaderStr:String = try String.init(contentsOfFile: shaderPath)

            let shaderStringUTF8 = shaderStr.cString(using:.utf8)
            var shaderStringUTF8Pointer = UnsafePointer(shaderStringUTF8)

            var shaderStringLength: GLint = GLint(Int32(shaderStr.count))
            let shaderHandle:GLuint = glCreateShader(type)
            glShaderSource(shaderHandle, 1,&shaderStringUTF8Pointer, &shaderStringLength)
            glCompileShader(shaderHandle)
            shaderLog(GLenum(GL_COMPILE_STATUS), shader: shaderHandle)
            return shaderHandle
        } catch let error {
            print(error)
            throw error
        }
    }
    open class func programLog(_ statue:GLenum,program:GLuint) {
        var linkSucc:GLint = 0
        glGetProgramiv(program, GLenum(statue), &linkSucc)
        if linkSucc == GL_FALSE
        {
            var message:Array = Array.init(repeating: 0, count: 256)
            glGetProgramInfoLog(program, GLsizei(MemoryLayout.stride * message.count), nil, &message[0])
            let msg = String.init(cString: message)
            print("gl link program fail  msg = \(msg)")
            exit(1)
        } else {
            print("link ok")
        }
    }
    open class func shaderLog(_ statue:GLenum,shader:GLuint){
        var status:GLint = 0
        glGetShaderiv(shader, statue, &status)
        if status == GL_FALSE {
            var message:Array = Array.init(repeating: 0, count: 256)
            glGetShaderInfoLog(shader, GLsizei(MemoryLayout.stride * message.count), nil, &message[0])
            let msg = String.init(cString: message)
            print("glGetShaderiv shaderIngoLog type = \(statue)  shader = \(shader) "+"  \(msg)")
            exit(1)
        } else {
            print("\(statue)  \(shader) shader 操作成功")
        }
    }
}

初始化顶点坐标

//前三位为顶点坐标  后四位为顶点色值rgba
   var vertices: [GLfloat] = [
        -0.5,  0.5,  1.0,   1, 0, 0, 1,
        -0.5, -0.5,  1.0,   0, 1, 0, 1,
         0.5, -0.5,  1.0,   0, 0, 1, 1,
    ]

将顶点坐标推给OpenGL

    private func setupVBOs() {
        var vertexBuffer: GLuint = 0
        glGenBuffers(1, &vertexBuffer)
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), vertexBuffer)
        glBufferData(GLenum(GL_ARRAY_BUFFER),
                     MemoryLayout.size * vertices.count,
                     vertices,
                     GLenum(GL_STATIC_DRAW))
    }

渲染

    private func render() {
        glBindFramebuffer(GLenum(GL_FRAMEBUFFER), frameBuffer)
        glBindRenderbuffer(GLenum(GL_RENDERBUFFER), renderBuffer)
        glClearColor(0, 1, 1, 1)
        glClear(GLbitfield(GL_COLOR_BUFFER_BIT))
        glViewport(0, 0, GLsizei(frame.width),GLsizei(frame.height))
        glVertexAttribPointer(GLuint(self.positon),
                              3,
                              GLenum(GL_FLOAT),
                              GLboolean(GL_FALSE),
                              GLsizei(MemoryLayout.size * 7),
                              nil)

        let offset: GLsizeiptr = MemoryLayout.size * 3
        glVertexAttribPointer(GLuint(self.color),
                              4,
                              GLenum(GL_FLOAT),
                              GLboolean(GL_FALSE),
                              GLsizei(MemoryLayout.size * 7),
                              UnsafeRawPointer(bitPattern: offset))

        glDrawArrays(GLenum(GL_TRIANGLES), 0, 3)
        context?.presentRenderbuffer(Int(GL_RENDERBUFFER))
    }

写在最后

写这篇博客的时候刚刚开始学 openGLES,在写这个博客的demo时候,各种查资料零零散散 费半日之力才成。在写的过程中出现了许多问题主要还是openGL es api方面的熟悉,api中各种参数代表的含义还是有多多的不了解。通过写这个demo我主要学习到一下几点

  • 所有的buffer 都需要有 申请-->绑定-->使用的一个过程
  • shader的主要流程有, 创建-->加载-->编译-->检测编译状态-->装配到着色程序-->着色程序进行链接-->使用着色程序的过程
  • 顶点buffer必须有glGenBuffers --> glBindBuffer --> glBufferData --> glEnableVertexAttribArray --> glVertexAttribPointer 流程。

demo地址

你可能感兴趣的:(OpenGL ES 3.0 入门之画一个三角形)