OpenGL ES /OpenGL ES /OpenGL ES /OpenGL ES /OpenGL ES Image rendering implementation and rendering problems OpenGL/OpenGL ES introduction: basic transformation – beginning vector/matrix OpenGL/OpenGL ES introduction: texture exploration – common API parsing OpenGL/OpenGL ES introduction: OpenGL/OpenGL ES: Vertex shaders and slice shaders (OpenGL transition OpenGL ES) Introduction to OpenGL/OpenGL ES: GLKit usage and case studies

GLKit profile

The GLKit framework is designed to simplify application development based on OpenGL/OpenGL ES. Its appearance speeds up OpenGL or OpenGL ES application development. Use math libraries, background texture loading, pre-created shader effects, and standard views and view controllers to implement the render loop.

The GLKit framework provides functionality and classes that can reduce the amount of work required to create new shader-based applications or support existing applications that rely on fixed function vertex or fragment processing provided by earlier versions of OpenGL or OpenGL ES.

GLKViewController extends the standard UIKit design mode for managing and rendering the contents of a drawing view. Apple deprecated OpenGL ES, but iOS developers can continue to use it.

Case 1: Image rendering

In the first case, we create a class that inherits GLKViewController and import the #import

header file

#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>

@interface ViewController : GLKViewController

@end
Copy the code

#import

#import

Here are the specific steps:

Initialize the context & Set the current context

/ / 1. Initialization context & set the current context _context = [[EAGLContext alloc] initWithAPI: kEAGLRenderingAPIOpenGLES3];if(! _context) { NSLog(@"Create ES context Failed"); } // Set the current context [EAGLContext]setCurrentContext:_context]; / / 2. Obtain GLKView & set the context GLKView * view = [[GLKView alloc] initWithFrame: self. The bounds context: _context]; view.backgroundColor = [UIColor clearColor]; view.delegate = self; [self.view addSubview:view];Copy the code

To initialize the context, we need to select the version of OpenGL ES we are using.

KEAGLRenderingAPIOpenGLES1 = 1, fixed line kEAGLRenderingAPIOpenGLES2 = 2, kEAGLRenderingAPIOpenGLES3 = 3,Copy the code

EAGLContext is the Apple iOS platform implementation of OpenGL ES rendering layer. The kEAGLRenderingAPIOpenGLES1 fixed line similar to the previous article said. While kEAGLRenderingAPIOpenGLES2 and kEAGLRenderingAPIOpenGLES3, which one to use here, difference is not big, just different from version.

Now you need to configure the render buffer for view creation and set the background color

    view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
    view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
    glClearColor(1, 1, 1, 1);
Copy the code

There is a buffer in OpenGL ES that stores the colors displayed on the screen. You can use its properties to set the color format of each pixel in the buffer. GLKViewDrawableColorFormatRGBA8888 = 0, the default value of each pixel of the smallest part of the buffer (RGBA) using 8 bit, (so 4 bytes per pixel, 4 * 8 bit) GLKViewDrawableColorFormatRGB565 if your APP allows a smaller range of colors, you can set this. Makes your APP consume less resources (memory and processing time)

DrawableDepthFormat: Deep cache format

GLKViewDrawableDepthFormatNone = 0 means no depth buffer GLKViewDrawableDepthFormat16, GLKViewDrawableDepthFormat24, If you want to use this property (usually used in 3 d games), you should choose GLKViewDrawableDepthFormat16 or GLKViewDrawableDepthFormat24. The difference here is using GLKViewDrawableDepthFormat16 will consume fewer resources

Load texture data (usingGLBaseEffect)

To use a local image as a texture, the code is as follows:

NSString *filePath = [[NSBundle mainBundle]pathForResource:@"kunkun" ofType:@"jpg"]; / / 2. Set the texture parameters NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys: @ (1), GLKTextureLoaderOriginBottomLeft, nil]; GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:nil]; CEffect = [[GLKBaseEffect alloc]init]; GLKBaseEffect alloc = [GLKBaseEffect alloc]init]; cEffect.texture2d0.enabled = GL_TRUE; cEffect.texture2d0.name = textureInfo.name;Copy the code

