iOS OpenGL ES3.0入门实践

news2024/11/19 9:23:53

一、效果图 

入门实践,做的东西比较简单,效果如下:

二、关于顶点坐标和纹理坐标

绘制图片需要设置顶点坐标和纹理坐标并加载像素数据,之所以要指定两组坐标是因为纹理和顶点使用不同的坐标系,就是告诉OpenGL:把图像的某一区域绘制到屏幕的某一区域,3个点能确定一个三角形区域,我们把一张图分成4个三角形,分别是中心点和每条边点两个顶点组成的三角形,如下图,它图能够帮助你理解后面代码 GLImage.m 中的两组顶点为什么是那样子设置的

三、关于坐标轴方向、投影和坐标映射

具体参考我的另一篇文章Android OpenGL ES 2.0入门实践,主要也是参考的Android官方文档,好在iOS都有对应的函数,就没再仔细看iOS官方文档了。 

四、代码

OpenGL在iOS12 就已经不推荐使用了,官方推荐使用Metal,最近在学习OpenGL ES就在Android和iOS上分别实践了一下,iOS端最简单的方式就是使用GLKit,主要使用GLKViewController这个现成的绘制环境,有了这个环境,就可以直接使用OpenGL的接口进行编码了,平台有更简单易用的 GLKBaseEffect,我没有使用,还是沿用了最基本的流程,使用GL着色器,下面是主要代码:

GlUtil.h

#import <Foundation/Foundation.h>
#import <GLKit/GLKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface GLUtil : NSObject

+(GLuint)createProgram:(NSString*)vertexShaderCode :(NSString*)fragmentShaderCode;

@end

NS_ASSUME_NONNULL_END

GLUtil.m

#import "GLUtil.h"

@implementation GLUtil

+ (NSString *)readShaderSource:(NSString *)bundleFileName {
    NSString* path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:bundleFileName];
    NSString* content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//    NSLog(@"%@", content);
    return content;
}

+ (GLuint)loadShader:(GLenum)type :(const char *)shaderCode {
    GLuint shader = glCreateShader(type);
    glShaderSource(shader, 1, &shaderCode, NULL);
    glCompileShader(shader);
    
    GLint commpiled;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &commpiled);
    if(!commpiled) {
        GLint infoLen = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
        if(infoLen > 0) {
            char* infoLog = malloc(infoLen);
            glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
            NSLog(@"%s", infoLog);
            free(infoLog);
            glDeleteShader(shader);
            return 0;
        }
    }
    return shader;
}

+ (GLuint)createProgram:(NSString *)vertexShaderFileName :(NSString *)fragmentShaderFileName {
    GLuint program = glCreateProgram();
    
    const char* vertexShaderCode = [[GLUtil readShaderSource:vertexShaderFileName] UTF8String];
    const char* fragmentShaderCode = [[GLUtil readShaderSource:fragmentShaderFileName] UTF8String];
    
    GLuint vertexShader = [self loadShader:GL_VERTEX_SHADER:vertexShaderCode];
    GLuint fragmentShader = [self loadShader:GL_FRAGMENT_SHADER:fragmentShaderCode];
    
    if (vertexShader == 0 || fragmentShader == 0) {
        return 0;
    }
    
    glAttachShader(program, vertexShader);
    glAttachShader(program, fragmentShader);
    glLinkProgram(program);
    GLint linked;
    glGetProgramiv(program, GL_LINK_STATUS, &linked);
    if(!linked) {
        GLint infoLen = 0;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
        if(infoLen > 0) {
            char* infoLog = malloc(infoLen);
            glGetProgramInfoLog(program, infoLen, NULL, infoLog);
            NSLog(@"%s", infoLog);
            free(infoLog);
            glDeleteProgram(program);
            return 0;
        }
    }
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    return program;
}

@end

vertexShader.glsl

顶点着色器代码 vertexShader.glsl

#version 300 es
uniform mat4 uMVPMatrix;
uniform mat4 translateMatrix;
uniform bool isTranslate;
layout(location = 0) in vec4 vPosition;
layout(location = 1) in vec2 aTextureCoord;
out vec2 vTexCoord;
void main() {
    if (isTranslate) {
        gl_Position = uMVPMatrix * translateMatrix * vPosition;
    } else {
        gl_Position = uMVPMatrix * vPosition;
    }
    vTexCoord = aTextureCoord;
}

