rendering

Process logic

Program execution flow

ViewDidLoad function (set background + create toggle filter effect toolbar) –> filterInit function (initialize context, prepare vertex and texture loading) –> shader load/compile, program use –> startFilerAnimation

When toggling the bottom FilterBar –> toggle the split screen option, use the corresponding shader to load/compile, and use –> startFilerAnimation for program

Built-in properties

  • Define vertex coordinates and texture coordinates structure SenceVertex
typedef struct {
    GLKVector3 positionCoord; // (X, Y, Z)
    GLKVector2 textureCoord; // (U, V)
} SenceVertex;
Copy the code
  • Context EAGLContext –>EAGLContext
  • The timer refreshed CADisplayLink and the start time NSTimeInterval
  • Shader program handle program– >GLuint
  • VertexBuffer –>GLuint
  • Texture ID textureID – > GLuint

SetupFilterBar Bottom filter scroll bar

Create the bottom part of the screen options toolbar, using the FilterBarDelegate callback to change the toolbar options action

@protocol FilterBarDelegate <NSObject>

- (void)filterBar:(FilterBar *)filterBar didScrollToIndex:(NSUInteger)index;

@end
Copy the code

The filterInit filter handles initialization

  • Initialize the context and set it to the current context
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:self.context];
Copy the code
  • Initialize vertex coordinates (0,1,2,3) and texture coordinates
self.vertices = malloc(sizeof(SenceVertex) * 4);
self.vertices[0] = (SenceVertex){{-1, 1, 0}, {0, 1}};
self.vertices[1] = (SenceVertex){{-1, -1, 0}, {0, 0}};
self.vertices[2] = (SenceVertex){{1, 1, 0}, {1, 1}};
self.vertices[3] = (SenceVertex){{1, -1, 0}, {1, 0}};
Copy the code
  • Bind the render cache bindRenderLayer
    • Create render cache, frame cache object
GLuint renderBuffer;
GLuint frameBuffer;
Copy the code
  • Gets the frame render cache name, binds the render cache and connects the render cache to the layer
glGenRenderbuffers(1, &renderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);
[self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer];
Copy the code
  • Gets the frame cache name, binds the frame cache, and attaches the render cache to the frame cache
glGenFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,
                            GL_COLOR_ATTACHMENT0,
                            GL_RENDERBUFFER,
                            renderBuffer);
Copy the code
  • Get texture image path, read image UIImage
NSString *imagePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"kunkun.jpg"]; / / read pictures UIImage * image = [UIImage imageWithContentsOfFile: imagePath];Copy the code
  • To load a texture from an image, return the texture ID: texttureID

    • Convert UIImage to CGImageRef

    • Read the size, width and height of the picture, rect

    • Gets the color space of the picture

    • Get image bytes width height 4 (RGBA) open up image data storage space

    • Create context

    • Flip the image upside down (the image is inverted by default)

    • The image is redrawn to get a new decompressed bitmap

    • Get texture ID

    • Load texture 2D data

    • Set texture properties (Filter and Wrap)

    • Binding texture

    • Release the context, imageData