In setting up the texture parameters, the need to note that texture coordinates by default the bottom left corner for the origin (0, 0), and the iOS coordinate system, the default screen in the upper left corner of the origin (0, 0), so you need to pay special attention to this, you need to set up GLKTextureLoaderOriginBottomLeft, Otherwise, when the image is displayed, it will flip.

Load vertex & texture data

Look at the code

// Set the vertex array GLfloatVertexData [] = {1, 0.5, 0.0 f to 1.0 f to 0.0 f, / / right 1, 0.5, 0.0 f to 1.0 f to 1.0 f, / / upper right - 1, 0.5, 0.0 f, f 0.0, 1.0, f / / upper 1, - 0.5 and 0.0 f, f 1.0, 0.0, f / / right - 1, 0.5, 0.0 f, f 0.0, 1.0, f / / upper left - 1, 0.5, 0.0 f, f 0.0, 0.0, f / / lower left}; // 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); / / vertex coordinate data glEnableVertexAttribArray (GLKVertexAttribPosition); 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

In this case, we put the vertex coordinates and texture coordinates in an array. In the following case, we use the union mode to store them, mainly to let you know the different ways. It is also possible to write the vertex coordinates and texture coordinates separately into different arrays, but to simplify the code, I chose to write them together.

Vertex array, the developer can optionally set the function pointer, when calling the draw method, directly from memory into the vertex data, that is, the part of the data was previously stored in memory. What we need to do is to open up the vertex buffer, the main purpose is to pursue higher performance, allocate a piece of video memory in advance, the vertex data in advance into the video memory, and the vertex related calculation is carried out in the GPU, so the performance has been greatly improved.

Note: In iOS, all vertex shader Attribute variables are turned off by default for performance reasons, meaning that vertex data is not available on the shader (server). Even if you already use the glBufferData method, copy the vertex data from memory to the vertex buffer (GPU memory); So, must open the channel by glEnableVertexAttribArray method, specify the access attributes, can let the vertex shaders to be able to access to the data from CPU to GPU. Note: data on the GPU is visible, the shader can read data, is determined by whether to enable the corresponding attribute, that’s the function of glEnableVertexAttribArray, allowed to read the vertex shader GPU (server) data. GLKVertexAttribPosition, / / vertex GLKVertexAttribNormal, / / normal GLKVertexAttribColor, / / color GLKVertexAttribTexCoord0, / / GLKVertexAttribTexCoord1 texture

The glVertexAttribPointer method is used to upload vertex data to video memory, i.e. set the appropriate way to read data from buffer.

Parameter 1: specifies the index value of the attribute to be modified. Parameter 2: Number of reads per read (for example, position is composed of 3 (x,y,z), color is 4 (R, G, B, A), and texture is 2). Parameter 3: Specifies the data type of each component in the array. 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: specifies whether fixed point data values should be normalized (GL_TRUE) or directly converted to fixed point values (GL_FALSE) when accessed. Parameter 5: specifies the offset between consecutive vertex attributes. If 0, the vertex attributes are understood to mean that they are tightly packed together. Parameter 6: Specifies a pointer to the first component of the first vertex property in the array. The initial value is 0

The above code, for vertex, each read three, for floating point data types, the offset is to read the next set of vertex data, 5 position, need to move the vertices to read the position of array should be read at the beginning, so the final parameter as NULL + 0 (GLfloat *), written as here is to make it easier for readers to understand, You can also write it as NULL. Therefore, it is not difficult to write (GLfloat *)NULL + 3 for the last parameter of the texture.

Draws the contents of the view

