iOS OpenGL ES3.0入门实践

一、效果图 

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

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

绘制图片需要设置顶点坐标和纹理坐标并加载像素数据,之所以要指定两组坐标是因为纹理和顶点使用不同的坐标系,就是告诉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;+(void)printActiveUniforms:(GLuint)program;@endNS_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;
}+ (void)printActiveUniforms:(GLuint)program {GLint maxUniformLen;//变量名的最大长度GLint numUniforms;//变量个数char* uniformName;//变量名glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &numUniforms);glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformLen);uniformName = malloc(maxUniformLen);for (GLint index = 0; index < numUniforms; index++) {GLint size;GLenum type;GLint location;glGetActiveUniform(program, index, maxUniformLen, NULL, &size, &type, uniformName);location = glGetUniformLocation(program, uniformName);NSString* typeStr;switch (type) {case GL_FLOAT:typeStr = (@"GL_FLOAT");break;case GL_FLOAT_VEC2:typeStr = (@"GL_FLOAT_VEC2");break;case GL_FLOAT_VEC3:typeStr = (@"GL_FLOAT_VEC3");break;case GL_FLOAT_VEC4:typeStr = (@"GL_FLOAT_VEC4");break;case GL_FLOAT_MAT4:typeStr = (@"GL_FLOAT_MAT4");break;case GL_BOOL:typeStr = (@"GL_BOOL");break;case GL_SAMPLER_2D:typeStr = (@"GL_SAMPLER_2D");break;default:typeStr = [NSString stringWithFormat:@"变量类型:0x%X", type];break;}NSLog(@"index=%d, name=%s, size=%d, type=%@, location=%d", index, uniformName, size, typeStr, location);}free(uniformName);
}@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;@endNS_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,     //顶点坐标V01.0f, 1.52f,   //顶点坐标V1-1.0f, 1.52f,   //顶点坐标V2-1.0f, -1.52f,  //顶点坐标V31.0f, -1.52f     //顶点坐标V4
};static const float TEX_VERTEX[]  = {0.5f, 0.5f, //纹理坐标V01.0f, 1.0f,     //纹理坐标V10.0f, 1.0f,     //纹理坐标V20.0f, 0.0f,   //纹理坐标V31.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 转换为 CGImageRefCGImageRef 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 {
}@endNS_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);[GLUtil printActiveUniforms:program];
}- (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"];// Combine the projection and camera view matricesvMatrix = 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 matricesvPMatrix = 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 transformationsglUniformMatrix4fv(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.rhkb.cn/news/192716.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring整合redis的key时出现\xac\xed\x00\x05t\前缀问题

AutowiredRedisTemplate redisTemplate;User usernew User(5,"tomhs","tttt");ValueOperations opsForValue redisTemplate.opsForValue();//存放key,opsForValue.set("user"user.getId(),user);//读取数据;System.out.println(opsForValue.get…

NLP领域的突破催生大模型范式的形成与发展

当前的大模型领域的发展&#xff0c;只是范式转变的开始&#xff0c;基础大模型才刚刚开始改变人工智能系统在世界上的构建和部署方式。 1、大模型范式 1.1 传统思路&#xff08;2019年以前&#xff09; NLP领域历来专注于为具有挑战性的语言任务定义和设计系统&#xff0c…

【广州华锐互动】VR居家防火逃生模拟演练增强训练的真实性

VR软件开发公司广州华锐互动在消防培训领域已开发了多款VR产品&#xff0c;今天为大家介绍VR居家防火逃生模拟演练系统&#xff0c;这是一种基于虚拟现实技术的消防教育训练设备&#xff0c;通过模拟真实的火灾场景&#xff0c;让使用者身临其境地体验火灾逃生过程&#xff0c;…

qemu 之 uboot、linux 启动

目录 编译uboot、kernel 编译启动从 uboot 中引导启动 linux注参考 本文主要说明 arm64 在 qemu 上的相关启动。 编译 使用的是 qemu-8.1.1 版本&#xff0c;编译命令如下: ../configure --cc/usr/local/bin/gcc --prefix/home/XXX/qemu_out --enable-virtfs --enable-slir…

基于安卓android微信小程序的食谱大全系统

项目介绍 本文以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;它主要是采用java语言技术和mysql数据库来完成对系统的设计。整个开发过程首先对食谱大全进行需求分析&#xff0c;得出食谱大全主要功能。接着对食谱大全进行总体设计和详细设计。总体设…

多种格式图片可用的二维码生成技巧,快来学习一下

将图片存入二维码是现在很常见的一种图片展现方式&#xff0c;有效的节省了图片占用内容空间以及获取图片内容的速度&#xff0c;所以现在会有很多人将不同的图片、照片生成二维码展示。如何使用图片二维码生成器来快速生成二维码呢&#xff1f;下面就让小编来给大家分享一下图…

大数据分析师职业技能提升好考吗?含金量高不高

随着大数据时代的到来&#xff0c;大数据分析技能需求已经成为很多企业和机构的必备要求。大数据分析师证书成为当下的热门之一&#xff0c;那么大数据分析师证书需要具备哪些条件呢&#xff1f; 首先&#xff0c;报考大数据分析师证书需要具备以下方面的条件&#xff1a; …

(C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。

要求&#xff1a;原始数组的数据从键盘随机输入&#xff0c;新数组以4行4列的方式输出。 #include<stdio.h> int main() {int matrix[4][4],matrix2[4][4];int count;for(int i 0;i < 4;i )for(int j 0;j < 4;j )scanf("%d",&matrix[i][j]);for(i…

读书充电,温暖你的冬日,本期为大家送出几本架构师成长和软件架构技术相关的好书,助你度过这个不太景气的寒冬!

目图书录 ⭐️《高并发架构实战&#xff1a;从需求分析到系统设计》⭐️《架构师的自我修炼&#xff1a;技术、架构和未来》⭐️《中台架构与实现&#xff1a;基于DDD和微服务》⭐️《分布式系统架构&#xff1a;架构策略与难题求解》⭐️《流程自动化实战&#xff1a;系统架构…

Java学习笔记(七)——面向对象编程(中级)

一、IDEA &#xff08;一&#xff09;常用的快捷键 &#xff08;二&#xff09;模版/自定义模版 二、包 &#xff08;一&#xff09;包的命名 &#xff08;二&#xff09;常用的包 &#xff08;三&#xff09;如何引入&#xff08;导入&#xff09;包 &#xff08;四&am…

Linux系统编程,Linux中的文件读写文件描述符

文章目录 Linux系统编程&#xff0c;Linux中的文件读写操作1.open函数&#xff0c;打开文件 Linux系统编程&#xff0c;Linux中的文件读写操作 1.open函数&#xff0c;打开文件 我们来看下常用的open函数 这个函数最终返回一个文件描述符struct file 我们查看一下它的Ubuntu…

.NET快速对接极光消息推送

什么是消息推送&#xff1f; 很多手机APP会不定时的给用户推送消息&#xff0c;例如一些新闻APP会给用户推送用户可能感兴趣的新闻&#xff0c;或者APP有更新了&#xff0c;会给用户推送是否选择更新的消息等等&#xff0c;这就是所谓的“消息推送”。 常见的一些APP消息推送…

【Vue】【uni-app】工单管理页面实现

用的是uni-app的uni-ui拓展组件实现的 功能是对工单进行一个展示&#xff0c;并对工单根据一些筛选条件进行搜索 目前是实现了除了日期之外的搜索功能&#xff0c;测试数据是下面这个tableData.js&#xff0c;都是我自己手写的&#xff0c;后端请求也稍微写了一些&#xff0c;…

vue3 el-menu初始化时选中没有高亮的问题(default-active和index的问题)

首先看官方文档的示例&#xff1a; 需要注意的是&#xff1a; 1、default-active的值是字符串&#xff0c;那么index绑定的值也要是字符串&#xff0c;且数字对应。不能default-avtive绑定的是1&#xff0c;而menu-item的index绑定的是45 2、default-active的值是当前选中me…

C++面向对象编程(4)——浅谈C++内存模型

目录 一. 说明 二. GDB实验 2.1 实验1&#xff1a;栈 2.2 实验2&#xff1a;堆 一. 说明 不同的操作系统对程序内存的管理和划分会有所不同。如上图所示的C内存区域划分主要是针对一般的情况&#xff0c;说明如下&#xff1a; 1. Stack&#xff1a;栈。由编译器管理分配和回…

远程登录Linux方法(Linux平台相互远程;Windows远程登录Linux、远程编码、文件传输;无法远程登录的问题解决;c程序的编译)

在实际使用Linux系统过程中我们不可避免的需要远程登录Linux&#xff0c;这是因为未来大家使用Linux服务器的时候你所对应的那台Linux服务器不一定提供界面(服务器可能在外地)。本篇将会介绍远程登录Linux的方法。 文章目录 1. SSH介绍2. Linux平台相互远程及文件传输2.1 Linux…

MySQL MHA高可用切换

MySQL MHA 1&#xff0e;什么是 MHA MHA&#xff08;MasterHigh Availability&#xff09;是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。 MHA 的出现就是解决MySQL 单点的问题。 MySQL故障切换过程中&#xff0c;MHA能做到0-30秒内自动完成故障切换操作。 MHA能在…

【PG】PostgreSQL高可用方案repmgr部署(非常详细)

目录 简介 1 概述 1.1 术语 1.2 组件 1.2.1 repmgr 1.2.2 repmgrd 1.3 Repmgr用户与元数据 2 安装部署 2.0 部署环境 2.1 安装要求 2.1.1 操作系统 2.1.2 PostgreSQL 版本 2.1.3 操作系统用户 2.1.4 安装位置 2.1.5 版本要求 2.2 安装 2.2.1 软件包安装 2.2…

【C++】日期类实现,与日期计算相关OJ题

文章目录 日期类的设计日期计算相关OJ题HJ73 计算日期到天数转换KY111 日期差值KY222 打印日期KY258 日期累加 在软件开发中&#xff0c;处理日期是一项常见的任务。为了方便地操作日期&#xff0c;我们可以使用C编程语言来创建一个简单的日期类。在本文中&#xff0c;我们将介…

从0到0.01入门 Webpack| 001.精选 Webpack面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…