• 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

    1. #import
    2. #import
    3. NS_ASSUME_NONNULL_BEGIN
    4. @interface GLUtil : NSObject
    5. +(GLuint)createProgram:(NSString*)vertexShaderCode :(NSString*)fragmentShaderCode;
    6. +(void)printActiveUniforms:(GLuint)program;
    7. @end
    8. NS_ASSUME_NONNULL_END

    GLUtil.m

    1. #import "GLUtil.h"
    2. @implementation GLUtil
    3. + (NSString *)readShaderSource:(NSString *)bundleFileName {
    4. NSString* path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:bundleFileName];
    5. NSString* content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    6. // NSLog(@"%@", content);
    7. return content;
    8. }
    9. + (GLuint)loadShader:(GLenum)type :(const char *)shaderCode {
    10. GLuint shader = glCreateShader(type);
    11. glShaderSource(shader, 1, &shaderCode, NULL);
    12. glCompileShader(shader);
    13. GLint commpiled;
    14. glGetShaderiv(shader, GL_COMPILE_STATUS, &commpiled);
    15. if(!commpiled) {
    16. GLint infoLen = 0;
    17. glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
    18. if(infoLen > 0) {
    19. char* infoLog = malloc(infoLen);
    20. glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
    21. NSLog(@"%s", infoLog);
    22. free(infoLog);
    23. glDeleteShader(shader);
    24. return 0;
    25. }
    26. }
    27. return shader;
    28. }
    29. + (GLuint)createProgram:(NSString *)vertexShaderFileName :(NSString *)fragmentShaderFileName {
    30. GLuint program = glCreateProgram();
    31. const char* vertexShaderCode = [[GLUtil readShaderSource:vertexShaderFileName] UTF8String];
    32. const char* fragmentShaderCode = [[GLUtil readShaderSource:fragmentShaderFileName] UTF8String];
    33. GLuint vertexShader = [self loadShader:GL_VERTEX_SHADER:vertexShaderCode];
    34. GLuint fragmentShader = [self loadShader:GL_FRAGMENT_SHADER:fragmentShaderCode];
    35. if (vertexShader == 0 || fragmentShader == 0) {
    36. return 0;
    37. }
    38. glAttachShader(program, vertexShader);
    39. glAttachShader(program, fragmentShader);
    40. glLinkProgram(program);
    41. GLint linked;
    42. glGetProgramiv(program, GL_LINK_STATUS, &linked);
    43. if(!linked) {
    44. GLint infoLen = 0;
    45. glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
    46. if(infoLen > 0) {
    47. char* infoLog = malloc(infoLen);
    48. glGetProgramInfoLog(program, infoLen, NULL, infoLog);
    49. NSLog(@"%s", infoLog);
    50. free(infoLog);
    51. glDeleteProgram(program);
    52. return 0;
    53. }
    54. }
    55. glDeleteShader(vertexShader);
    56. glDeleteShader(fragmentShader);
    57. return program;
    58. }
    59. + (void)printActiveUniforms:(GLuint)program {
    60. GLint maxUniformLen;//变量名的最大长度
    61. GLint numUniforms;//变量个数
    62. char* uniformName;//变量名
    63. glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &numUniforms);
    64. glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformLen);
    65. uniformName = malloc(maxUniformLen);
    66. for (GLint index = 0; index < numUniforms; index++) {
    67. GLint size;
    68. GLenum type;
    69. GLint location;
    70. glGetActiveUniform(program, index, maxUniformLen, NULL, &size, &type, uniformName);
    71. location = glGetUniformLocation(program, uniformName);
    72. NSString* typeStr;
    73. switch (type) {
    74. case GL_FLOAT:
    75. typeStr = (@"GL_FLOAT");
    76. break;
    77. case GL_FLOAT_VEC2:
    78. typeStr = (@"GL_FLOAT_VEC2");
    79. break;
    80. case GL_FLOAT_VEC3:
    81. typeStr = (@"GL_FLOAT_VEC3");
    82. break;
    83. case GL_FLOAT_VEC4:
    84. typeStr = (@"GL_FLOAT_VEC4");
    85. break;
    86. case GL_FLOAT_MAT4:
    87. typeStr = (@"GL_FLOAT_MAT4");
    88. break;
    89. case GL_BOOL:
    90. typeStr = (@"GL_BOOL");
    91. break;
    92. case GL_SAMPLER_2D:
    93. typeStr = (@"GL_SAMPLER_2D");
    94. break;
    95. default:
    96. typeStr = [NSString stringWithFormat:@"变量类型:0x%X", type];
    97. break;
    98. }
    99. NSLog(@"index=%d, name=%s, size=%d, type=%@, location=%d", index, uniformName, size, typeStr, location);
    100. }
    101. free(uniformName);
    102. }
    103. @end

    vertexShader.glsl

    顶点着色器代码 vertexShader.glsl

    1. #version 300 es
    2. uniform mat4 uMVPMatrix;
    3. uniform mat4 translateMatrix;
    4. uniform bool isTranslate;
    5. layout(location = 0) in vec4 vPosition;
    6. layout(location = 1) in vec2 aTextureCoord;
    7. out vec2 vTexCoord;
    8. void main() {
    9. if (isTranslate) {
    10. gl_Position = uMVPMatrix * translateMatrix * vPosition;
    11. } else {
    12. gl_Position = uMVPMatrix * vPosition;
    13. }
    14. vTexCoord = aTextureCoord;
    15. }

    fragmentShader.glsl

    片段着色器代码 fragmentShader.glsl

    1. #version 300 es
    2. precision mediump float;
    3. uniform bool isTexture;
    4. uniform vec4 vColor;
    5. uniform sampler2D uTextureUnit;
    6. in vec2 vTexCoord;
    7. out vec4 fragColor;
    8. void main() {
    9. if(isTexture) {
    10. fragColor = texture(uTextureUnit,vTexCoord);
    11. } else {
    12. fragColor = vColor;
    13. }
    14. }

    绘制2D纹理的类 GLImage

    GLImage.h

    1. #import
    2. NS_ASSUME_NONNULL_BEGIN
    3. @interface GLImage : NSObject
    4. -(void)loadTexture:(NSString*)imageName;
    5. -(void)draw:(int)positionHandle :(int)textureHandle :(GLuint*)vbo;
    6. @end
    7. NS_ASSUME_NONNULL_END

    GLImage.m

    1. #import "GLImage.h"
    2. #import
    3. //原文链接:https://blog.csdn.net/gongxiaoou/article/details/89344561
    4. //图片尺寸:2385 × 3623
    5. static const float POSITION_VERTEX[] = {
    6. 0.0f, 0.0f, //顶点坐标V0
    7. 1.0f, 1.52f, //顶点坐标V1
    8. -1.0f, 1.52f, //顶点坐标V2
    9. -1.0f, -1.52f, //顶点坐标V3
    10. 1.0f, -1.52f //顶点坐标V4
    11. };
    12. static const float TEX_VERTEX[] = {
    13. 0.5f, 0.5f, //纹理坐标V0
    14. 1.0f, 1.0f, //纹理坐标V1
    15. 0.0f, 1.0f, //纹理坐标V2
    16. 0.0f, 0.0f, //纹理坐标V3
    17. 1.0f, 0.0f //纹理坐标V4
    18. };
    19. static const short VERTEX_INDEX[] = {
    20. 0, 1, 2, //V0,V1,V2 三个顶点组成一个三角形
    21. 0, 2, 3, //V0,V2,V3 三个顶点组成一个三角形
    22. 0, 3, 4, //V0,V3,V4 三个顶点组成一个三角形
    23. 0, 4, 1 //V0,V4,V1 三个顶点组成一个三角形
    24. };
    25. @interface GLImage()
    26. {
    27. GLuint textureID;
    28. }
    29. @end
    30. @implementation GLImage
    31. //原文链接:https://blog.csdn.net/qq_30513483/article/details/101538967
    32. - (void)loadTexture:(NSString *)imageName {
    33. UIImage *image = [UIImage imageNamed:imageName];
    34. // 将 UIImage 转换为 CGImageRef
    35. CGImageRef cgImageRef = [image CGImage];
    36. GLuint width = (GLuint)CGImageGetWidth(cgImageRef);
    37. GLuint height = (GLuint)CGImageGetHeight(cgImageRef);
    38. CGRect rect = CGRectMake(0, 0, width, height);
    39. // 绘制图片
    40. CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    41. void *imageData = malloc(width * height * 4);
    42. CGContextRef context = CGBitmapContextCreate(imageData, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    43. CGContextTranslateCTM(context, 0, height);
    44. CGContextScaleCTM(context, 1.0f, -1.0f);
    45. CGColorSpaceRelease(colorSpace);
    46. CGContextClearRect(context, rect);
    47. CGContextDrawImage(context, rect, cgImageRef);
    48. // 生成纹理
    49. glGenTextures(1, &textureID);
    50. glBindTexture(GL_TEXTURE_2D, textureID);
    51. // 将图片数据写入纹理缓存
    52. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
    53. // 设置如何把纹素映射成像素
    54. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    55. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    56. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    57. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    58. // 解绑
    59. glBindTexture(GL_TEXTURE_2D, 0);
    60. // 释放内存
    61. CGContextRelease(context);
    62. free(imageData);
    63. }
    64. - (void)draw:(int)positionHandle :(int)textureHandle :(GLuint*)vbo {
    65. //顶点坐标
    66. glEnableVertexAttribArray(positionHandle);
    67. glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
    68. glBufferData(GL_ARRAY_BUFFER, sizeof(POSITION_VERTEX), POSITION_VERTEX, GL_STATIC_DRAW);
    69. glVertexAttribPointer(positionHandle, 2, GL_FLOAT, GL_FALSE, 0, 0);
    70. //纹理坐标
    71. glEnableVertexAttribArray(textureHandle);
    72. glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
    73. glBufferData(GL_ARRAY_BUFFER, sizeof(TEX_VERTEX), TEX_VERTEX, GL_STATIC_DRAW);
    74. glVertexAttribPointer(textureHandle, 2, GL_FLOAT, GL_FALSE, 0, 0);
    75. //激活纹理
    76. glActiveTexture(GL_TEXTURE0);
    77. //绑定纹理
    78. glBindTexture(GL_TEXTURE_2D, textureID);
    79. //绘制
    80. glDrawElements(GL_TRIANGLES, sizeof(VERTEX_INDEX)/sizeof(short), GL_UNSIGNED_SHORT, VERTEX_INDEX);
    81. }
    82. @end

    GLViewController.h

    1. #import
    2. NS_ASSUME_NONNULL_BEGIN
    3. @interface GLViewController : GLKViewController {
    4. }
    5. @end
    6. NS_ASSUME_NONNULL_END

    GLViewController.m

    1. #import "GLViewController.h"
    2. #import "GLUtil.h"
    3. #import "GLImage.h"
    4. const int VBO_NUM = 2;
    5. static const GLfloat vertices[] = {
    6. 0.0f, 0.5f, 0.0f,
    7. -0.5f, -0.5f, 0.0f,
    8. 0.5f, -0.5f, 0.0f
    9. };
    10. @interface GLViewController ()
    11. {
    12. GLuint program;
    13. int muMVPMatrixHandle;
    14. int translateMatrixHandle;
    15. int isTranslateHandle;
    16. int isTextureHandle;
    17. int colorHandle;
    18. GLKMatrix4 vMatrix;
    19. GLKMatrix4 projMatrix;
    20. GLKMatrix4 vPMatrix;
    21. GLKMatrix4 translateMatrix;
    22. GLuint vbo[VBO_NUM];
    23. GLImage* glImage;
    24. }
    25. @end
    26. @implementation GLViewController
    27. - (void)initGL{
    28. program = [GLUtil createProgram:@"vertexShader.glsl" :@"fragmentShader.glsl"];
    29. glUseProgram(program);
    30. muMVPMatrixHandle = glGetUniformLocation(program, "uMVPMatrix");
    31. translateMatrixHandle = glGetUniformLocation(program, "translateMatrix");
    32. isTranslateHandle = glGetUniformLocation(program, "isTranslate");
    33. isTextureHandle = glGetUniformLocation(program, "isTexture");
    34. colorHandle = glGetUniformLocation(program, "vColor");
    35. glGenBuffers(VBO_NUM, vbo);
    36. [GLUtil printActiveUniforms:program];
    37. }
    38. - (void)viewDidLoad {
    39. [super viewDidLoad];
    40. // Do any additional setup after loading the view.
    41. GLKView* glView = (GLKView*)self.view;
    42. glView.context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];
    43. [EAGLContext setCurrentContext:glView.context];//这句必须得有,不然画不出来图形
    44. [self initGL];
    45. glImage = [GLImage new];
    46. [glImage loadTexture:@"dog.jpg"];
    47. // Combine the projection and camera view matrices
    48. vMatrix = GLKMatrix4MakeLookAt(0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
    49. }
    50. - (void) onSurfaceChange:(float)width :(float)height {
    51. NSLog(@"%fx%f", width, height);
    52. glViewport(0, 0, width, height);
    53. //投影矩阵
    54. if (width > height) {
    55. float ratio = width / height;
    56. projMatrix = GLKMatrix4MakeFrustum(-ratio, ratio, -1.0f, 1.0f, 3.0f, 7.0f);
    57. } else {
    58. float ratio = height / width;
    59. projMatrix = GLKMatrix4MakeFrustum(-1.0f, 1.0f, -ratio, ratio, 3.0f, 7.0f);
    60. }
    61. // Combine the projection and camera view matrices
    62. vPMatrix = GLKMatrix4Multiply(projMatrix, vMatrix);
    63. }
    64. - (void)viewWillLayoutSubviews {
    65. [super viewWillLayoutSubviews];
    66. float width = self.view.frame.size.width;
    67. float height = self.view.frame.size.height;
    68. NSLog(@"%s %fx%f", __func__, width, height);
    69. [self onSurfaceChange:width :height];
    70. }
    71. - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    72. // NSLog(@"%s %f", __func__, [[NSDate new] timeIntervalSince1970]);
    73. // Apply the combined projection and camera view transformations
    74. glUniformMatrix4fv(muMVPMatrixHandle, 1, false, (float*)&vPMatrix);
    75. glClearColor(0.9f, 0.9f, 0.9f, 1.0f);//背景色
    76. glClear(GL_COLOR_BUFFER_BIT);
    77. //画图片
    78. glUniform1i(isTranslateHandle, 0);//禁用矩阵偏移
    79. glUniform1i(isTextureHandle, 1);//启用纹理
    80. [glImage draw:0 :1 :vbo];
    81. double degrees = (long)([[NSDate new] timeIntervalSince1970] * 50) % 360;
    82. double radians = degrees * M_PI / 180.0;
    83. // NSLog(@"degrees=%f", degrees);
    84. translateMatrix = GLKMatrix4Translate(GLKMatrix4Identity, cos(radians), sin(radians), 0.0f);
    85. glUniformMatrix4fv(translateMatrixHandle, 1, GL_FALSE, (float*)&translateMatrix);
    86. glUniform1i(isTranslateHandle, 1);//启用矩阵偏移
    87. glUniform1i(isTextureHandle, 0);//禁用纹理
    88. glUniform4f(colorHandle, 0.0f, 0.5f, 1.0f, 1.0f);
    89. glEnableVertexAttribArray(0);
    90. glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
    91. glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    92. glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    93. glDrawArrays(GL_TRIANGLES, 0, 3);
    94. }
    95. @end

  • 相关阅读:
    Linux FrameBuf介绍
    vector中的迭代器失效问题
    ddddocr1.4.8失效的解决方法
    seata事务回滚引起的skywalking数据库存储空间剧增的问题排查
    『NLP学习笔记』TextCNN文本分类原理及Pytorch实现
    【Hack The Box】linux练习-- Valentine
    高等教育学备考:教育学概述
    iOS基础开发介绍
    Qt —UDP通信QUdpSocket 简介 +案例
    App移动端测试(3)—— ADB命令
  • 原文地址:https://blog.csdn.net/jjf19891208/article/details/134362510