GLuint createTextureWithImage:(UIImage *)image {//1, convert UIImage to CGImageRef CGImageRef CGImageRef = [image CGImage]; If (! cgImageRef) { NSLog(@"Failed to load image"); exit(1); } GLuint width = (GLuint)CGImageGetWidth(cgImageRef); GLuint height = (GLuint)CGImageGetHeight(cgImageRef); Rect = CGRectMake(0, 0, width, height); / / get photo color space CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB (); (gba) void *imageData = malloc(width * height *4); //4. Create context /* Parameter 1: data, pointing to the memory address of the drawn image to be rendered 2: width,bitmap width, in pixels 3: height,bitmap height, in pixels 4: BitPerComponent, the number of bits per component of pixels in memory, such as 32-bit RGBA, is set to 8. KCGImageAlphaPremultipliedLast colorSpace, bitmap using the color space: RGBA */ CGContextRef context = CGBitmapContextCreate(imageData, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); CGContextTranslateCTM(context, 0, height); CGContextScaleCTM (context, 1.0 f, 1.0 f); CGColorSpaceRelease(colorSpace); CGContextClearRect(context, rect); CGContextDrawImage(context, recT, cgImageRef); CGContextDrawImage(context, recT, cgImageRef); GLuint textureID; GLuint textureID; glGenTextures(1, &textureID); glBindTexture(GL_TEXTURE_2D, textureID); GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D parameters 1: texture mode, GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D parameters 2: loading level, generally set to 0 parameter 3: texture color value GL_RGBA parameter 4: wide parameter 5: high parameter 6: 7: format 8: type 9: GlTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData); //7. Set the texture property 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); //8. Bind texture /* Parameter 1: texture dimension parameter 2: texture ID, since there is only one texture, give 0. */ glBindTexture(GL_TEXTURE_2D, 0); //9. Release context,imageData CGContextRelease(context); free(imageData); //10. Return textureID; }Copy the code
  • Set texture ID
self.textureID = textureID
Copy the code
  • Set the viewport
glViewport(0, 0, self.drawableWidth, self.drawableHeight); - (GLint)drawableWidth {GLint backingWidth; glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth); return backingWidth; } // Get the render cache height - (GLint)drawableHeight {GLint backingHeight; glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight); return backingHeight; }Copy the code
  • Set the vertex cache
	GLuint vertexBuffer;
    glGenBuffers(1, &vertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    GLsizeiptr bufferSizeBytes = sizeof(SenceVertex) * 4;
    glBufferData(GL_ARRAY_BUFFER, bufferSizeBytes, self.vertices, GL_STATIC_DRAW);
Copy the code
  • Set the default shader setupNormalShaderProgram
[self setupShaderProgramWithName:@"Normal"];
Copy the code

StartFilerAnimation Filter animation

  • Pause the CADisplayLink timer
  • Method to set displayLink
  • Add displayLink to runloop running loop;
// Start a filter animation - (void)startFilerAnimation {//1. //CADisplayLink timer if (self.displayLink) {[self.displayLink invalidate]; self.displayLink = nil; } //2. Set the displayLink method self.startTimeInterval = 0; self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(timeAction)]; / / 3. Add displayLink to the runloop running cycle [self. DisplayLink addToRunLoop: [NSRunLoop mainRunLoop] forMode: NSRunLoopCommonModes]; }Copy the code
  • Timing of the animation

    • The use of the program

    • Binding buffer

    • The incoming time

    • Remove the canvas

    • redraw

    • Render to the screen

Animation - (void)timeAction {//DisplayLink's current time if (self.starttimeInterval == 0) {self.starttimeInterval = self.displayLink.timestamp; } // Use program glUseProgram(self.program); // bind buffer glBindBuffer(GL_ARRAY_BUFFER, self.vertexbuffer); / / incoming time CGFloat currentTime = self. DisplayLink. Timestamp - self. StartTimeInterval; GLuint time = glGetUniformLocation(self.program, "Time"); glUniform1f(time, currentTime); // Clear the canvas glClear(GL_COLOR_BUFFER_BIT); glClearColor(1, 1, 1, 1); // Redraw glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // Render to screen [self.context presentRenderbuffer:GL_RENDERBUFFER]; }Copy the code

FilterBarDelegate

Select a grayscale shader based on index and restart the filter animation

-(void)filterBar:(FilterBar *)filterBar didScrollToIndex:(NSUInteger)index;

  • Index = 0 sets the default shader

    [self setupShaderProgramWithName:@”Normal”];

  • Index = 1 sets the binary screen shader

    [self setupShaderProgramWithName:@”SplitScreen_2″];

  • Index = 2 sets the 3 split screen shader

    [self setupShaderProgramWithName:@”SplitScreen_3″];

  • Index = 3 sets 4 split-screen shaders

    [self setupShaderProgramWithName:@”SplitScreen_4″];

  • Index = 4 Sets 6 split-screen shaders

    [self setupShaderProgramWithName:@”SplitScreen_6″];

  • Index = 5 sets 9 split-screen shaders

    [self setupShaderProgramWithName:@”SplitScreen_9″];

Split-screen filter: Chip shader Shaders implement different algorithms

Switch split screen – setupShaderProgramWithName loading different vertex shader program and the fragment shader

Binary screen

The x value of the texture coordinate is unchanged, and the y value is changed:

When y is in the range [0, 0.5], the screen’s (0, 0) coordinates need the corresponding image’s (0, 0.25), so y = y+0.25

When y is in the range of [0.5, 1], the screen’s (0,0.5) coordinates need the corresponding image’s (0,0.25), so y = y-0.25

  • Chip shader program
precision highp float; uniform sampler2D Texture; varying highp vec2 TextureCoordsVarying; void main() { vec2 uv = TextureCoordsVarying.xy; float y; If (uv) > = 0.0 && y uv) < = 0.5) {y y = uv. Y + 0.25; } else {y = uv. y-0.25; } gl_FragColor = texture2D(Texture, vec2(uv.x, y)); }Copy the code

