The purpose of this case is to familiarize yourself with the GLKit framework. If you still don’t know about it, you can take a look at this article

The overall effect is as follows:

The preparatory work

  • Create an iOS project and change the parent class of the ViewController created by the system from UIViewController toGLKViewController, where the view’s parent class is changed from UIView toGLKView
  • OC version
    • Import the GLKit framework header file in viewController.h#import <GLKit/GLKit.h>
    • Import Opengl ES headers in viewController. h#import <OpenGLES/ES3/gl.h>、#import <OpenGLES/ES3/glext.h>
  • Swift version
    • Import the GLKit framework directly into viewController.swiftimport GLKit

So how do you load images using this framework and the overall flow chart is as follows

There are mainly the following steps:

  • SetupConfig: OpenGL ES related initialization, mainly initialization context and GLKView object
  • SetupVertex: Sets vertex data, including vertex coordinates and texture coordinates
  • SetupTexture function: Sets the texture
  • GLKViewDelegate delegate method: Draws textures to the screen

Note: The code is divided into OC and SWIFT versions

SetupConfig function

This function is mainly to initialize the context, set the GLKView view and set the background color, the overall process is as follows

According to the flow chart, it is divided into four parts

  • Initialize the context
  • Get the GLKView object and set its context,
  • Configuring the render buffer
  • Set the background color

Initialize the context

It is primarily the initialization context, which can have multiple contexts in a project, but is currently limited to one

  • OC version
/ / 1. Initialization context & set the current context / * EAGLContext OpenGLES render layer is apple's iOS platform. KEAGLRenderingAPIOpenGLES1 = 1, Fixed line kEAGLRenderingAPIOpenGLES2 = 2, kEAGLRenderingAPIOpenGLES3 = 3, */ context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3]; // Check whether the context is created successfully. context) { NSLog(@"Create ES context Failed"); } / / set the current context [EAGLContext setCurrentContext: context];Copy the code
  • Swift version
Eaglcontext. init(API: eaglContext. init(API: eaglContext. init)) .opengles3) guard let cont = self.context else{return} //2, eaglContext.setCurrent (cont)Copy the code

Get the GLKView object and set its context,

Since the view of the controller has been changed to GLKView in the previous preparation, all you need to do is get the View, convert the type to GLKView, and set its context to the newly initialized context

Context GLKView *view =(GLKView *) self.view; view.context = context; //Swift version let glView = self.view as! GLKView glView.context = contCopy the code

Configure buffers

It is mainly to configure the color cache and depth cache

Color cache format

Through GLKView attribute drawableColorFormat Settings, has the following three, default is GLKViewDrawableColorFormatRGBA8888

  • GLKViewDrawableColorFormatRGBA8888: at the end of the 8888 indicates RGBA 8 bit of each
  • GLKViewDrawableColorFormatRGB565: if the app allows a smaller range of color, can be set to this value, can let the app consumes fewer resources
  • GLKViewDrawableColorFormatSRGBA8888,

The depth buffer format Through GLKView attribute drawableDepthFormat Settings, has the following three, default is GLKViewDrawableDepthFormat24

  • GLKViewDrawableDepthFormatNone: it means no buffer
  • GLKViewDrawableDepthFormat16: generally used for 3 d games, compared to 24, consume less resources
  • GLKViewDrawableDepthFormat24: generally used for 3 d games
/ / / / OC version 3. Configuration view create render buffer. The drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; view.drawableDepthFormat = GLKViewDrawableDepthFormat16; / / swift version glView. DrawableColorFormat =. RGBA8888 glView. DrawableDepthFormat =. Format24Copy the code

Set the background color

This background color is set for GLKView, this method is basically the same in OC and Swift

GlClearColor (0.3, 0.4, 0.5, 1.0)Copy the code

SetupVertex function

Basically setting up vertex data and passing data from CPU to GPU

It is mainly divided into three parts:

  • Create vertex data
  • Creating a vertex buffer
  • Open the channel

Create vertex data using a one-dimensional array, each vertex contains 5 data, namely vertex coordinates (x, y, z) + texture coordinates (S, T)

1. Set the vertex array (vertex coordinates, texture coordinates) /* texture coordinates range [0,1]; The origin is the lower left corner (0,0); So (0, 0) is the lower left corner of the texture image, point (1, 1) is the top right corner. * / GLfloat vertexData [] = {0.5, 0.5, 0.0 f, f 1.0, 0.0, f / / lower 0.5, 0.5, 0.0 f, 1.0 f, 1.0 f, / / right - 0.5, 0.5, 0.0 f, f 0.0, 1.0, f / / upper left 0.5, 0.5, 0.0 f, f 1.0, 0.0, f / / right - 0.5, 0.5, 0.0 f, f 0.0, 1.0, f / / upper left - 0.5, 0.5, 0.0f, 0.0f, 0.0f, 0.0f, // bottom left}; Var vertexData: [GLfloat] = [0.5, 0.5, 0.0, 1.0, 0.0, / / lower 0.5, 0.5, 0.0, 1.0, 1.0, / / upper right - 0.5, 0.5, 0.0, 0.0, 1.0, / / upper left 0.5, 0.5, 0.0, 1.0, 0.0, / / right - 0.5, 0.5, 0.0, 0.0, 1.0, / / upper left - 0.5, 0.5, 0.0, 0.0, 0.0, / / bottom left];Copy the code

Opening up a vertex buffer is to copy the vertex data in memory into the vertex buffer

  • Vertex data: The developer can optionally set the function pointer to pass the vertex data directly from memory when calling the draw method, i.e. the vertex data is stored in memory
  • Vertex buffer: The high performance approach is to allocate a piece of video memory in advance, the vertex data into the video memory, this part of the video memory is the vertex buffer

The copy process is divided into three steps

  • Create the vertex cache identifier ID
  • Bind the vertex buffer to clarify the purpose of the display
  • Copy data from memory to GPU memory

The specific code is as follows:

  • OC version
//(1). Create vertex buffer identifier ID GLuint bufferID; glGenBuffers(1, &bufferID); GlBindBuffer (GL_ARRAY_BUFFER, bufferID); GL_ARRAY_BUFFER (GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);Copy the code
  • Swift version
Var bufferID: Buffers(1, &bufferID); glBindBuffer(GLenum(GL_ARRAY_BUFFER); GLenum(GL_ARRAY_BUFFER), MemoryLayout<GLfloat>. Stride * vertexdata.count, &vertexData, GLenum(GL_STATIC_DRAW))Copy the code

Note: OC sizeof computes the sizeof the data. This does not apply to swift. Swift is safe by default, meaning that memory operations are prohibited in swift. So MemoryLayout

. Stride * vertexdata.count can be used to get the size of the data in memory

In ios, all vertex shaders Attribute variables are turned off by default, meaning that vertex shaders are not available on the server. Even if you use glBufferData, the vertex shader cannot read the passed vertex data and therefore cannot see the desired effect

  • Must passglEnableVertexAttribArrayMethod to enable vertex shaders to access data copied from the CPU to the GPU
  • Note: Whether the data is visible on the GPU, that is, whether the shader can read the data, depends on whether the Attribute channel is enabled. If the Attribute channel is enabled, it can be read; if not, it cannot be read

Since there are two coordinates (vertex coordinates + texture coordinates), the channel needs to be opened twice, as shown in the following code

  • OC version
/ / vertex coordinate data glEnableVertexAttribArray (GLKVertexAttribPosition); /* Parameter 1-index: specifies the index value of the vertex attribute to be modified, for example, GLKVertexAttribPosition parameter 2-size: Position is 3 (x, y, z), color is 4 (R, G, B, A), texture is 2 (s, t) parameter 3-type: For each component in the array, the available symbolic constants are GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT, GL_FIXED, and GL_FLOAT, starting with GL_FLOAT. Parameter 4-normalized: specifies whether fixed point data values should be normalized (GL_TRUE) or converted to fixed point values when accessed. Default is GL_FALSE. Parameter 5-stride: specifies the offset between consecutive vertex attributes. If 0, the vertex attributes are understood to mean that they are tightly packed together. 6- PTR: Specifies a pointer to the first component of the first vertex property in the array. GlVertexAttribPointer (GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 0); / / texture coordinates data glEnableVertexAttribArray (GLKVertexAttribTexCoord0); glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 3);Copy the code
  • Swift version
//swift GLsizei(MemoryLayout<CGFloat>. Size * 5) //swift GLsizei(MemoryLayout<CGFloat>. UnsafeMutablePointer<GLubyte> glEnableVertexAttribArray(GLuint(GLKVertexAttrib.position.rawValue)) glVertexAttribPointer(GLuint(GLKVertexAttrib.position.rawValue), 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(MemoryLayout<GLfloat>.stride*5), nil) glEnableVertexAttribArray(GLuint(GLKVertexAttrib.texCoord0.rawValue)) glVertexAttribPointer(GLuint(GLKVertexAttrib.texCoord0.rawValue), 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(MemoryLayout<GLfloat>.stride*5), UnsafeMutableRawPointer(bitPattern: 3*MemoryLayout<GLfloat>.stride))Copy the code

Note: The last argument to glVertexAttribPointer is (GLfloat *)NULL + 0 in OC or UnsafeRawPointer in Swift. Since the subscript changes dynamically, To create a pointer, use UnsafeMutableRawPointer

SetupTexture function

Basically set up the texture and use apple GLKit to provide GLKBaseEffect to do the shader work (vertices/tiles)

It is divided into the following three steps:

  • Gets the texture image path
  • Setting texture Parameters
  • Initialize effect and set the properties

Gets the texture image path

This is a common operation in iOS development, so I won’t go into detail

//OC version NSString *filePath = [[NSBundle mainBundle]pathForResource:@"kunkun" ofType:@" JPG "]; //Swift let path = bundle.main. path(forResource: "mouse", ofType: "JPG ")Copy the code

Setting texture Parameters

Mainly set the texture mapping with screen coordinates, because the texture of the default origin (0, 0) in the lower left corner, and origin in the upper left corner of the screen, need to set it to GLKTextureLoaderOriginBottomLeft

  • OC version
// The origin of the texture coordinates is the lower left corner, but the image shows that the origin should be the upper left corner. NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:@(1),GLKTextureLoaderOriginBottomLeft, nil]; GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:nil];Copy the code
  • Swift version
//2, set guard let textureInfo = try? GLKTextureLoader.texture(withContentsOfFile: path! , options: [GLKTextureLoaderOriginBottomLeft:NSNumber.init(integerLiteral: 1)] ) else { return }Copy the code

Initialize the effect

Use apple GLKit to provide GLKBaseEffect for shaders (vertices/tiles)

CEffect = [[GLKBaseEffect alloc]init]; cEffect.texture2d0.enabled = GL_TRUE; cEffect.texture2d0.name = textureInfo.name; / / Swift version effect = GLKBaseEffect () effect. Texture2d0. Enabled = GLboolean (GL_TRUE) effect. Texture2d0. Name = textureInfo.nameCopy the code

Proxy method

GLKViewDelegate’s proxy method, which draws the contents of the view

  • OC version
/* The GLKView object makes its OpenGL ES context the current context and binds its framebuffer to the target of the OpenGL ES render command. The delegate method should then draw the contents of the view. */- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{ //1. glClear(GL_COLOR_BUFFER_BIT); //2. Prepare [cEffect prepareToDraw]; //3. Say, Say, Say, glDrawArrays(Say, GL_TRIANGLES, 0, 6); }Copy the code
  • Swift version
override func glkView(_ view: GLKView, drawIn rect: CGRect) {glClear(GLbitfield(GL_COLOR_BUFFER_BIT)) // prepareToDraw effect.preparetodraw () // start drawing glDrawArrays(GLenum(GL_TRIANGLES), 0, 6) }Copy the code

For the complete code, see Github-08_glkit_image_OC and 08_GLKit_Image_Swift, which are available in OC and Swift versions respectively