Let’s take a look at the effect: the video is a bit blurry, so let’s make do with it. If you want to see it in HIGH definition, you can run it yourself.

                                  

If you read my article on GLKit in front, you will feel that the effect is not the same as the effect of the previous article? It’s exactly the same effect but instead of using the same texture for each of the six faces, it’s using a different texture for each face. We know that The GLKit provided by Apple only supports 2 textures at most, but here we need to support 6 textures, so we can’t use GLKit anymore, but we can write our own shaders in GLSL to do this.

Vertex shader section

Let’s take a look at the vertex shader section of the code, as follows:

attribute vec4 position; // Attribute vec2 coordPosition; // Texture coordinates varying lowp VEC2 varyingCoordPos; // The texture coordinates passed to the slice shader varying LOWp VEC4 varyingPosition; Uniform MAT4 modelViewMat vertex coordinates passed to the slice shader; // Model view transformation matrix -- the actual rotation matrix void is used for rotationmain() { varyingCoordPos = coordPosition; // Assign the texture coordinates passed to the slice shader varyingPosition = position; // Assign the vertex coordinates passed to the slice shader gl_Position = modelViewMat * position; // Compute the rotated vertex coordinates}Copy the code

The meaning of this code we directly look at the annotation bar, write very clear, need to pay attention to is, really write GLSL code as far as possible do not write Chinese annotations, because sometimes there will be some inexplicable errors. If you don’t know GLSL syntax, check out my previous article – GLSL for the First time

The slice shader section

The fragment shader code is as follows:

uniform sampler2D colorMap1; Uniform sampler2D colorMap2; // Texture 2 Uniform sampler2D colorMap3; // Texture 3 Uniform sampler2D colorMap4; // Texture 4 Uniform sampler2D colorMap5; // Texture 5 Uniform sampler2D colorMap6; // Texture 6 VARYING LOWp VEC2 varyingCoordPos; // The texture coordinate varying lowP VEC4 varyingPosition is passed from the vertex shader. // The vertex coordinates passed by the vertex shader are voidmain() {
    if(varyingPosition. Z = = 0.3) gl_FragColor = texture2D (colorMap1 varyingCoordPos); / / in front of theif(varyingPosition. X = = 0.3) gl_FragColor = texture2D (colorMap2 varyingCoordPos); / / rightif(varyingPosition. Z = = 0.3) gl_FragColor = texture2D (colorMap3 varyingCoordPos); / / behindif(varyingPosition. X = = 0.3) gl_FragColor = texture2D (colorMap4 varyingCoordPos); / / the leftif(varyingPosition. Y = = 0.3) gl_FragColor = texture2D (colorMap5 varyingCoordPos); / / the aboveif(varyingPosition. Y = = 0.3) gl_FragColor = texture2D (colorMap6 varyingCoordPos); / / the following}Copy the code

This code is clearly written in the comments. It defines six sampler2D to represent six different textures, and then determines which texture should be used based on the different vertex coordinates. Note the varyingCoordPos, varyingPosition is not passed directly from the vertex shader, in fact, the vertex shader to the pixel shader in between the image assembly, rasterization, etc. In fact, if you think about it, you can’t pass it directly because the number of times a vertex shader executes is determined by the number of vertices; The number of times the pixel shader executes is determined by the number of pixels, much larger than the number of times the vertex shader executes, so it is not possible to pass directly. In fact, in rasterization, a series of calculations (such as interpolation calculation, etc.) will get the coordinates of each pixel, but that is not our concern. In addition, 0.3 (reflecting the size of the cube) is related to the external coordinates of the vertex, you can adjust according to your own needs.

OC loads custom shaders

In fact, this part of the content in iOS how to use GLSL to write a custom shader has been introduced in detail, here will not go into detail, directly paste the code:

#import "GLView.h"
#import <OpenGLES/ES3/gl.h>
#import <GLKit/GLKit.h>

typedef struct {
    GLKVector3 coordPosition;
    GLKVector2 textureCoord;
} CMVertex;

@interface GLView()

@property (nonatomic, strong) EAGLContext *context;
@property (nonatomic, strong) CAEAGLLayer *eaglLayer;
@property (nonatomic, assign) GLuint program;
@property (nonatomic, assign) GLuint frameBuffer;
@property (nonatomic, assign) GLuint renderBuffer;
@property (nonatomic, assign) CMVertex *vertices;
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic, assign) NSUInteger rotationAngle;
@property (nonatomic, assign) GLKMatrix4 rotationMat;


