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.mzph.cn/news/143418.shtml

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

相关文章

IDEA 2023搭建 SpringMVC +FreeMarker+JDBC

1.IDEA的版本&#xff0c;目前最新是2023&#xff0c;要选择旗舰版。笔者曾选择社区版&#xff0c;发现少了很多功能。只能重新安装。 2.安装好以后的第1件事&#xff0c;是设置Maven&#xff0c;并将下载地址改为淘定站&#xff0c;参照这篇一次包会——最新IDEA配置Maven指南…

老师的保命大法

数字化高度发达的今天&#xff0c;成绩查询系统已经成为学校教育中不可或缺的一部分。不同于传统的成绩公布方式&#xff0c;成绩查询系统更加高效、便捷&#xff0c;同时也充分保障了每位学生的隐私&#xff0c;今天就来揭秘这个教师保命大法&#xff01; 1、代码查询法 对于…

下载文件时的文件名中文乱码问题,文件名丢失

涉及到的java代码如下&#xff0c;下载的时候文件名为中文 package com.example.springboot.service.impl;import com.alibaba.excel.EasyExcel; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.StringU…

墨西哥专线一次最多发几条柜?

墨西哥专线一次最多发几条柜这个问题涉及到海运业务中的一些复杂因素。墨西哥是一个重要的贸易国家&#xff0c;其与美国和加拿大之间的贸易往来非常频繁&#xff0c;因此海运业务也非常活跃。在墨西哥专线上&#xff0c;一次最多发几条柜通常取决于以下几个因素&#xff1a; 1…

Java 等后端应用如何获取客户端真实IP —— 筑梦之路

需求说明 现有一套Java开发的应用&#xff0c;需要能获取到用户访问的真实IP地址&#xff0c;以此来过滤到一些不安全的因素。而实际部署的场景中Java服务提供给用户访问需要经过多次代理&#xff0c;默认情况下是无法获取到客户端真实IP地址的&#xff0c;因此要实现该需求&a…

ubuntu 20通过docker安装onlyoffice,并配置https访问

目录 一、安装docker &#xff08;一&#xff09;更新包列表和安装依赖项 &#xff08;二&#xff09;添加Docker的官方GPG密钥 &#xff08;三&#xff09;添加Docker存储库 &#xff08;四&#xff09;安装Docker &#xff08;五&#xff09;启动Docker服务并设置它随系…

编程怎么学习视频教程,编程实例入门教程,中文编程开发语言工具下载

编程怎么学习视频教程&#xff0c;编程实例入门教程&#xff0c;中文编程开发语言工具下载。 给大家分享一款中文编程工具&#xff0c;零基础轻松学编程&#xff0c;不需英语基础&#xff0c;编程工具可下载。 这款工具不但可以连接部分硬件&#xff0c;而且可以开发大型的软件…

接口测试--知识问答

1 做接口测试当请求参数多时tps下降明显&#xff0c;此接口根据参数从redis中获取数据&#xff0c;每个参数与redis交互一次&#xff0c;当一组参数是tps5133&#xff0c;五组参数是tps1169&#xff0c;多次交互影响了处理性能&#xff0c;请详细阐述如何改进增进效果的方案。 …

mysql数据库时间

记录MySQL今天又一个新的问题&#xff1a; 场景&#xff1a;nodejs后台容器部署 问题原因&#xff1a;纯属好心办坏事&#xff0c;由于考虑了时区&#xff08;现在看来纯属多余&#xff09;&#xff0c;在写入时间时使用了time_str.toLocaleString("chinese", { ti…

MOS管体电极接源端版图layout画法

记录一个lvs一直跑不通的问题。 问题描述&#xff1a;lvs一直显示某几个MOS管的体电极连接问题。连线没有问题&#xff0c;版图中已经画了衬底。 原因&#xff1a; 图中四个管子的衬底接了源端&#xff0c;没接电源。 解决办法&#xff1a; 法1、源端接地 法2、将这四个管子…

一个项目进行测试的一些最基本环境

在一个项目开发到发布的整个过程中&#xff0c;会使用到很多个环境进行测试和运行项目。最基本的开发环境、测试环境、准生产环境、生成环境 一、开发环境 开发环境顾名思义就是我们程序猿自己把项目放到自己的电脑上&#xff0c;配置好以后&#xff0c;跑起来项目&#xff0…

Git的基本操作以及原理介绍

文章目录 基本操作创建git仓库配置name和email .git目录的结构git add & git commit.git目录结构的变化 git追踪管理的数据git的版本回退回退的原理回退的三种情况 版本库中文件的删除git分支管理分支的删除合并分支时的冲突分支的合并模式分支策略git stash不要在master分…

【软考篇】中级软件设计师 第三部分(一)

中级软件设计师 第三部分&#xff08;一&#xff09; 十七. I/O管理软件十八. 输入/输出技术十九. 总线系统二十. 磁盘管理20.1 移臂调度算法20.2 缓冲区 二十一. 操作系统二十二. 数据库22.1 三级模式-两级映射22.2 数据库设计22.3 规范化理论22.4 范式判断22.5 模式分解 二十…

C++基础知识记录

github仓库不定期更新: https://github.com/han-0111/CppLearning 文章目录 C如何工作编译器和链接器编译器预处理(Preprocessing)includedefineif/endif 链接器一种比较复杂的情况 变量变量类型intcharshortlonglong longfloatdoublebool如何查看数据大小 函数头文件条件语句…

2023年亚太杯数学建模思路 - 案例:ID3-决策树分类算法

文章目录 0 赛题思路1 算法介绍2 FP树表示法3 构建FP树4 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法&#xff0c;就是频繁模…

运营商大数据,金融贷款精准营销赢得客户

运营商大数据精准营销赢得客户推广 在大数据的新形势下&#xff0c;它推动了经济和金融的发展趋势。其中&#xff0c;大数据获取客户是企业营销和推广的一个阶段&#xff0c;是新一轮的转型发展。通过大数据准确获取客户是一个基本概念。 大数据根据您指定的物理模型选择客户&…

【ArcGIS Pro二次开发】(76):面积平差工具

之前做过一个【三调土地利用现状分类面积汇总】的工具&#xff0c;在流程中使用了面积平差的方法。 考虑了在其它场合可能也需要进行面积平差&#xff0c;因此单独提取出来作为一个工具。 平差实现的方法如下图&#xff1a; 主要的计算过程如上图所示&#xff0c;算出总面积差…

【luckfox】3、计算重量差

前言 本章结合之前的hx711驱动&#xff0c;实现读取质量&#xff0c;记录时间及剩余质量并存入csv文件&#xff0c;计算质量差并总计。 代码 luckfox-pico\project\app\test_app\hx711\hx711_app_addtime.c #include <stdio.h> #include <stdlib.h> #include &…

MySQL数据库约束

目录 数据库约束 1.NULL约束 2.UNIQUE&#xff1a;唯一约束 3.DEFAULT&#xff1a;默认值约束 4.PRIMARY KEY&#xff1a;主键约束 5.FOREIGN KEY&#xff1a;外键约束 数据库约束 以下为本篇文章会介绍的约束 (1)NOT NULL - 指示某列不能存储 NULL 值。 (2)UNIQUE - …

带头双向循环链表

目录 一、结构定义 二、结点创建 三、头结点初始化 四、链表打印 五、尾插 六、头插 七、尾删 八、头删 九、查找&#xff08;返回结点&#xff09; 十、任意位置插入 十一、任意位置删除 十二、利用LTInsert写尾插函数 十三、利用LTInsert写头插函数 十四、利用…