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))
    }
}

最后效果

相关推荐
若水无华2 天前
fiddler 配置ios手机代理调试
ios·智能手机·fiddler
Aress"2 天前
【ios越狱包安装失败?uniapp导出ipa文件如何安装到苹果手机】苹果IOS直接安装IPA文件
ios·uni-app·ipa安装
Jouzzy2 天前
【iOS安全】Dopamine越狱 iPhone X iOS 16.6 (20G75) | 解决Jailbreak failed with error
安全·ios·iphone
瓜子三百克2 天前
采用sherpa-onnx 实现 ios语音唤起的调研
macos·ios·cocoa
左钦杨2 天前
IOS CSS3 right transformX 动画卡顿 回弹
前端·ios·css3
努力成为包租婆2 天前
SDK does not contain ‘libarclite‘ at the path
ios
安和昂3 天前
【iOS】Tagged Pointer
macos·ios·cocoa
I烟雨云渊T3 天前
iOS 阅后即焚功能的实现
macos·ios·cocoa
struggle20253 天前
适用于 iOS 的 开源Ultralytics YOLO:应用程序和 Swift 软件包,用于在您自己的 iOS 应用程序中运行 YOLO
yolo·ios·开源·app·swift
Unlimitedz3 天前
iOS视频编码详细步骤(视频编码器,基于 VideoToolbox,支持硬件编码 H264/H265)
ios·音视频