@end

@implementation GLView


- (id)initWithFrame:(CGRect)frame {
    if(self = [super initWithFrame:frame]) {
        _rotationAngle = 0;
        _rotationMat = GLKMatrix4Identity;
    }
    return self;
}

- (void)layoutSubviews {
    [self prepareContext];
    [self prepareDrawableSuface];
    [self clearBuffers];
    [self prepareRenderBuffer];
    [self prepareFrameBuffer];
    [self setupDisplayLink];
    [self prepareDraw];
    [self draw];
}

+ (Class)layerClass {
    return [CAEAGLLayer class];
}

- (void)prepareDrawableSuface {
    self.eaglLayer = (CAEAGLLayer *) [self layer];
    self.eaglLayer.contentsScale = [UIScreen mainScreen].scale;
    self.eaglLayer.drawableProperties = @{kEAGLDrawablePropertyColorFormat:kEAGLColorFormatRGBA8,kEAGLDrawablePropertyRetainedBacking:@false};
}

- (void)prepareContext {
    self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
    [EAGLContext setCurrentContext:self.context];
}

- (void)clearBuffers {
    glDeleteFramebuffers(1, &_frameBuffer);
    self.frameBuffer = 0;
    glDeleteRenderbuffers(1, &_renderBuffer);
    self.renderBuffer = 0;
}

- (void)prepareRenderBuffer {
    glGenRenderbuffers(1, &_renderBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, self.renderBuffer);
    [self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.eaglLayer];
}

- (void)prepareFrameBuffer {
    glGenFramebuffers(1, &_frameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, self.frameBuffer);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.renderBuffer);
}

- (void)prepareDraw {
    [self createProgram];
    [self prepareCoordData];
    for (int i = 0; i < 6; i++) {
        NSString *imageName = [NSString stringWithFormat:@"%d.jpg",i+11];
        NSString *locationName = [NSString stringWithFormat:@"colorMap%d",i+1];
        [self prepareTextureInfoWithImage:imageName location:locationName texture:GL_TEXTURE0 + i index:i];
    }

    [self prepareRotationMat];
}

- (void)prepareRotationMat {
     GLuint loc = glGetUniformLocation(self.program, "modelViewMat");
    glUniformMatrix4fv(loc, 1, GL_FALSE, &_rotationMat.m00);
}

