Home > AI > Uncategorized

Opengl – Rectangle & Line

这个程序是学习github上的代码,我重新写了遍(这个学习方法挺有效),做了注解,并在此文与大家分享调试中遇到的注意事项,最后几个点花了我一个上午。

>>>框架

GLContext,

GLBaseViewController,

GLProgram(也有人命名为shaderProgram,这边统一GL打头,好看些

GLDrawProtocol

 

GLContext.swft

import Foundation
import OpenGLES
import GLKit

class GLContext {
    
    enum GLContextError: Error {
        case notSupportES3
    }
    
    var context: EAGLContext?
    
    fileprivate(set) var maxVertexAttrib: Int = 9
    fileprivate(set) var maxTextureUnits: Int = 0
    fileprivate(set) var maxTextureSize: Int = 0
    
    
    
    init() throws {
        context = EAGLContext(api: .openGLES3)
        if context ==  nil {
            throw GLContextError.notSupportES3
        }
        
        checkEnviroment()
    }
    
    
    func activate(){
        EAGLContext.setCurrent(context)
    }
    
    
    func deactivate(){
        if EAGLContext.current() == context {
            EAGLContext.setCurrent(nil)
        }
    }
    
    
    func checkEnviroment(){
        //指针是GLint, 编号是GLuint
        //unsafeMutablePointer 是可变指针,类似于inout,所以要&
        //unsafePointer不可变,所以不要&
        var n: GLint = 0
        
        //attribute 修饰的变量数目有限,比如最大0个,glGetIntegerv查询
        glGetIntegerv(GLenum(GL_MAX_VERTEX_ATTRIBS), &n)
        self.maxVertexAttrib = Int(n)
        
        glGetIntegerv(GLenum(GL_MAX_TEXTURE_UNITS), &n)
        self.maxTextureUnits = Int(n)
        
        glGetIntegerv(GLenum(GL_MAX_TEXTURE_SIZE), &n)
        self.maxTextureSize = Int(n)
        
        outputEnviroment()
    }
    
    
    func outputEnviroment(){
        var description = ""
        
        description += "\n"
        description += "\n-Max VertexAttrib: \(self.maxVertexAttrib)"
        description += "\n-Max TextureUnits: \(self.maxTextureUnits)"
        description += "\n-Max TextureSize: \(self.maxTextureSize)"
        
        print(description)
    }
}

 

 

GLBaseViewController.swift

import UIKit
import GLKit

class GLBaseViewController: GLKViewController {

    var ctx: GLContext!
    
    
    override func viewDidLoad() {
        super.viewDidLoad()

        do {
            ctx = try GLContext()
        } catch GLContext.GLContextError.notSupportES3 {
            print(GLNote.failed.rawValue, "not support OpenglES3")
        } catch {
            print(GLNote.failed.rawValue, "context")
        }
        
        let view = self.view as! GLKView
        view.context = ctx.context!
        ctx.activate()
        
        view.drawableColorFormat = .RGBA8888
        view.drawableDepthFormat = .format24
        view.drawableMultisample = .multisample4X
        
        self.preferredFramesPerSecond = 60
        self.pauseOnWillResignActive = true
        self.resumeOnDidBecomeActive = true
        
        //glEnable should be placed here, in glkView will occur error
        glEnable(GLenum(GL_DEPTH_TEST))
        //GL_DEPTH_TEST: check if there are pixels in front of the current pixel, if exists, then current pixel will not be drew. In other words, opengl always draw the uppermost pixel.
        
        //GL_BLEND: use blend map, you need close GL_DEPTH_TEST, open GL_BLEND, set glColor4f, glBlendFunc
    }

    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    

    

}

 

GLProgram.swift

//
//  GLProgram.swift
//  stNightGirl
//
//  Created by sj on 05/05/2018.
//  Copyright © 2018 sj. All rights reserved.
//

import Foundation
import OpenGLES

enum GLNote: String {
    case failed = "[OpenGL Failure]"
}

class GLProgram {
    
    //着色器文件
    var vertexFile: String
    var fragmentFile: String
    
    var program: GLuint = 0 //起始编号为0
    var vertexShader: GLuint = 0
    var fragmentShader: GLuint = 0
    
    
    
    init(vertexFileName: String, fragmentFileName: String){
        self.vertexFile = vertexFileName
        self.fragmentFile = fragmentFileName
    }
    
    
    deinit {
        glDeleteProgram(program)
    }
    
    
    //MARK: program 4
    func use(){
        if program != 0 {
            glUseProgram(program)
        }
    }
    
    
    func validate() -> Bool {
        glValidateProgram(program)
        
        var status: GLint = 0
        glGetProgramiv(program, GLenum(GL_VALIDATE_STATUS), &status)
        if status == 0 {
            outputProgramError()
            return false
        }
        
        return true
    }
    
    
    fileprivate func linkProgram() -> Bool {
        
        program = glCreateProgram()
        
        glAttachShader(program, vertexShader)
        glAttachShader(program, fragmentShader)
        glLinkProgram(program)
        
        var status: GLint = 0
        glGetProgramiv(program, GLenum(GL_LINK_STATUS), &status)
        if status == 0 {
            outputProgramError()
            glDeleteProgram(program)
            return false
        }
        
        glDetachShader(program, vertexShader)
        glDetachShader(program, fragmentShader)
        glDeleteShader(vertexShader)
        glDeleteShader(fragmentShader)
        
        return true
    }
    
    
    fileprivate func outputProgramError(){
        var logLength: GLint = 0
        
        glGetProgramiv(program, GLenum(GL_INFO_LOG_LENGTH), &logLength)
        if logLength > 0{
            var logChar: [GLchar] = [GLchar](repeating: 0, count: Int(logLength))
            glGetProgramInfoLog(program, GLsizei(logLength), &logLength, &logChar)
            
            let logString = String(cString: logChar, encoding: String.Encoding.utf8)
            print("Error in Program: \(logString)")
        }
    }
    
    func getUniformLocation(name: String) -> Int32 {
        return glGetUniformLocation(program, name.cString(using: .utf8))
    }
    
    //MARK: shader 3
    
    fileprivate func compileShader() -> Bool {
    
        if !compileShader(shader: &vertexShader, type: GLenum(GL_VERTEX_SHADER), fileName: self.vertexFile) {
            
            print(GLNote.failed.rawValue, "compile vertex shader")
            
            return false
        }
        
        if !compileShader(shader: &fragmentShader, type: GLenum(GL_FRAGMENT_SHADER), fileName: fragmentFile){
            
            print(GLNote.failed.rawValue, "compile fragment shader")
            
            return false
        }
        
        return true
        
    }
    
    fileprivate func compileShader(shader: inout GLuint, type: GLenum, fileName: String) -> Bool {
        // 1
        shader = glCreateShader(type)
        if shader == 0 {
            print(GLNote.failed.rawValue, "create shader fail, check context")
        }
        
        // 2
        var source: UnsafePointer<Int8>
        do {
            let url = Bundle.main.url(forResource: fileName, withExtension: nil)
            source = try NSString(contentsOf: url!, encoding: String.Encoding.utf8.rawValue).utf8String!
            var sourceChar: UnsafePointer<GLchar>? = UnsafePointer<GLchar>(source)
            glShaderSource(shader, 1, &sourceChar, nil)
        } catch {
            print(GLNote.failed.rawValue, "read shader src \(fileName)", error)
        }

        // 3
        glCompileShader(shader)
        
        // 4
        var status: GLint = 0
        glGetShaderiv(shader, GLenum(GL_COMPILE_STATUS), &status) //1 success, 0 fail
        if status == 0 {
            outputShaderError(shader: shader, fileName: fileName)
            glDeleteShader(shader)
            return false
        }
    
        return true
    }
    
 
    
    fileprivate func outputShaderError(shader: GLuint, fileName: String){
        
        var logLength: GLint = 0
        glGetShaderiv(shader , GLenum(GL_INFO_LOG_LENGTH), &logLength)

        if logLength > 0 {
            let logChar: UnsafeMutablePointer<GLchar> = UnsafeMutablePointer<GLchar>.allocate(capacity: Int(logLength))
            logChar.initialize(to: 0)
            
            glGetShaderInfoLog(shader, GLsizei(logLength), &logLength, logChar)
            
            let logString = String(cString: logChar, encoding: String.Encoding.utf8)
            print(GLNote.failed.rawValue, "shader\(fileName), \(String(describing: logString))")
            
            logChar.deinitialize()
            logChar.deallocate(capacity: Int(logLength))
        }
    }
    
    
    @discardableResult
    func loadShaders() -> Bool {
        if !compileShader() {
            return false
        }
        
        if !linkProgram() {
            return false
        }
        
        return true
    }
    
  
    
}

 

 

GLDrawProtocol.swift

import Foundation


protocol GLDrawProtocol {
    var shaderProgram: GLProgram { get set }
    
    func loadShader()
    func bindVertexData()
    func draw()
}

 

以上能成功读shader,朋友们直接拿去用即可,有什么可以email我。

下面是测试文件,Triangle

Triangle.swift

import Foundation
import GLKit

class Triangle: GLDrawProtocol {
    var shaderProgram: GLProgram

    var vertexArray: GLuint = 0
    var vertexBuffer: GLuint = 0
    var indexBuffer: GLuint = 0
    var vertices: [GLfloat] = [ //accept GLFloat & Float
        0.5, -0.5, 0,
        0.5, 0.5, 0,
        -0.5, 0.5, 0,
        -0.5, -0.5, 0 ]
    var indices: [GLuint] = [ //must be GLuint, Int no
        0, 1, 2,
        0, 2, 3 ]
    
    
    
    
    init() {
        shaderProgram = GLProgram(vertexFileName: "tri.vsh", fragmentFileName: "tri.fsh")
    }
    
    
    func loadShader() {
        shaderProgram.loadShaders()
    }
    
    
    func bindVertexData() {
        //产生vao,顶点数组对象,每个顶点有坐标/法线/颜色/纹理坐标信息
        //@param: 1st 数量,2nd 编号数组
        /*
        //多个vao
        var uu: [GLuint] = [GLuint](repeating: 0, count:19)
        glGenVertexArrays(19, &uu)
         
        //单个vao
        var pp: GLuint = 0
        glGenVertexArrays(1, &pp)
        */
        
        // 1 vao
        glGenVertexArrays(1, &vertexArray)
        glBindVertexArray(vertexArray)
        
        // 2 vbo
        glGenBuffers(1, &vertexBuffer)
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), vertexBuffer)
        let verticesBufferSize = vertices.count * MemoryLayout<GLfloat>.size
        glBufferData(GLenum(GL_ARRAY_BUFFER), verticesBufferSize, vertices, GLenum(GL_STATIC_DRAW)) //static,数据不变;dynamic,变;stream,每帧变
        
        // 3 veo
        glGenBuffers(1, &indexBuffer)
        glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), indexBuffer) //designate buffer type
        let indexBufferSize = indices.count * MemoryLayout<GLuint>.size
        glBufferData(GLenum(GL_ELEMENT_ARRAY_BUFFER), indexBufferSize, indices, GLenum(GL_STATIC_DRAW))
        
        // 4 enable position in shader
        let stride = 3 * MemoryLayout<GLfloat>.size
        
        //@param indx: number in shader, see tri.vsh -> layout (location = 0) in position
        //@param size: must be 1, 2, 3, or 4. The initial value is 4.
        glVertexAttribPointer(0, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(stride), UnsafeRawPointer(bitPattern: 0)) 
        glEnableVertexAttribArray(0)
        
        // 5
        glBindVertexArray(0) //release vao
    
    }
    
    
    func draw() {
        // 1 clear, in parent glkView
        
        // 2 have turn program or uniform will occur error
        shaderProgram.use()
        
        // 3 animate
        let offset: Int32 = shaderProgram.getUniformLocation(name: "positionOffset")
        let pos = generatePosition()
        glUniform3f(offset, pos.x, pos.y, 0)
        
        let customColor = shaderProgram.getUniformLocation(name: "customColor")
        let color = generateColors()
        glUniform4f(customColor, color.red, color.green, color.blue, 1.0)

        // 4
        glBindVertexArray(vertexArray)
        glDrawElements(GLenum(GL_TRIANGLES), 6, GLenum(GL_UNSIGNED_INT), UnsafeRawPointer(bitPattern: 0)) //according to your index order
        glBindVertexArray(0)

    }
    
}