fragmentShader.glsl

片段着色器代码 fragmentShader.glsl

#version 300 es
precision mediump float;
uniform bool isTexture;
uniform vec4 vColor;
uniform sampler2D uTextureUnit;
in vec2 vTexCoord;
out vec4 fragColor;
void main() {
   if(isTexture) {
       fragColor = texture(uTextureUnit,vTexCoord);
   } else {
       fragColor = vColor;
   }
}

绘制2D纹理的类 GLImage

GLImage.h

#import <GLKit/GLKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface GLImage : NSObject

-(void)loadTexture:(NSString*)imageName;

-(void)draw:(int)positionHandle :(int)textureHandle :(GLuint*)vbo;

@end

NS_ASSUME_NONNULL_END

GLImage.m

#import "GLImage.h"
#import <UIKit/UIKit.h>

//原文链接:https://blog.csdn.net/gongxiaoou/article/details/89344561

//图片尺寸:2385 × 3623
static const float POSITION_VERTEX[]  = {
    0.0f, 0.0f,     //顶点坐标V0
    1.0f, 1.52f,   //顶点坐标V1
    -1.0f, 1.52f,   //顶点坐标V2
    -1.0f, -1.52f,  //顶点坐标V3
    1.0f, -1.52f     //顶点坐标V4
};

static const float TEX_VERTEX[]  = {
    0.5f, 0.5f, //纹理坐标V0
    1.0f, 1.0f,     //纹理坐标V1
    0.0f, 1.0f,     //纹理坐标V2
    0.0f, 0.0f,   //纹理坐标V3
    1.0f, 0.0f    //纹理坐标V4
};

static const short VERTEX_INDEX[]  = {
    0, 1, 2,  //V0,V1,V2 三个顶点组成一个三角形
    0, 2, 3,  //V0,V2,V3 三个顶点组成一个三角形
    0, 3, 4,  //V0,V3,V4 三个顶点组成一个三角形
    0, 4, 1   //V0,V4,V1 三个顶点组成一个三角形
};


@interface GLImage()
{
    GLuint textureID;
}

@end

@implementation GLImage


//原文链接:https://blog.csdn.net/qq_30513483/article/details/101538967
- (void)loadTexture:(NSString *)imageName {
    UIImage *image = [UIImage imageNamed:imageName];
    
    // 将 UIImage 转换为 CGImageRef
    CGImageRef cgImageRef = [image CGImage];
    GLuint width = (GLuint)CGImageGetWidth(cgImageRef);
    GLuint height = (GLuint)CGImageGetHeight(cgImageRef);
    CGRect rect = CGRectMake(0, 0, width, height);

    // 绘制图片
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    void *imageData = malloc(width * height * 4);
    CGContextRef context = CGBitmapContextCreate(imageData, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGContextTranslateCTM(context, 0, height);
    CGContextScaleCTM(context, 1.0f, -1.0f);
    CGColorSpaceRelease(colorSpace);
    CGContextClearRect(context, rect);
    CGContextDrawImage(context, rect, cgImageRef);
     
    // 生成纹理
    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_2D, textureID);
    // 将图片数据写入纹理缓存
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
        
    // 设置如何把纹素映射成像素
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            
    // 解绑
    glBindTexture(GL_TEXTURE_2D, 0);
    
    // 释放内存
    CGContextRelease(context);
    free(imageData);
}

- (void)draw:(int)positionHandle :(int)textureHandle :(GLuint*)vbo {
    //顶点坐标
    glEnableVertexAttribArray(positionHandle);
    glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(POSITION_VERTEX), POSITION_VERTEX, GL_STATIC_DRAW);
    glVertexAttribPointer(positionHandle, 2, GL_FLOAT, GL_FALSE, 0, 0);
    
    //纹理坐标
    glEnableVertexAttribArray(textureHandle);
    glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(TEX_VERTEX), TEX_VERTEX, GL_STATIC_DRAW);
    glVertexAttribPointer(textureHandle, 2, GL_FLOAT, GL_FALSE, 0, 0);
    //激活纹理
    glActiveTexture(GL_TEXTURE0);
    //绑定纹理
    glBindTexture(GL_TEXTURE_2D, textureID);
    //绘制
    glDrawElements(GL_TRIANGLES, sizeof(VERTEX_INDEX)/sizeof(short), GL_UNSIGNED_SHORT, VERTEX_INDEX);
}

@end

GLViewController.h

#import <GLKit/GLKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface GLViewController : GLKViewController {
}

@end

NS_ASSUME_NONNULL_END

GLViewController.m

#import "GLViewController.h"
#import "GLUtil.h"
#import "GLImage.h"


const int VBO_NUM = 2;

static const GLfloat vertices[] = {
    0.0f,  0.5f, 0.0f,
    -0.5f, -0.5f, 0.0f,
    0.5f, -0.5f, 0.0f
    
};


@interface GLViewController ()
{
    GLuint program;
    int muMVPMatrixHandle;
    int translateMatrixHandle;
    int isTranslateHandle;
    int isTextureHandle;
    int colorHandle;
    GLKMatrix4 vMatrix;
    GLKMatrix4 projMatrix;
    GLKMatrix4 vPMatrix;
    GLKMatrix4 translateMatrix;
    GLuint vbo[VBO_NUM];
    
    GLImage* glImage;
}

@end

@implementation GLViewController

- (void)initGL{
    program = [GLUtil createProgram:@"vertexShader.glsl" :@"fragmentShader.glsl"];
    glUseProgram(program);
    muMVPMatrixHandle = glGetUniformLocation(program, "uMVPMatrix");
    translateMatrixHandle = glGetUniformLocation(program, "translateMatrix");
    isTranslateHandle = glGetUniformLocation(program, "isTranslate");
    isTextureHandle = glGetUniformLocation(program, "isTexture");
    colorHandle = glGetUniformLocation(program, "vColor");
    
    glGenBuffers(VBO_NUM, vbo);
    NSLog(@"%s", __func__);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    GLKView* glView = (GLKView*)self.view;
    glView.context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];
    [EAGLContext setCurrentContext:glView.context];//这句必须得有,不然画不出来图形
    
    [self initGL];
    
    glImage = [GLImage new];
    [glImage loadTexture:@"dog.jpg"];
    
    //
    vMatrix = GLKMatrix4MakeLookAt(0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
}

- (void) onSurfaceChange:(float)width :(float)height {
    NSLog(@"%fx%f", width, height);
    glViewport(0, 0, width, height);
    //投影矩阵
    if (width > height) {
        float ratio = width / height;
        projMatrix = GLKMatrix4MakeFrustum(-ratio, ratio, -1.0f, 1.0f, 3.0f, 7.0f);
    } else {
        float ratio = height / width;
        projMatrix = GLKMatrix4MakeFrustum(-1.0f, 1.0f, -ratio, ratio, 3.0f, 7.0f);
    }
    
    // Combine the projection and camera view matrices
    vPMatrix = GLKMatrix4Multiply(projMatrix, vMatrix);
}


- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    float width = self.view.frame.size.width;
    float height = self.view.frame.size.height;
    NSLog(@"%s %fx%f", __func__, width, height);
    [self onSurfaceChange:width :height];
}

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
//    NSLog(@"%s %f", __func__, [[NSDate new] timeIntervalSince1970]);
    // Apply the combined projection and camera view transformations
    glUniformMatrix4fv(muMVPMatrixHandle, 1, false, (float*)&vPMatrix);
    
    glClearColor(0.9f, 0.9f, 0.9f, 1.0f);//背景色
    glClear(GL_COLOR_BUFFER_BIT);
    
    //画图片
    glUniform1i(isTranslateHandle, 0);//禁用矩阵偏移
    glUniform1i(isTextureHandle, 1);//启用纹理
    [glImage draw:0 :1 :vbo];
    
    double degrees = (long)([[NSDate new] timeIntervalSince1970] * 50) % 360;
    double radians = degrees * M_PI / 180.0;