- (void)setupDisplayLink {
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(rotationCube)];
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)rotationCube {
    _rotationAngle = (self.rotationAngle + 1) % 360;
    GLKMatrix4 rotaMat = GLKMatrix4MakeRotation(GLKMathDegreesToRadians(_rotationAngle), 0.3, 0.7, 0.5);
    self.rotationMat = rotaMat;
    GLuint loc = glGetUniformLocation(self.program, "modelViewMat"); glUniformMatrix4fv(loc, 1, GL_FALSE, &_rotationMat.m00); [self draw]; } - (void)prepareCoordData { _vertices = malloc(sizeof(CMVertex) * 36); / / the front _vertices [0] = (CMVertex) {{0.3, 0.3, 0.3}, {0.0, 1.0}}; _vertices [1] = (CMVertex) {{0.3, 0.3, 0.3}, {0.0, 0.0}}; _vertices [2] = (CMVertex) {{0.3, 0.3, 0.3}, {1.0, 0.0}}; _vertices [3] = (CMVertex) {{0.3, 0.3, 0.3}, {1.0, 0.0}}; _vertices [4] = (CMVertex) {{0.3, 0.3, 0.3}, {1.0, 1.0}}; _vertices [5] = (CMVertex) {{0.3, 0.3, 0.3}, {0.0, 1.0}}; / / right _vertices [6] = (CMVertex) {{0.3, 0.3, 0.3}, {0.0, 1.0}}; _vertices [7] = (CMVertex) {{0.3, 0.3, 0.3}, {0.0, 0.0}}; _vertices [8] = (CMVertex) {{0.3, 0.3, 0.3}, {1.0, 0.0}}; _vertices [9] = (CMVertex) {{0.3, 0.3, 0.3}, {1.0, 0.0}}; _vertices [10] = (CMVertex) {{0.3, 0.3, 0.3}, {1.0, 1.0}}; _vertices [11] = (CMVertex) {{0.3, 0.3, 0.3}, {0.0, 1.0}}; / / behind _vertices [12] = (CMVertex) {{0.3, 0.3, 0.3}, {0.0, 1.0}}; _vertices [13] = (CMVertex) {{0.3, 0.3, 0.3}, {0.0, 0.0}}; _vertices [14] = (CMVertex) {{0.3, 0.3, 0.3}, {1.0, 0.0}}; _vertices [15] = (CMVertex) {{0.3, 0.3, 0.3}, {1.0, 0.0}}; _vertices [16] = (CMVertex) {{0.3, 0.3, 0.3}, {1.0, 1.0}}; _vertices [17] = (CMVertex) {{0.3, 0.3, 0.3}, {0.0, 1.0}}; / / left _vertices [18] = (CMVertex) {{0.3, 0.3, 0.3}, {0.0, 1.0}}; _vertices [19] = (CMVertex) {{0.3, 0.3, 0.3}, {0.0, 0.0}}; _vertices [20] = (CMVertex) {{0.3, 0.3, 0.3}, {1.0, 0.0}}; _vertices [21] = (CMVertex) {{0.3, 0.3, 0.3}, {1.0, 0.0}}; _vertices [22] = (CMVertex) {{0.3, 0.3, 0.3}, {1.0, 1.0}}; _vertices [23] (CMVertex) = {{0.3, 0.3, 0.3}, {0.0, 1.0}}; / / _vertices [24] = (CMVertex) {{0.3, 0.3, 0.3}, {0.0, 1.0}}; _vertices [25] = (CMVertex) {{0.3, 0.3, 0.3}, {0.0, 0.0}}; _vertices [26] = (CMVertex) {{0.3, 0.3, 0.3}, {1.0, 0.0}}; _vertices [27] = (CMVertex) {{0.3, 0.3, 0.3}, {1.0, 0.0}}; _vertices [28] = (CMVertex) {{0.3, 0.3, 0.3}, {1.0, 1.0}}; _vertices [29] = (CMVertex) {{0.3, 0.3, 0.3}, {0.0, 1.0}}; / / the _vertices [30] = (CMVertex) {{0.3, 0.3, 0.3}, {0.0, 1.0}}; _vertices [31] = (CMVertex) {{0.3, 0.3, 0.3}, {0.0, 0.0}}; _vertices [32] = (CMVertex) {{0.3, 0.3, 0.3}, {1.0, 0.0}}; _vertices [33] = (CMVertex) {{0.3, 0.3, 0.3}, {1.0, 0.0}}; _vertices [34] = (CMVertex) {{0.3, 0.3, 0.3}, {1.0, 1.0}}; _vertices [35] = (CMVertex) {{0.3, 0.3, 0.3}, {0.0, 1.0}}; GLuint buffer; glGenBuffers(1, &buffer); glBindBuffer(GL_ARRAY_BUFFER, buffer); glBufferData(GL_ARRAY_BUFFER, sizeof(CMVertex) * 36, self.vertices, GL_STATIC_DRAW); GLuint indx = glGetAttribLocation(self.program,"position");
    glEnableVertexAttribArray(indx);
    glVertexAttribPointer(indx, 3, GL_FLOAT, GL_FALSE, sizeof(CMVertex), NULL + offsetof(CMVertex, coordPosition));
    GLuint textureCoordIndex = glGetAttribLocation(self.program, "coordPosition");
    glEnableVertexAttribArray(textureCoordIndex);
    glVertexAttribPointer(textureCoordIndex, 2, GL_FLOAT, GL_FALSE, sizeof(CMVertex), NULL + offsetof(CMVertex, textureCoord));
}

- (GLuint)shaderWithType:(GLenum)type path:(NSString *)sourcePath {
    GLuint shader = glCreateShader(type);
    NSString *content = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil];
    
    const char *sourceContent = [content UTF8String];
    glShaderSource(shader, 1, &sourceContent, NULL);
    glCompileShader(shader);
    GLint status;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
    if(status == GL_FALSE) {
        char message[512];
        glGetShaderInfoLog(shader, sizeof(message), NULL, message);
        NSLog(@"compile shader error: %@",[NSString stringWithUTF8String:message]);
        glDeleteShader(shader);
        return 0;
    }
    return shader;
}