extension Triangle {
    fileprivate func generateColors() -> (red: GLfloat, green: GLfloat, blue: GLfloat) {
        let t = CFAbsoluteTimeGetCurrent()
        let red = cos(t)/2 + 0.5
        let green = sin(t)/2 + 0.5
        let blue = cos(t)*sin(t)
    
        return (GLfloat(red), GLfloat(green), GLfloat(blue))
    }
    
    fileprivate func generatePosition() -> (x: GLfloat, y: GLfloat) {
        let t = CFAbsoluteTimeGetCurrent()
        let x = cos(t)/2 //圆的参数方程,半径为屏幕一半
        let y = sin(t)/2
        return (GLfloat(x), GLfloat(y))
    }
}

 

tri.vsh

#version 300 core

precision highp float;

layout (location = 0) in vec3 position;
uniform vec3 positionOffset;

void main() {
    gl_Position = vec4(position + positionOffset, 1);
}

 

tri.fsh

#version 300 core

precision highp float;

uniform vec4 customColor;
out vec4 color;

void main() {
    //color = vec4(1.0, 1.0, 1.0, 1.0);
    color = customColor;
}

 

Line.swift

import Foundation
import GLKit



class Line: GLDrawProtocol {
    
    var shaderProgram: GLProgram
    
    var vertexArray: GLuint = 0
    var vertexBuffer: GLuint = 0
    
