入门实践,做的东西比较简单,效果如下:
绘制图片需要设置顶点坐标和纹理坐标并加载像素数据,之所以要指定两组坐标是因为纹理和顶点使用不同的坐标系,就是告诉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着色器,下面是主要代码:
- #import
- #import
-
- NS_ASSUME_NONNULL_BEGIN
-
- @interface GLUtil : NSObject
-
- +(GLuint)createProgram:(NSString*)vertexShaderCode :(NSString*)fragmentShaderCode;
-
- +(void)printActiveUniforms:(GLuint)program;
-
- @end
-
- NS_ASSUME_NONNULL_END
- #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
- #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
- #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
- #import
-
- NS_ASSUME_NONNULL_BEGIN
-
- @interface GLImage : NSObject
-
- -(void)loadTexture:(NSString*)imageName;
-
- -(void)draw:(int)positionHandle :(int)textureHandle :(GLuint*)vbo;
-
- @end
-
- NS_ASSUME_NONNULL_END
- #import "GLImage.h"
- #import
-
- //原文链接: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
- #import
-
- NS_ASSUME_NONNULL_BEGIN
-
- @interface GLViewController : GLKViewController {
- }
-
- @end
-
- NS_ASSUME_NONNULL_END
- #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