- (void)createProgram {
    
    self.program = glCreateProgram();
    NSString *vertexPath = [[NSBundle mainBundle] pathForResource:@"shader" ofType:@"vsh"];
    NSString *fragmentPath = [[NSBundle mainBundle] pathForResource:@"shader" ofType:@"fsh"];
    GLuint vertexShader = [self shaderWithType:GL_VERTEX_SHADER path:vertexPath];
    GLuint fragmentShader = [self shaderWithType:GL_FRAGMENT_SHADER path:fragmentPath];
    glAttachShader(self.program, vertexShader);
    glAttachShader(self.program, fragmentShader);
    glLinkProgram(self.program);
    GLint status;
    glGetProgramiv(self.program, GL_LINK_STATUS, &status);
    if(! status) { char message[1024]; glGetProgramInfoLog(self.program, sizeof(message), NULL, message); NSLog(@"program link error:%@",[NSString stringWithUTF8String:message]);
        glDeleteProgram(self.program);
        return;
    }
//    NSLog(@"program link success"); glDeleteShader(vertexShader); glDeleteShader(fragmentShader); glUseProgram(self.program); } - (void)prepareTextureInfoWithImage:(NSString *)imageName location:(NSString *)locName texture:(GLenum)tex index:(GLint)index { UIImage *image = [UIImage imageNamed:imageName]; CGImageRef imageRef = image.CGImage; size_t width = CGImageGetWidth(imageRef); size_t height = CGImageGetHeight(imageRef); void * imageData = calloc(width * height * 4, sizeof(GLbyte)); CGContextRef context = CGBitmapContextCreate(imageData, width, height, 8, width * 4, CGImageGetColorSpace(imageRef), CGImageGetBitmapInfo(imageRef)); CGContextTranslateCTM(context, 0, height); CGContextScaleCTM(context, 1, -1); CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); CGContextRelease(context); GLuint texe; glGenTextures(1, &texe); glActiveTexture(tex); glBindTexture(GL_TEXTURE_2D, texe); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); GLsizei w = (GLsizei)width; GLsizei h = (GLsizei)height; glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData); free(imageData); GLuint textureLocation = glGetUniformLocation(self.program, [locName UTF8String]); glUniform1i(textureLocation, index); } - (void)draw {glClearColor(0.3, 0.5, 0.12, 1); glClear(GL_COLOR_BUFFER_BIT); CGSize size = self.bounds.size; CGFloat scale = [[UIScreen mainScreen] scale]; glViewport(0, 0, size.width * scale, size.height * scale); glEnable(GL_CULL_FACE); glDrawArrays(GL_TRIANGLES, 0, 36); [self.context presentRenderbuffer: GL_RENDERBUFFER]; } - (void)dealloc {if([EAGLContext currentContext] == self.context) {
        [EAGLContext setCurrentContext:nil];
    }
    if (self.program) {
        glDeleteProgram(self.program);
        self.program = 0;
    }
    if (_vertices) {
        free(_vertices);
        _vertices = nil;
    }
    [self.displayLink invalidate];

}
@endCopy the code

If you don’t understand this code, you can first look at the article about how to use GLSL custom shader in iOS. In fact, you can compare the previous case about GLKit application, so that you can know more clearly what the so-called GLBaseEfact in GLKit is. In fact, it is similar to our program here. It is a program packaged by Apple that has its own vertex shader and slice shader.

Related articles:
  1. OpenGL first lesson — Name explanation
  2. OpenGL is introduced in lesson 2 – The common fixed storage shader

  3. OpenGL introduction third lesson — matrix transformation and coordinate system

  4. OpenGL entry lesson 4 — Depth

  5. OpenGL entry lesson 5 — front and back elimination

  6. OpenGL entry lesson 6 — Clipping and Blending

  7. OpenGL entry lesson 7 — Texture

  8. OpenGL is in class 8 — a supplementary case

  9. OpenGL ES que

  10. The application of GLKit

  11. GLSL met

  12. How to use custom shaders written in GLSL in iOS

  13. Precision qualifier in GLSL