OpenGL ES 01 渲染一个四边形

项目架构

着色器封装

vertex

cpp 复制代码
#version 300 es
// 接收顶点数据
layout (location = 0) in vec3 aPos; // 位置变量的属性位置值为0
layout (location = 1) in vec4 aColors; // 位置变量的属性位置值为1
out vec4 vertexColor; // 为片段着色器指定一个颜色输出

void main() {
    gl_Position = vec4(aPos, 1.0); // 设置顶点位置
    vertexColor = aColors; // 设置顶点颜色为红色
}

fragment

cpp 复制代码
#version 300 es

precision mediump float;

in vec4 vertexColor; // 从顶点着色器传递过来的输入变量
out vec4 FragColor; // 输出到帧缓冲的颜色

void main() {
    FragColor = vertexColor; // 设置片段颜色
}

着色器类封装

Swift 复制代码
import UIKit

class Shader: NSObject {
    var shaderProgram: GLuint = 0
    
    private func loadShaderSource(from file: String) -> String? {
        guard let path = Bundle.main.path(forResource: file, ofType: "glsl") else {
            print("Failed to find shader file: \(file)")
            return nil
        }
        do {
            let source = try String(contentsOfFile: path, encoding: .utf8)
            return source
        } catch {
            print("Failed to load shader file: \(file), error: \(error)")
            return nil
        }
    }
    
    func begin() {
        glUseProgram(shaderProgram)
    }
    
    func compileShader(vert: String, frag: String) {
        // 读取着色器源代码
        guard let vertexSource = loadShaderSource(from: vert),
              let fragmentSource = loadShaderSource(from: frag) else {
            return
        }
        
        // 打印着色器源代码
        print("Vertex Shader Source:\n\(vertexSource)")
        print("Fragment Shader Source:\n\(fragmentSource)")
        
        // 创建着色器程序
        let vertexShader = glCreateShader(GLenum(GL_VERTEX_SHADER))
        let fragmentShader = glCreateShader(GLenum(GL_FRAGMENT_SHADER))
        
        // 将着色器源码附加到着色器对象上
        vertexSource.withCString { ptr in
            var p: UnsafePointer<GLchar>? = UnsafePointer<GLchar>(ptr)
            glShaderSource(vertexShader, 1, &p, nil)
        }
        fragmentSource.withCString { ptr in
            var p: UnsafePointer<GLchar>? = UnsafePointer<GLchar>(ptr)
            glShaderSource(fragmentShader, 1, &p, nil)
        }
        
        // 编译顶点着色器
        glCompileShader(vertexShader)
        // 检查编译错误
        var status: GLint = 0
        glGetShaderiv(vertexShader, GLenum(GL_COMPILE_STATUS), &status)
        if status == GL_FALSE {
            var logLength: GLint = 0
            glGetShaderiv(vertexShader, GLenum(GL_INFO_LOG_LENGTH), &logLength)
            
            // Allocate buffer with an extra byte for the null terminator
            let bufferLength = Int(logLength) + 1
            var log = [GLchar](repeating: 0, count: bufferLength)
            
            // Get the shader info log
            glGetShaderInfoLog(vertexShader, logLength, nil, &log)
            
            
            // Convert the buffer to a Swift string
            if let logString = String(validatingUTF8: log) {
                print("编译 顶点着色器 error: \(logString)")
            } else {
                print("编译 顶点着色器 error: Failed to retrieve log.")
            }
            return
        }

        // 编译片元着色器
        glCompileShader(fragmentShader)
        // 检查编译错误
        glGetShaderiv(fragmentShader, GLenum(GL_COMPILE_STATUS), &status)
        if status == GL_FALSE {
            var logLength: GLint = 0
            glGetShaderiv(fragmentShader, GLenum(GL_INFO_LOG_LENGTH), &logLength)
            
            // Allocate buffer with an extra byte for the null terminator
            let bufferLength = Int(logLength) + 1
            var log = [GLchar](repeating: 0, count: bufferLength)
            
            // Get the shader info log
            glGetShaderInfoLog(fragmentShader, logLength, nil, &log)
            
            // Convert the buffer to a Swift string
            if let logString = String(validatingUTF8: log) {
                print("编译 片元着色器 error: \(logString)")
            } else {
                print("编译 片元着色器 error: Failed to retrieve log.")
            }
            return
        }
        
        // 创建程序对象并链接着色器
        shaderProgram = glCreateProgram()
        glAttachShader(shaderProgram, vertexShader)
        glAttachShader(shaderProgram, fragmentShader)
        glLinkProgram(shaderProgram)
        var linkStatus: GLint = 0
               //获取链接状态
               glGetProgramiv(shaderProgram, GLenum(GL_LINK_STATUS), &linkStatus)
               if linkStatus == GL_FALSE {
                   NSLog("link error")
                   let message = UnsafeMutablePointer<GLchar>.allocate(capacity: 512)
                   glGetProgramInfoLog(shaderProgram, GLsizei(MemoryLayout<GLchar>.size * 512), nil, message)
                   let str = String(utf8String: message)
                   print("error = \(str ?? "没获取到错误信息")")
                   return
               } else {
                     NSLog("link success")
               }

        // 删除着色器对象,因为它们已经链接到程序对象中
        glDeleteShader(vertexShader)
        glDeleteShader(fragmentShader)
    }
}

ViewController

Swift 复制代码
import UIKit
import GLKit

/**
 渲染一个四边形
 */
class ViewController: UIViewController {
    
    var eaglLayer: CAEAGLLayer!
    var myContext: EAGLContext!
    var shader = Shader()
    var vao = GLuint()
    var renderBuffer = GLuint()
    var frameBuffer = GLuint()
    var fbo = GLuint()
    var fboTexture = GLuint()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 设置渲染显示区域
        setupLayer()
        // 初始化上下文
        setupContext()
        // 设置帧缓冲区
        setupRenderBuffers()
        setupFrameBuffer()
        // 准备着色器
        prepareShader()
        prepareVAOAndVBO()
        renderLayer()
    }
    
    func setupLayer() {
        eaglLayer = CAEAGLLayer()
        eaglLayer.frame = view.frame
        eaglLayer.isOpaque = true
        view.layer.addSublayer(eaglLayer)
    }
    
    func setupContext() {
        if let context = EAGLContext(api: .openGLES3) {
            EAGLContext.setCurrent(context)
            myContext = context
            print("Create context success")
        } else {
            print("Create context failed!")
        }
    }
    
    // 生成和绑定渲染缓冲区对象,为渲染缓冲区分配存储空间,生成和绑定帧缓冲区对象,并将渲染缓冲区附加到帧缓冲区的颜色附件点
    func setupRenderBuffers() {
        //生成一个渲染缓冲区对象,并将其ID存储在,colorRenderBuffer变量中。
        glGenRenderbuffers(1, &renderBuffer)
        //绑定生成的渲染缓冲区对象,使其成为当前的渲染缓冲区
        glBindRenderbuffer(GLenum(GL_RENDERBUFFER), renderBuffer)
       
    }
    
    func setupFrameBuffer() {
        // 生成一个帧缓冲区对象,并将其ID存储在frameBuffer变量中
        glGenFramebuffers(1, &frameBuffer)
        // 将frameBuffer绑定到GL_FRAMEBUFFER目标上
        glBindFramebuffer(GLenum(GL_FRAMEBUFFER), frameBuffer)
        // 将渲染缓冲区附加到帧缓冲区的颜色附件点。
        glFramebufferRenderbuffer(GLenum(GL_FRAMEBUFFER), GLenum(GL_COLOR_ATTACHMENT0), GLenum(GL_RENDERBUFFER), renderBuffer)
        //方法为当前绑定的渲染缓冲区分配存储空间,并将其与 CAEAGLLayer 关联。
        myContext.renderbufferStorage(Int(GL_RENDERBUFFER), from: eaglLayer)
    }
    func prepareShader() {
        shader.compileShader(vert: "vertex", frag: "fragment")
    }
    
    func prepareVAOAndVBO() {
        let positions: [GLfloat] = [
            -0.5, -0.5, 0.0,  // 左下角
             0.5, -0.5, 0.0,  // 右下角
             -0.5,  0.5, 0.0,   // 左上角
             0.5,  0.5, 0.0   // 左上角
        ]
        
        let colors: [GLfloat] = [
            1.0, 0.2, 0.2, 1.0,
            0.5, 1.0, 0.2, 1.0,
            0.5, 0.5, 1.0, 1.0
        ]
        
        let indices: [GLubyte] = [
            0, 1, 2,3
        ]

        var positionVBO = GLuint()
        glGenBuffers(1, &positionVBO)
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), positionVBO)
        glBufferData(GLenum(GL_ARRAY_BUFFER), MemoryLayout<GLfloat>.size * positions.count, positions, GLenum(GL_STATIC_DRAW))
        
        var colorVBO = GLuint()
        glGenBuffers(1, &colorVBO)
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), colorVBO)
        glBufferData(GLenum(GL_ARRAY_BUFFER), MemoryLayout<GLfloat>.size * colors.count, colors, GLenum(GL_STATIC_DRAW))

        var ebo = GLuint()
        glGenBuffers(1, &ebo)
        glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), ebo)
        glBufferData(GLenum(GL_ELEMENT_ARRAY_BUFFER), MemoryLayout<GLubyte>.size * indices.count, indices, GLenum(GL_STATIC_DRAW))
        
        glGenVertexArrays(1, &vao)
        glBindVertexArray(vao)
        
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), positionVBO)
        glVertexAttribPointer(0, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(MemoryLayout<GLfloat>.size * 3), nil)
        glEnableVertexAttribArray(0)
        
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), colorVBO)
        glVertexAttribPointer(1, 4, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(MemoryLayout<GLfloat>.size * 4), nil)
        glEnableVertexAttribArray(1)

        
        glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), ebo)
        
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), 0)
        glBindVertexArray(0)
    }
    
    func renderLayer() {
        glClearColor(0.0, 0.0, 0.0, 1.0)
        glClear(GLbitfield(GL_COLOR_BUFFER_BIT))

        glViewport(GLint(0), GLint(0), GLsizei(view.frame.size.width), GLsizei(view.frame.size.height))
        
        shader.begin()

        // 解绑FBO
        glBindFramebuffer(GLenum(GL_FRAMEBUFFER), 0)
        
        // 渲染FBO内容到屏幕
        glClearColor(0.0, 0.0, 0.0, 1.0)
        glClear(GLbitfield(GL_COLOR_BUFFER_BIT))
        
        // 绑定默认帧缓冲区
        glBindFramebuffer(GLenum(GL_FRAMEBUFFER), frameBuffer)
        
        // 使用FBO中的纹理进行后处理或直接绘制到屏幕
        // 这里你需要一个简单的着色器来绘制纹理到屏幕
        shader.begin()
        
        // 绑定FBO纹理
        glBindTexture(GLenum(GL_TEXTURE_2D), fboTexture)
        
        // 绘制一个全屏四边形,将FBO纹理渲染到屏幕上
        // 你需要设置适当的顶点和纹理坐标
        // 这里假设你已经有一个VAO和VBO来绘制全屏四边形
        
        glBindVertexArray(vao)
        glDrawElements(GLenum(GL_TRIANGLE_STRIP), 4, GLenum(GL_UNSIGNED_BYTE), nil)
        glBindVertexArray(0)
        
        myContext.presentRenderbuffer(Int(GL_RENDERBUFFER))
    }
}

最后效果

相关推荐
请叫我飞哥@3 小时前
iOS在项目中设置 Dev、Staging 和 Prod 三个不同的环境
ios·xcode·swift
shankss14 小时前
网页跳转App,Universal Links(iOS)和 App Links(Android) 如何设置
android·flutter·ios
あjdc21 小时前
iOS Delegate模式
网络协议·macos·ios·objective-c·cocoa
ii_best21 小时前
如何写一个完整的按键精灵 iOS 脚本示例
ios
ii_best21 小时前
按键精灵苹果 iOS 脚本工具的基本编写方法
ios
翀哥~1 天前
ios系统冷知识
ios
凌鲨2 天前
IOS学习路线图
学习·ios·cocoa
陈佬昔没带相机2 天前
对比Swift和ArkTS,也许以后可以这样做开发
前端·ios·harmonyos
Cedric_Anik2 天前
iOS渲染概述
ui·ios·swift