//    NSLog(@"degrees=%f", degrees);
    translateMatrix = GLKMatrix4Translate(GLKMatrix4Identity, cos(radians), sin(radians), 0.0f);
    glUniformMatrix4fv(translateMatrixHandle, 1, GL_FALSE, (float*)&translateMatrix);
    
    glUniform1i(isTranslateHandle, 1);//启用矩阵偏移
    glUniform1i(isTextureHandle, 0);//禁用纹理
    glUniform4f(colorHandle, 0.0f, 0.5f, 1.0f, 1.0f);
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glDrawArrays(GL_TRIANGLES, 0, 3);
}

@end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1199912.html

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

相关文章

ENVI IDL:如何将txt文本文件转化为GeoTIFF文件?

01 前言 此处的文本文件形式如下&#xff1a; 里面包含了众多点位信息&#xff08;不是站点数据&#xff09;&#xff0c;我们需要依据上述点的经纬度信息放到对应位置的像素点位置&#xff0c;放置完后如下&#xff1a; 可以发现&#xff0c;还存在部分缺失值&#xff0c;我们…

C++八股文

第一章 编译内存相关 1.1 main函数之前和之后执行的代码 main函数之前 设置栈指针 初始化静态变量和全局变量&#xff08;即.data内容&#xff09;&#xff1b;将未初始化的全局变量赋值&#xff1a;short、int、long初始化为0&#xff0c;bool初始化为false&#xff0c;指针…

Python使用SQLAlchemy操作sqlite