The GLKView object makes the 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. Look at the following code

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { //1. Clear color cache glClear(GL_COLOR_BUFFER_BIT); //2. Prepare [cEffect prepareToDraw]; //3. Say, Say, Say, glDrawArrays(Say, GL_TRIANGLES, 0, 6); }Copy the code

OK, so that’s it in case one, let’s see how it works. (It’s basically just loading an image with UIImageView.)

Case two: Drawing a cube

The demo portal

In case 2, instead of creating classes that inherit from GLKViewController, we write code directly in ViewController, importing the #import

header file directly into the. M file.

Then declare the required attributes:

typedef struct { GLKVector3 positionCoord; GLKVector2 textureCoord; // Texture coordinates GLKVector3 normal; } ZBVertex; // number of vertices static NSInteger const KCoordCount = 36; @interface ViewController () <GLKViewDelegate> @property (nonatomic, strong) GLKView *glkView; @property (nonatomic, strong) GLKBaseEffect *baseEffect; @property (nonatomic, assign) ZBVertex *vertices; // timer @property (nonatomic, strong) CADisplayLink *displayLink; @property (nonatomic, assign) NSInteger Angle; // vertexBuffer ID @property (nonatomic, assign) GLuint vertexBuffer; @endCopy the code

Initialize the context & Set the current context

/ / 1. Create the context EAGLContext * context = [[EAGLContext alloc] initWithAPI: kEAGLRenderingAPIOpenGLES3]; [EAGLContextsetCurrentContext:context]; / / 2. Create GLKView and set up the proxy CGRect frame = CGRectMake (0, 100, the self. View. Frame. The size, width, and the self. The frame. The size, width); self.glkView = [[GLKView alloc]initWithFrame:frame context:context]; self.glkView.backgroundColor = [UIColor redColor]; self.glkView.delegate = self; / / 3. Use the depth buffer self. GlkView. DrawableDepthFormat = GLKViewDrawableDepthFormat24; self.glkView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; GlDepthRangef (1, 0); glDepthRangef(1, 0); [self.view addSubview:self.glkView]; NSString *imagePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"timg.jpeg"]; UIImage *image = [UIImage imageWithContentsOfFile:imagePath]; NSDictionary *options = @{GLKTextureLoaderOriginBottomLeft : @(YES)}; GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithCGImage:[image CGImage] options:options error:nil]; Self.baseeffect = [[GLKBaseEffect alloc]init]; self.baseEffect = [GLKBaseEffect alloc]init]; self.baseEffect.texture2d0.name = textureInfo.name; self.baseEffect.texture2d0.target = textureInfo.target;Copy the code

The initialization code above is almost the same as in Case 1, and the code for loading the texture data is also placed here, so I won’t go into details here.

Load vertex & texture data

