iOS OpenGL ES3.0入门实践

news/2024/7/20 22:17:47 标签: ios, OpenGL

一、效果图 

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

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

绘制图片需要设置顶点坐标和纹理坐标并加载像素数据,之所以要指定两组坐标是因为纹理和顶点使用不同的坐标系,就是告诉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;

@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;
}


+ (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;

@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);
    [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 matrices
    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.niftyadmin.cn/n/5175262.html

相关文章

python类中的抽象函数,以及继承后子类的比较

抽象函数的定义方式 导包 from abs import ABCMeta,abstractmethod声明抽象类 class Area(object):abstractmethoddef area(self):pass在抽象类中&#xff0c;不用写构造函数&#xff0c;抽象类不能进行实例化 继承抽象类的子类必须将抽象类中的函数进行重写&#xff08;不重…

ElastaticSearch ---es客户端 TransportClient

TransportClient TransportClient客户端&#xff0c;官方在es 7.0版本中将弃用TransportClient客户端&#xff0c;且在8.0版本中完全移除它. es 7.0及以上的版本&#xff0c;请使用 RestHighLevelClient。 如果项目中使用的es版本不高&#xff0c;可以使用 TransportClient。 …

HTML表单的基本应用

注释很详细&#xff0c;直接上代码 效果&#xff1a; 涉及内容&#xff1a; 1.信息输入框 2.单选框 3.复选框&#xff08;多选框&#xff09; 4.文本输入框 5.下拉栏 6.隐藏域&#xff08;用于防脚本&#xff09; 7.确认按钮 8.重置按钮 9.普通按钮 10.禁用部件 11.下拉栏默认选…

测试笔记:如何使用 vcpkg 编译Google-V8脚本引擎(ECMA/JavaScript)?

WIN32K下一键杀死所有同名进程命令行&#xff1a;taskkill /F /IM chrome.exe ############################## 1、准备 Visual Studio 2019 2、准备 Visual Studio 2022 3、安装 VC、MSBuild 编译集成环境 4、安装 Python 3.x&#xff0c;早期V8发行版本编译安装 2.x 5、…

【计算机毕业设计】基于微信小程序实现校园综合服务平台-芒果校园(源码+路演ppt)

项目场景&#xff1a; 这个是之前在准备比赛做的项目&#xff0c;本来拿来去参加的&#xff0c;后面因为一些原因&#xff0c;这个项目被搁置了&#xff0c;今天打开源码 好在还在&#xff0c;但当我打开的时候&#xff0c;接口发生了一些变化&#xff0c;例如 getLocation();…

国际阿里云:云服务器灾备方案!!!

保障企业业务稳定、IT系统功能正常、数据安全十分重要&#xff0c;可以同时保障数据备份与系统、应用容灾的灾备解决方案应势而生&#xff0c;且发展迅速。ECS可使用快照、镜像进行备份。 灾备设计 快照备份 阿里云ECS可使用快照进行系统盘、数据盘的备份。目前&#xff0c;阿…

【Seata源码学习 】 扫描@GlobalTransaction注解 篇一

1. SeataAutoConfiguration 自动配置类的加载 基于SpringBoot的starter机制&#xff0c;在应用上下文启动时&#xff0c;会加载SeataAutoConfiguration自动配置类 # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfigurationio.seata.spring.boot.aut…

【Proteus仿真】【51单片机】多路温度控制系统

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真51单片机控制器&#xff0c;使用按键、LED、蜂鸣器、LCD1602、DS18B20温度传感器、HC05蓝牙模块等。 主要功能&#xff1a; 系统运行后&#xff0c;默认LCD1602显示前4路采集的温…