Python使用SQLAlchemy操作sqlite sqllite1. SQLite的简介2. 在 Windows 上安装 SQLite3. 使用SQLite创建数据库3.1 命令行创建数据库3.2 navicat连接数据库 4.sqlite的数据类型存储类SQLite Affinity 类型Boolean 数据类型Date 与 Time 数据类型 5. 常用的sql语法**创建表(CREA…

程序员职业生涯规划:多领域路线图一网打尽 | 开源日报 No.72

kamranahmedse/developer-roadmap Stars: 244.4k License: NOASSERTION 这是一个互动的路线图&#xff0c;指南和其他教育内容&#xff0c;旨在帮助开发人员在他们的职业生涯中成长。 提供多个不同领域 (如前端、后端、DevOps 等) 的路线图路线图可交互&#xff0c;并提供了详…

MySQL中外键的使用及外键约束策略

一、外键约束的概念 外键约束&#xff08;FOREIGN KEY,缩写FK是数据库设计的一个概念&#xff0c;它确保在两个表之间的关系保持数据的一致性和完整性。 外键是指表中的某个字段的依赖于另一张表中某个字段的值&#xff0c;而被依赖的字段必须具有主键约束或者唯一约束&#…

适用于初学者的 .NET MAUI

适用于初学者的 .NET MAUI | Microsoft Learn 记录微软Learn中用到的代码。文章比较粗糙&#xff0c;大部分是项目代码粘贴。想详细学习的可到上面的链接学习&#xff0c;代码可以从这里复制后直接运行。 练习中一共有两个页面&#xff1a; 1、MainPage.xaml 用于添加列表中的…

网络安全之认识托管威胁检测与响应(MDR)

随着数字化转型加速&#xff0c;企业的IT环境日益复杂&#xff0c;面临的网络安全威胁也在不断增加。传统的防御措施已经无法有效应对新型威胁&#xff0c;而且很多企业缺乏专业的网络安全团队和技术手段&#xff0c;导致大量的安全事件未能及时被发现和处理。 在这种背景下&a…

如何安装Node.js? 创建Vue脚手架

1.进入Node.js官网&#xff0c;点击LTS版本进行下载 Node.js (nodejs.org)https://nodejs.org/en 2.然后一直【Next】即可 3.打开【cmd】,输入【node -v】注意node和-v中间的空格 查看已安装的Node.js的版本号&#xff0c;如果可以看到版本号&#xff0c;则安装成功 创建Vue脚手…

Flutter实践一:package组织

1.架构概览 为了降低Flutter工程里lib的复杂度&#xff0c;应尽量拆分一些代码成为独立的package。如图&#xff1a; 我们将通用的组件、领域模型、API、features、存储、repository等抽取成了单独的package。这时lib只剩下多国语言、基本的页面、路由等代码了&#xff1a; 这…

Flutter笔记:使用Flutter构建响应式PC客户端/Web页面-案例

Flutter笔记 使用Flutter构建响应式PC客户端/Web页面-案例 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/detai…

Python基础入门例程53-NP53 前10个偶数(循环语句)

最近的博文&#xff1a; Python基础入门例程52-NP52 累加数与平均值(循环语句)-CSDN博客 Python基础入门例程51-NP51 列表的最大与最小(循环语句)-CSDN博客 Python基础入门例程50-NP50 程序员节&#xff08;循环语句&#xff09;-CSDN博客 目录 最近的博文&#xff1a; 描…

TCSVT(IEEE Transactions on Circuits and Systems for Video Technology)期刊投稿指南

目录 TCSVT 期刊简介 TCSVT 期刊影响因子和分区​ 期刊官方网站 期刊投稿网址 投稿指南网址 稿件格式要求 稿件提交指南 A. 提交稿件 B. 手稿格式 C. 摘要指南 D. 提交图形的指南 E. 页面和彩色图形费用 TCSVT 期刊简介 IEEE Transactions on Circuits and Systems…

RGB颜色空间与BMP格式图片

RGB颜色空间 RGB可以分为两大类&#xff1a;一种是索引形式&#xff0c;一种是像素形式&#xff1a; 索引形式&#xff1a;存储每个像素在调色板中的索引 RGB1&#xff1a;每个像素用1bit表示&#xff0c;调色板中只包含两种颜色&#xff08;黑白&#xff09;RGB4&#xff1a…

恒源云之oss上传数据、云台下载数据

目录 一、本地cmd上传数据二、使用云平台下载数据 一、本地cmd上传数据 需要下载恒源云客户端oss需要先将数据&#xff08;代码、数据集&#xff09;压缩成zip文件。 本地cmd打开oss&#xff0c;测试是否安成功 oss输入oss命令&#xff0c;并正确输入账号密码 oss login在个人…

按键编程 pal库和标准库

按钮的电路设计 电路的搭建 原理与编程 创建了两个变量 用来捕捉按键的状态 先让两个变量都为1 previous和current都为1 &#xff08;按键没按下&#xff09; 然后让current去捕捉按键的状态通过读gpioA的pin0 如果为0就是按键按下 如果为1就是按键没按下 然后赋值给current …

U-Boot 图形化配置及其原理

目录 U-Boot 图形化配置体验menuconfig 图形化配置原理make menuconfig 过程分析Kconfig 语法简介 添加自定义菜单 在前两章中我们知道uboot 可以通过mx6ull_alientek_emmc_defconfig 来配置&#xff0c;或者通过文件mx6ull_alientek_emmc.h 来配置uboot。还有另外一种配置uboo…

理解快速排序

理解快速排序 首先了解以下快速排序 快速排序&#xff08;QuickSort&#xff09;是一种常用的排序算法&#xff0c;属于比较排序算法的一种。它是由英国计算机科学家Tony Hoare于1960年提出的&#xff0c;是一种分而治之&#xff08;divide and conquer&#xff09;的算法。 …

systemd-timesyncd

介绍 systemd-timesyncd 是一个用于跨网络同步系统时钟的守护服务。它实现了一个 SNTP 客户端。与NTP的复杂实现相比&#xff0c;这个服务简单的多&#xff0c;它只专注于从远程服务器查询然后同步到本地时钟。除非你打算为客户端提供 NTP 服务器或者连接本地硬件时钟&#xff…

Keras实现图注意力模型GAT

简介&#xff1a;本文实现了一个GAT图注意力机制的网络层&#xff0c;可以在Keras中像调用Dense网络层、Input网络层一样直接搭积木进行网络组合。 一&#xff0c;基本展示 如下图所示&#xff0c;我们输入邻接矩阵和节点特征矩阵之后&#xff0c;可以直接调用myGraphAttention…

第90步 深度学习图像分割:U-Net建模

基于WIN10的64位系统演示 一、写在前面 从这一期开始&#xff0c;我们杀个回马枪&#xff0c;继续学习深度学习图像分割系列&#xff0c;以为4090上岗了。 图像分割是计算机视觉的一个重要任务&#xff0c;目的是将数字图像分割成多个部分或区域&#xff0c;这些部分通常对应…