Self. vertices = malloc(sizeof(ZBVertex) * kCoordCount); self.vertices = malloc(sizeof(ZBVertex) * kCoordCount); / / in front of the self, are [0] = (ZBVertex) {{0.5, 0.5, 0.5}, {0, 1}}; Self. Are [1] = (ZBVertex) {{0.5, 0.5, 0.5}, {0, 0}}; Self. Are [2] = (ZBVertex) {{0.5, 0.5, 0.5}, {1, 1}}; Self. Are [3] = (ZBVertex) {{0.5, 0.5, 0.5}, {0, 0}}; Self. Are [4] = (ZBVertex) {{0.5, 0.5, 0.5}, {1, 1}}; Self. Are [5] = (ZBVertex) {{0.5, 0.5, 0.5}, {1, 0}}; . / / the above self are [6] = (ZBVertex) {{0.5, 0.5, 0.5}, {1, 1}}; Self. Are [7] = (ZBVertex) {{0.5, 0.5, 0.5}, {0, 1}}; Self. Are [8] = (ZBVertex) {{0.5, 0.5, 0.5}, {1, 0}}; Self. Are [9] = (ZBVertex) {{0.5, 0.5, 0.5}, {0, 1}}; Self. Are [10] = (ZBVertex) {{0.5, 0.5, 0.5}, {1, 0}}; Self. Are [11] = (ZBVertex) {{0.5, 0.5, 0.5}, {0, 0}}; / / self. Below are [12] = (ZBVertex) {{0.5, 0.5, 0.5}, {1, 1}}; Self. Are [13] = (ZBVertex) {{0.5, 0.5, 0.5}, {0, 1}}; Self. Are [14] = (ZBVertex) {{0.5, 0.5, 0.5}, {1, 0}}; Self. Are [15] = (ZBVertex) {{0.5, 0.5, 0.5}, {0, 1}}; Self. Are [16] = (ZBVertex) {{0.5, 0.5, 0.5}, {1, 0}}; Self. Are [17] = (ZBVertex) {{0.5, 0.5, 0.5}, {0, 0}}; / / the left self are [18] = (ZBVertex) {{0.5, 0.5, 0.5}, {1, 1}}; Self. Are [19] = (ZBVertex) {{0.5, 0.5, 0.5}, {0, 1}}; Self. Are [20] = (ZBVertex) {{0.5, 0.5, 0.5}, {1, 0}}; Self. Are [21] = (ZBVertex) {{0.5, 0.5, 0.5}, {0, 1}}; Self. Are [22] = (ZBVertex) {{0.5, 0.5, 0.5}, {1, 0}}; Self. Are [23] = (ZBVertex) {{0.5, 0.5, 0.5}, {0, 0}}; . / / the right self are [24] = (ZBVertex) {{0.5, 0.5, 0.5}, {1, 1}}; Self. Are [25] = (ZBVertex) {{0.5, 0.5, 0.5}, {0, 1}}; Self. Are [26] = (ZBVertex) {{0.5, 0.5, 0.5}, {1, 0}}; Self. Are [27] = (ZBVertex) {{0.5, 0.5, 0.5}, {0, 1}}; Self. Are [28] = (ZBVertex) {{0.5, 0.5, 0.5}, {1, 0}}; Self. Are [29] = (ZBVertex) {{0.5, 0.5, 0.5}, {0, 0}}; / / behind the self are [30] = (ZBVertex) {{0.5, 0.5, 0.5}, {0, 1}}; Self. Are [31] = (ZBVertex) {{0.5, 0.5, 0.5}, {0, 0}}; Self. Are [32] = (ZBVertex) {{0.5, 0.5, 0.5}, {1, 1}}; Self. Are [33] = (ZBVertex) {{0.5, 0.5, 0.5}, {0, 0}}; Self. Are [34] = (ZBVertex) {{0.5, 0.5, 0.5}, {1, 1}}; Self. Are [35] = (ZBVertex) {{0.5, 0.5, 0.5}, {1, 0}}; // Buffers(1, &_vertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); glBufferData(GL_ARRAY_BUFFER, sizeof(ZBVertex) * KCoordCount, self.vertices, GL_STATIC_DRAW); / / vertex data glEnableVertexAttribArray (GLKVertexAttribPosition); glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(ZBVertex), NULL + offsetof(ZBVertex, positionCoord)); / / texture data glEnableVertexAttribArray (GLKVertexAttribTexCoord0); glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(ZBVertex), NULL + offsetof(ZBVertex, textureCoord));Copy the code

Self.vertices = malloc(sizeof(ZBVertex) * KCoordCount); self.vertices = malloc(sizeof(ZBVertex) * KCoordCount); self.vertices = malloc(sizeof(ZBVertex) * KCoordCount); The following code is the same as in case 1.

Create a loop

    self.angle = 0;
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(reDisplay)];
    [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
Copy the code

The above code creates a loop to execute the method reDisplay. CADisplayLink is like a timer and provides a periodic call. It belongs to quartzCore. framework. Specific can refer to the blog www.cnblogs.com/panyangjun/…

Self. Angle = (self. Angle + 1) % 360; self. Angle = (self. Angle + 1) % 360; / / modify baseEffect. Transform. ModelviewMatrix self. BaseEffect. Transform. ModelviewMatrix = GLKMatrix4MakeRotation (GLKMathDegreesToRadians (self Angle), 0.3, 1, 0.7); // re-render [self.glkView display]; }Copy the code

dealloc

- (void)dealloc {
    if ([EAGLContext currentContext] == self.glkView.context) {
        [EAGLContext setCurrentContext:nil];
    }
    if(_vertices) { free(_vertices); _vertexBuffer = 0; } //displayLink invalidate [self.displayLink invalidate]; }Copy the code

End result: