OpenGL ES 01 渲染一个四边形

news2024/12/20 1:47:14

项目架构

着色器封装

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.coloradmin.cn/o/2262448.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode二叉搜索树部分笔记

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 二叉搜索树 1. 二叉搜索树的最小绝对差2. 二叉搜索树中第 K 小的元素3. 验证二叉搜索树 1. 二叉搜索树的最小绝对差 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中…

推送本地仓库到远程git仓库

目录 推送本地仓库到远程git仓库1.1修改本地仓库用户名1.2 push 命令1.3远程分支查看 推送本地仓库到远程git仓库 删除之前的仓库中的所有内容&#xff0c;从新建库&#xff0c;同时创建一个 A.txt 文件 清空原有的远程仓库内容&#xff0c;重新创建一个新的仓库&#xff0c;…

暂停一下,给Next.js项目配置一下ESLint(Next+tailwind项目)

前提 之前开自己的GitHub项目&#xff0c;想着不是团队项目&#xff0c;偷懒没有配置eslint&#xff0c;后面发现还是不行。eslint的存在可以帮助我们规范代码格式&#xff0c;同时 ctrl s保存立即调整代码格式是真的很爽。 除此之外&#xff0c;团队使用eslint也是好处颇多…

基于微信小程序的小区疫情防控ssm+论文源码调试讲解

第2章 程序开发技术 2.1 Mysql数据库 为了更容易理解Mysql数据库&#xff0c;接下来就对其具备的主要特征进行描述。 &#xff08;1&#xff09;首选Mysql数据库也是为了节省开发资金&#xff0c;因为网络上对Mysql的源码都已进行了公开展示&#xff0c;开发者根据程序开发需…

Win11安装安卓子系统WSA

文章目录 简介一、启用Hyper-V二、安装WSA三、安装APKAPK商店参考文献 简介 WSA&#xff1a;Windows Subsystem For Android 一、启用Hyper-V 控制面板 → 程序和功能 → 启用或关闭 Windows 功能 → 勾选 Hyper-V 二、安装WSA 进入 Microsoft Store&#xff0c;下拉框改为 …

[面试题]--索引用了什么数据结构?有什么特点?

答&#xff1a;使用了B树&#xff1a; 时间复杂度&#xff1a;O(logN),可以有效控制树高 B树特点&#xff1a; 1.叶子节点之间有相互链接的作用&#xff0c;会指向下一个相近的兄弟节点。 MySQL在组织叶子节点使用的是双向链表 2.非叶子节点的值都保存在叶子节点当中 MySQL非叶…

Element plus 下拉框组件选中一个选项后显示的是 value 而不是 label

最近刚进行 Vue3 Element plus 项目实践&#xff0c;在进行表单二次封装的时候&#xff0c;表单元素 select 下拉框组件选中一个选项后显示的是 value 而不是 label&#xff0c;下面上代码&#xff1a; 原来的写法&#xff1a; <el-selectv-if"v.type select"…

bean创建源码

去字节面试&#xff0c;直接让人出门左拐&#xff1a;Bean 生命周期都不知道&#xff01; spring启动创建bean流程 下面就接上了 bean生命周期 doGetBean Object sharedInstance this.getSingleton(beanName); sharedInstance this.getSingleton(beanName, new ObjectF…

【C++】- 掌握STL List类:带你探索双向链表的魅力

文章目录 前言&#xff1a;一.list的介绍及使用1. list的介绍2. list的使用2.1 list的构造2.2 list iterator的使用2.3 list capacity2.4 list element access2.5 list modifiers2.6 list的迭代器失效 二.list的模拟实现1. list的节点2. list的成员变量3.list迭代器相关问题3.1…

泷羽sec学习打卡-brupsuite8伪造IP和爬虫审计

声明 学习视频来自B站UP主 泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都 与本人无关,切莫逾越法律红线,否则后果自负 关于brupsuite的那些事儿-Brup-FaskIP 伪造IP配置环境brupsuite导入配置1、扩展中先配置python环境2、安…

挑战一个月基本掌握C++(第五天)了解运算符,循环,判断

一 运算符 运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C 内置了丰富的运算符&#xff0c;并提供了以下类型的运算符&#xff1a; 算术运算符关系运算符逻辑运算符位运算符赋值运算符杂项运算符 1.1 算术运算符 假设变量 A 的值为 10&#xff0c;变量 B 的值为…

JAVA没有搞头了吗?

前言 今年的Java程序员群体似乎承受着前所未有的焦虑。投递简历无人问津&#xff0c;难得的面试机会也难以把握&#xff0c;即便成功入职&#xff0c;也往往难以长久。于是&#xff0c;不少程序员感叹&#xff1a;互联网的寒冬似乎又一次卷土重来&#xff0c;环境如此恶劣&…

Linux -- 线程控制相关的函数

目录 pthread_create -- 创建线程 参数 返回值 代码 -- 不传 args&#xff1a; 编译时带 -lpthread 运行结果 为什么输出混杂&#xff1f; 如何证明两个线程属于同一个进程&#xff1f; 如何证明是两个执行流&#xff1f; 什么是LWP&#xff1f; 代码 -- 传 args&a…

达梦查询表字段详细信息脚本(字段名称、描述、类型、长度及是否为空)

达梦查询表字段详细信息脚本&#xff08;字段名称、描述、类型、长度及是否为空&#xff09; 该SQL 脚本&#xff0c;用于查询表中字段的基本信息&#xff0c;包括字段名称、描述、数据类型、数据长度、是否为空及是否为主键等属性。 SQL 脚本 -- 输入变量 DECLAREp_owner VA…

学习笔记073——Java中的【Object】和【包装类】

文章目录 1、Object 类1.1、什么是 Object 类1.2、可能被重写的常用方法 2、包装类2.1、什么是包装类&#xff1f;2.2、装箱和拆箱 1、Object 类 1.1、什么是 Object 类 Java 通过类来构建代码的结构&#xff0c;类分为两种&#xff1a; 1、Java 提供的工具类&#xff0c;不…

面向预测性维护的TinyML技术栈全面综述

论文标题&#xff1a;A Holistic Review of the TinyML Stack for Predictive Maintenance&#xff08;面向预测性维护的TinyML技术栈全面综述&#xff09; 作者信息&#xff1a;Emil Njor, Mohammad Amin Hasanpour, Jan Madsen, Xenofon Fafoutis&#xff0c;均来自丹麦技术…

【MySQL】InnoDB引擎中的Compact行格式

目录 1、背景2、数据示例3、Compact解释【1】组成【2】头部信息【3】隐藏列【4】数据列 4、总结 1、背景 mysql中数据存储是存储引擎干的事&#xff0c;InnoDB存储引擎以页为单位存储数据&#xff0c;每个页的大小为16KB&#xff0c;平时我们操作数据库都是以行为单位进行增删…

【SpringAOP】深入浅出SpringAOP从原理到源码

AOP对象是如何创建的 对于熟悉Spring IOC流程源码的同学来说&#xff0c;一定了解bean的整个生命周期&#xff0c;也就是从实例化、属性填充、初始化三个过程。那么对于Bean 工厂来说&#xff0c;是如何保证需要创建代理的对象创建代理的呢。 从图中可以看到&#xff0c;本质…

VMware虚拟机Ubuntu 18.04版本 磁盘扩容

一、版本配置 虚拟机版本&#xff1a;VMware WORKSTATION 16 PRO Ubuntu版本&#xff1a;Ubuntu 18.04 二、磁盘大小介绍 目的&#xff1a;磁盘扩容&#xff08;20G----->100G&#xff09;&#xff0c;从20G扩到100G 查看磁盘大小命令&#xff1a;df -h 扩容前的磁盘大小 …

QT多线程(二):基于互斥锁与读写锁的线程同步

此处需要说明的是&#xff0c;这里的线程同步概念与操作系统中的线程同步并无区别&#xff0c;都是避免多个线程同时访问临界区数据可能产生的读写错误问题。在 Qt 中&#xff0c;有多个类可以实现线程同步的功能&#xff0c;这些类包括 QMutex、QMutexLocker、 QReadWriteLock…