Three split screen

The x value of texture coordinates remains unchanged, and the y value changes:

When y is in the [0, 1/3] range, the screen’s (0, 0) coordinates need to correspond to the image’s (0, 1/3), so y = y+1/3

When y is in the [1/3, 2/3] range, the screen’s (0, 1/3) coordinates need the corresponding image’s (0, 1/3), so y stays the same

When y is in the [2/3, 1] range, the screen’s (0, 2/3) coordinates need to correspond to the image’s (0, 1/3), so y = y-1/3

  • Chip shader program
precision highp float; uniform sampler2D Texture; varying highp vec2 TextureCoordsVarying; void main() { vec2 uv = TextureCoordsVarying.xy; If (uv.y < 1.0/3.0) {uv.y = uv.y + 1.0/3.0; } else if (uv) y > 2.0/3.0) {uv. Y = uv. Y - 1.0/3.0. } gl_FragColor = texture2D(Texture, uv); }Copy the code

Four split screen

The screen is divided into four equal sections to display a reduced texture image:

When x is in the range [0, 0.5], x = x*2

When x is in the range of [0.5, 1], x = (x-0.5)*2

When y is in the range [0, 0.5], y = y*2

When y is in the range of [0.5, 1], y = (y-0.5)*2

  • Chip shader program
precision highp float; uniform sampler2D Texture; varying highp vec2 TextureCoordsVarying; void main() { vec2 uv = TextureCoordsVarying.xy; If (uv.x <= 0.5){uv.x = uv.x * 2.0; }else{uv.x = (uv.x -0.5) * 2.0; } if (uv.y<= 0.5) {uv.y = uv.y * 2.0; }else{uv.y = (uV. y-0.5) * 2.0; } gl_FragColor = texture2D(Texture, uv); }Copy the code

Six split screen

Texture coordinates x and Y change rules:

When x is in the range [0, 1/3], x is equal to x plus 1/3

When x is in the range 1/3, 2/3, x doesn’t change

When x is in the [2/3, 1] range, x is equal to x minus 1/3

When y is in the range [0, 0.5], y = y+0.25

When y is in the range of [0.5, 1], y = y-0.24

  • Chip shader program
precision highp float; uniform sampler2D Texture; varying highp vec2 TextureCoordsVarying; void main() { vec2 uv = TextureCoordsVarying.xy; If (uv.x <= 1.0/3.0){uv.x = uv.x + 1.0/3.0; } else if (uv) x > = 2.0/3.0) {uv. X = uv. X - 1.0/3.0. } if(uv.y <= 0.5){uv.y = uv.y + 0.25; }else {uv.y = uv.y -0.25; } gl_FragColor = texture2D(Texture, uv); }Copy the code

Nine points screen

Texture coordinates x, y change rule: when x is in [0, 1/3] range, x = x*3

When x is in the range 1/3, 2/3, x is equal to x minus 1/3 times 3

When x is in the [2/3, 1] range, x is equal to x minus 2/3 times 3

When y is in the range 0, 1/3, y is equal to y times 3

When y is in the 1/3, 2/3 range, y is equal to y minus 1/3 times 3

When y is in the [2/3, 1] range, y is equal to y minus 2/3 times 3

  • Chip shader program
precision highp float; uniform sampler2D Texture; varying highp vec2 TextureCoordsVarying; void main() { vec2 uv = TextureCoordsVarying.xy; If (uv.x < 1.0/3.0) {uv.x = uv.x * 3.0; } else if (uv) x < 2.0/3.0) {uv. X = (uv) x - 1.0/3.0) * 3.0; } else {uv.x = (uv.x-2.0/3.0) * 3.0; } if (uv.y <= 1.0/3.0) {uv.y = uv.y * 3.0; } else if (uv) y < 2.0/3.0) {uv. Y = (uv) y - 1.0/3.0) * 3.0; } else {uv.y = (uv.y-2.0/3.0) * 3.0; } gl_FragColor = texture2D(Texture, uv); }Copy the code