当前位置: 首页 > article >正文

OpenGL ES 01 渲染一个四边形

项目架构

着色器封装

vertex

#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

#version 300 es

precision mediump float;

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

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

着色器类封装

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

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

最后效果


http://www.kler.cn/a/445677.html

相关文章:

  • 基于字节大模型的论文翻译(含免费源码)
  • 深入解析MySQL Explain关键字:字段意义及调优策略
  • js html转pdf
  • 内容与资讯API优质清单
  • ​在VMware虚拟机上设置Ubuntu与主机共享文件夹​
  • prometheus 搭建监控
  • [Unity]【图形渲染】【游戏开发】Shader数学基础4-更多矢量运算
  • PC寄存器(Program Counter Register)jvm
  • 2024年云计算的发展趋势如何?
  • 【图像处理lec7】图像恢复、去噪
  • SSM 框架结合 Vue 实现电脑测评系统:助力用户明智选择
  • 在M系列芯片的Mac上使用Uniapp开发的依赖安装指南
  • 裸金属服务器的作用都有哪些?
  • GitHub年度报告发布!Python首次超越JavaScript
  • 高校教师成果管理小程序的设计与实现springboot+论文源码调试讲解
  • 全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(多分支结构)
  • 在VBA中结合正则表达式和查找功能给文档添加交叉连接
  • css 动画实现从中间到两边亮度逐渐变暗的流水灯效果
  • DNS 服务器是什么?有什么作用
  • MQTT入门:在Spring Boot中建立连接及测试
  • 面试题整理10----k8s集群架构是什么
  • 数据库管理系统——数据库设计
  • 【Linux】基础IO------理解文件系统(inode)
  • Java 面经之 Kafka
  • MATLAB常用颜色RGB汇总
  • 低空无人机产教融合技术详解