    //method 1: pure array
    var v1: [GLfloat] = [
        -0.5, 0.5, 0,
        0.5, -0.5, 0
    ]
    
    //method 2: position
    struct Point {
        var x: GLfloat
        var y: GLfloat
        var z: GLfloat
    }
    var v2: [Point] = [
        Point(x: -0.5, y: 0.5, z: 0),
        Point(x: 0.5, y: -0.5, z: 0)
    ]
    
    //method 3:  multi attributes
    struct Vertex {
        var position = [GLfloat](repeating: 0, count: 3)
        //var texCoord = [Int](repeating: 0, count: 2)
    }
    var v3: [Vertex] = [
        Vertex(position: [-0.5, 0.5, 0]),
        Vertex(position: [0.5, -0.5, 0])
    ]
    
 

    
    init() {
        shaderProgram = GLProgram(vertexFileName: "line.vsh", fragmentFileName: "line.fsh")
    }
    
    func loadShader() {
        shaderProgram.loadShaders()
    }
    
    func bindVertexData() {
        // 1 vao
  
        glGenVertexArrays(1, &vertexArray)
        glBindVertexArray(vertexArray)
        
        
        // 2 vbo
        glGenBuffers(1, &vertexBuffer)
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), vertexBuffer)
        
        //m1
        //let bs1 = v1.count * MemoryLayout<GLfloat>.size
        //glBufferData(GLenum(GL_ARRAY_BUFFER), GLsizeiptr(bs1), v1, GLenum(GL_STATIC_DRAW))
        
        //m2
        let bs2 = v2.count * MemoryLayout<Point>.size
        glBufferData(GLenum(GL_ARRAY_BUFFER), bs2, v2, GLenum(GL_STATIC_DRAW))
        
        //m3 - no
        //let bs3 = v3.count * MemoryLayout<Vertex>.size
        //glBufferData(GLenum(GL_ARRAY_BUFFER), GLsizeiptr(bs3), v3, GLenum(GL_STATIC_DRAW))
        
        // 3
        //m1
        //glVertexAttribPointer(0, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(MemoryLayout<GLfloat>.size * 3), UnsafeRawPointer(bitPattern: 0))
        
        //m2
        glVertexAttribPointer(0, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(MemoryLayout<Point>.size), UnsafeRawPointer(bitPattern: 0))
        
        //m3 - no
        //glVertexAttribPointer(0, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(MemoryLayout<Vertex>.size), UnsafeRawPointer(bitPattern: 0))
        
        glEnableVertexAttribArray(0)
        
        glBindVertexArray(0)
        
    
    }
    
    
    func draw() {

        
        glLineWidth(10)
        shaderProgram.use()
        glBindVertexArray(vertexArray)
        glDrawArrays(GLenum(GL_LINES), 0, 2)
        glBindVertexArray(0)
       

        //GL_POINTS
        
        //GL_LINES
        //GL_LINE_LOOP
        //GL_LINE_STRIP
        
        //顶点为3的倍数,剩下的1或2个点不显示
        //GL_TRIANGLES
        //v1, v2, v3 -> v4, v5, v6
        //GL_TRIANGLE_STRIP,
        //2)偶数, T = [n-1, n-2, n]
        //3)奇数, T = [n-2, n-1, n]
        //GL_TRIANGLE_FAN 共享定顶点
        
    }
    
    
}

 

line.vsh

#version 300 core

precision highp float;

layout (location = 0) in vec3 position;

void main(){
    gl_Position = vec4(position, 1.0);
}

 

line.fsh

#version 300 core

precision highp float;

out vec4 color;

void main(){
    color = vec4(0.0f, 0.0f, 1.0f, 1.0f);
}

 

TriangleViewController.swift

import UIKit
import GLKit

class TriangleViewController: GLBaseViewController {

    var triangle: Triangle!
    var line: Line!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        triangle = Triangle()
        triangle.loadShader()
        triangle.bindVertexData()
    
        line = Line()
        line.loadShader()
        line.bindVertexData()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    

    override func glkView(_ view: GLKView, drawIn rect: CGRect) {
        glClearColor(0.5, 0.8, 0.9, 1)
        glClear(GLbitfield(GL_COLOR_BUFFER_BIT) | GLbitfield(GL_DEPTH_BUFFER_BIT))
        
        //opengl draw order is like stack
        //first triangle, then line
        //so line is infront of triangle
        
        line.draw()
        triangle.draw()
        
    }

}

 

 

Related posts:

Leave a Reply