In today’s example, static images are added with dynamic effects: zoom, out-of-body, shake, flash white, burr, illusion. Also look at the code in the shader. Because we are going to show dynamic effects today, we added a time stamp to the GLSL image loading code to calculate the period of dynamic effects in the slice shader. For the rest of the code, please refer to Split Screen Filter and Grayscale & Mosaic Filter
First, the effect drawing
Second, the shader code part
1. Normal effect
1. Vertex shaders
attribute vec4 Position;
attribute vec2 TextureCoords;
varying vec2 TextureCoordsVarying;
void main (void) {
gl_Position = Position;
TextureCoordsVarying = TextureCoords;
}
Copy the code
2. Chip shader
precision highp float; uniform sampler2D Texture; varying vec2 TextureCoordsVarying; void main (void) { vec4 mask = texture2D(Texture, TextureCoordsVarying); Gl_FragColor = vec4 (mask. RGB, 1.0); }Copy the code
2, scaling
The simplest way to do this: in the vertex shader, as time changes, multiply the vertex coordinates x and y by the corresponding time magnification ratio to achieve the scaling effect.
We only modify the vertex shader:
attribute vec4 Position; attribute vec2 TextureCoords; varying vec2 TextureCoordsVarying; // Keep accumulating uniform float Time as timer refresh method increments timestamp; //π const float PI = 3.1415926; Void main (void) {// Duration of a zoom effect, 0.6 seconds float duration = 0.6; Float maxAmplitude = 0; float maxAmplitude = 0; float maxAmplitude = 0; // The range of time is controlled between 0.0 and 0.6. Then, according to the period, the position of the current second within the period is calculated to calculate the state of the picture in this second. Mod (a, b) is equivalent to a%b. Float time = mod(time,duration) float time = mod(time,duration) // The magnitude of the Angle is [0, π], and the amplitude of the Angle is [0, π]. The amplitude of the Angle is [0, π]. Float amplitude = 1.0 + maxAmplitude * abs(sin(time * (PI/duration))); float amplitude = 1.0 + maxAmplitude * abs(sin(time * (PI/duration))); // Multiply the vertex coordinates x and y by an zoom ratio, and achieve the effect of zoom and stretch without changing the texture coordinates. Gl_Position = vec4(position. x * amplitude, position. y * amplitude, position.zw); TextureCoordsVarying = TextureCoords; }Copy the code
Out-of-body experience
Principle: Two images are superimposed together to mix colors, and then the upper image changes with the timestamp to enlarge and reduce transparency.
precision highp float; uniform sampler2D Texture; varying vec2 TextureCoordsVarying; uniform float Time; Void main (void) {// Cycle float duration = 0.7; // Max transparency 0.4 float maxAlpha = 0.4; // Float maxScale = 1.8; // Calculate the proportion of the current time in the cycle. Float progress = mod(Time, duration)/duration; float progress = mod(Time, duration)/duration; Float alpha = maxAlpha * (1.0 - progress); float alpha = maxAlpha * (1.0 - progress); Float scale = 1.0 + (maxscale-1.0) * progress; float scale = 1.0 + (maxscale-1.0) * progress; Float weakX = 0.5 + (TextureCoordsVarying. X-0.5)/scale; float weakX = 0.5 + (TextureCoordsVarying. X-0.5)/scale; Float weakY = 0.5 + (TextureCoordsVarying. Y-0.5)/scale; float weakY = 0.5 + (TextureCoordsVarying. Y-0.5)/scale; vec2 weakTextureCoords = vec2(weakX, weakY); WeakMask = texture2D(Texture, weakTextureCoords); // The Texture of the underlying image vec4 mask = texture2D(Texture, TextureCoordsVarying); // mix gl_FragColor = mask * (1.0-alpha) + weakMask * alpha; }Copy the code
4, jitter,
Principle: Color offset + weak amplification. It is to enlarge the texture first, and then color offset the texture coordinate pixel after amplification
precision highp float; uniform sampler2D Texture; varying vec2 TextureCoordsVarying; uniform float Time; Void main (void) {// Cycle float duration = 0.7; Float maxScale = 1.1; float maxScale = 1.1; // Float offset = 0.02; Float progress = mod(Time, duration)/duration; float progress = mod(Time, duration)/duration; Vec2 offsetCoords = vec2(offset, offset) * progress; // Offset = vec2(offset, offset) * progress; Float scale = 1.0 + (maxscale-1.0) * progress; float scale = 1.0 + (maxscale-1.0) * progress; Float weakX = 0.5 + (TextureCoordsVarying. X-0.5)/scale; float weakX = 0.5 + (TextureCoordsVarying. X-0.5)/scale; Float weakY = 0.5 + (TextureCoordsVarying. Y-0.5)/scale; float weakY = 0.5 + (TextureCoordsVarying. Y-0.5)/scale; vec2 ScaleTextureCoords0 = vec2(weakX, weakY); ScaleTextureCoords = VEC2 (0.5, 0.5) + (TextureCoordsVarying - VEC2 (0.5, 0.5))/scale; ScaleTextureCoords = vec2(0.5, 0.5) + (TextureCoordsVarying - vec2(0.5, 0.5))/scale; // Texture = texture2D(ScaleTextureCoords); Vec4 maskR = texture2D(Texture, ScaleTextureCoords + offsetCoords); vec4 maskB = texture2D(Texture, ScaleTextureCoords - offsetCoords); Gl_FragColor = vec4(maskr.r, mask.g, maskb.b, mask.a); }Copy the code
5, flash of white
How it works: Blend the two layers, but the top image is a pure white mask. It fades over time.
precision highp float; uniform sampler2D Texture; varying vec2 TextureCoordsVarying; uniform float Time; Const float PI = 3.1415926; Void main (void) {float duration = 0.6; Float time = mod(time, duration); float time = mod(time, duration); // the whiteMask layer vec4 whiteMask = vec4(1.0, 1.0, 1.0, 1.0); Float amplitude = abs(sin(time * (PI/duration)); float amplitude = abs(time * (PI/duration)); // The Texture layer vec4 mask = texture2D(Texture, TextureCoordsVarying); Gl_FragColor = mask * (1.0 - amplitude) + whiteMask * amplitude; }Copy the code
6, burr
Principle: Tear + tiny color offset. (Tearing is the offset in the X coordinate.)
First set a threshold (maximum jitter value), the offset value of the current pixel, if less than this threshold color offset. If not, multiply by a reduction factor. The net effect is that most rows will be slightly offset, and only a few rows will be significantly offset
precision highp float; uniform sampler2D Texture; varying vec2 TextureCoordsVarying; uniform float Time; Const float PI = 3.1415926; /* Because there is no built-in random function, here is a custom random number method fract (x) : returns the fractional part of x: Sin (n) * a maximum value with a decimal point and if you want to have a low random number, you have to multiply it by a large number, and if you want to have a small number in the range [0,1], Sin * 1 */ float rand(float n) {return fract(sin(n) * 43758.5453123); } void main (void) {float maxJitter = 0.06; // Cycle float duration = 0.3; // Red offset float colorROffset = 0.01; // Float colorBOffset = -0.025; Float time = mod(time, duration*2.0); float time = mod(time, duration*2.0); Float amplitude = Max (sin(time * (PI/duration)), 0.0); float amplitude = Max (time * (PI/duration)); Float jitter = rand(TextureCoordsVarying. Y) * 2.0-1.0; float jitter = rand(TextureCoordsVarying. // maxJitter * amplitude [0, 0.06] // if the offset value is too large, it is not a minor tear effect. Bool needOffset = abs(jitter) < maxJitter * amplitude; Float textureX = TextureCoordsVarying. X + (needOffset? Jitter: (jitter * amplitude * 0.006)); Vec2 textureCoords = VEC2 (textureX, TextureCoordsVarying. Y); // Texture mask = texture2D(Texture, textureCoords); Veckr = texture2D(Texture, textureCoords + vec2(colorROffset * amplitude, 0.0)); Vec4 maskB = texture2D(Texture, textureCoords + vec2(colorBOffset * amplitude, 0.0)); // Random RBGA gl_FragColor = vec4(maskr.r, mask.g, maskb.b, mask.a); }Copy the code
7, hallucinations,
In fact, combined with the above knowledge, some simulators will not run, real machine no pressure.
Principle: residual shadow + color offset
Residual shadow: At regular intervals, a new layer is created, which is predominantly red and then becomes less transparent over time. Multiple layers move in circles with distance from each other over time to create a residual effect.
Color offset: in the process of moving the picture, the blue is in the front and the red is in the back. In other words, during the process of moving, some of the values of the red channel are lost in the original position at an interval of time, and the values of this part of the red channel will gradually recover with time offset
precision highp float; uniform sampler2D Texture; varying vec2 TextureCoordsVarying; uniform float Time; Const float PI = 3.1415926; Const float duration = 2.0; /* The color value of the single pixel that spins the phantom is calculated at the exact position of the current pixel in the current second, i.e., the exact position of the picture at that moment. */ vec4 getMask(float time, vec2 textureCoords, Float padding) {// Vec2 translation = vec2(sin(time * (PI * 2.0 / duration)), cos(time * (PI * 2.0 / duration))); // New texture coordinates = original texture coordinates + offset * circular coordinates (new layers are spaced between layers, Vec2 translationTextureCoords = textureCoords + padding * translation; // Get the Texture of the new layer vec4 mask = texture2D(Texture, translationTextureCoords); return mask; } /* Count the layers created at some point, MaskAlphaProgress (float currentTime, float hideTime, float startTime) {//mod (cycle + duration - startTime, Float time = mod(Duration + CurrentTime-startTime, duration); float time = mod(Duration + CurrentTime-startTime, duration); // If less than 0.9, return time, otherwise, return 0.9 min(time, hideTime); } void main (void) {float time = mod(time, duration); // Float scale = 1.2; // Float padding = 0.5 * (1.0-1.0 / scale); Vec2 textureCoords = VEC2 (0.5, 0.5) + (TextureCoordsVarying - VEC2 (0.5, 0.5))/scale; Float hideTime = 0.9; float hideTime = 0.9; // Increase the layer interval float timeGap = 0.2; // Note: only the red transparent channel value is retained, because the illusion effect remains red float maxAlphaR = 0.5; // Max R float maxAlphaG = 0.05; // Max G float maxAlphaB = 0.05; Vec4 mask = getMask(time, textureCoords, padding); //RGB: float alphaR = 1.0 for loop; // R float alphaG = 1.0; // G float alphaB = 1.0; // B // Final layer color: initialize vec4 resultMask = vec4(0, 0, 0, 0); // Loop: each loop gets the color of the new layer, the phantom color. For (float f = 0.0; f < duration; f += timeGap) { float tmpTime = f; Vec4 tmpMask = getMask(tmpTime, textureCoords, padding); // The layer created at some point, Float tmpAlphaR = maxAlphaR - maxAlphaR * float tmpAlphaR = maxAlphaR - maxAlphaR * float tmpAlphaR = maxAlphaR - maxAlphaR * float tmpAlphaR = maxAlphaR - maxAlphaR * maskAlphaProgress(time, hideTime, tmpTime) / hideTime; float tmpAlphaG = maxAlphaG - maxAlphaG * maskAlphaProgress(time, hideTime, tmpTime) / hideTime; float tmpAlphaB = maxAlphaB - maxAlphaB * maskAlphaProgress(time, hideTime, tmpTime) / hideTime; // Result += temporary color * transparency, ResultMask += vec4(tmpmask. r * tmpAlphaR, tmpmask. g * tmpAlphaG, tmpmask. b * tmpAlphaB, 1.0); AlphaR -= tmpAlphaR; alphaG -= tmpAlphaG; alphaB -= tmpAlphaB; } // Final color += RGB * transparency of the original texture resultMask += vec4(mask.r * alphaR, mask.g * alphaG, mask.b * alphaB, 1.0); gl_FragColor = resultMask; }Copy the code
Three, the code part
#import "ViewController.h"
#import <GLKit/GLKit.h>
#import "FilterBar.h"
//之前也提到过,c语言结构体,存放顶点数据
typedef struct {
GLKVector3 positionCoord; // (X, Y, Z)
GLKVector2 textureCoord; // (U, V)
} SenceVertex;
@interface ViewController ()<FilterBarDelegate>
// 顶点数组
@property (nonatomic, assign) SenceVertex *vertices;
// 上下文
@property (nonatomic, strong) EAGLContext *context;
// 用于刷新屏幕的专属定时器(相比timer,它可以和屏幕刷新同频)
@property (nonatomic, strong) CADisplayLink *displayLink;
// 开始的时间戳
@property (nonatomic, assign) NSTimeInterval startTimeInterval;
// 着色器程序
@property (nonatomic, assign) GLuint program;
// 顶点缓冲区id
@property (nonatomic, assign) GLuint vertexBuffer;
// 纹理的id
@property (nonatomic, assign) GLuint textureID;
@end
@implementation ViewController
//释放部分
- (void)dealloc {
//上下文释放
if ([EAGLContext currentContext] == self.context) {
[EAGLContext setCurrentContext:nil];
}
//顶点缓存区释放
if (_vertexBuffer) {
glDeleteBuffers(1, &_vertexBuffer);
_vertexBuffer = 0;
}
//顶点数组释放
if (_vertices) {
free(_vertices);
_vertices = nil;
}
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// 移除 displayLink
if (self.displayLink) {
[self.displayLink invalidate];
self.displayLink = nil;
}
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blackColor];
/*
整体思路:
和GLSL加载图片的流程一样
不同分屏滤镜效果,主要是在着色器里去计算的
这里加一个计时器的关键点在于,让屏幕保持一直刷新渲染,方便切换滤镜及时刷新。更多是用于有动效的滤镜
*/
//1、创建底部切换bar
[self setupFilterBar];
//2、GLSL加载图片流程
[self loaderImage];
//3、启动定时器,刷新屏幕
[self startRender];
}
#pragma mark - 1
- (void)setupFilterBar {
CGFloat filterBarWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat filterBarHeight = 100;
CGFloat filterBarY = [UIScreen mainScreen].bounds.size.height - filterBarHeight;
NSArray *dataSource = @[@"无",@"缩放",@"灵魂出窍",@"抖动",@"闪白",@"毛刺",@"幻觉"];
FilterBar *filerBar = [[FilterBar alloc] initWithFrame:CGRectMake(0, filterBarY, filterBarWidth, filterBarHeight)];
filerBar.itemList = dataSource;
filerBar.delegate = self;
[self.view addSubview:filerBar];
}
- (void)filterBar:(FilterBar *)filterBar didScrollToIndex:(NSUInteger)index {
//1. 选择默认shader
if (index == 0) {
[self setUpDrawShaderWith:@"normal"];
}else if(index == 1)
{
[self setUpDrawShaderWith:@"scale"];
}else if(index == 2)
{
[self setUpDrawShaderWith:@"soulOut"];
}else if(index == 3)
{
[self setUpDrawShaderWith:@"shake"];
}else if(index == 4)
{
[self setUpDrawShaderWith:@"shineWhite"];
}else if(index == 5)
{
[self setUpDrawShaderWith:@"glitch"];
}else if(index == 6){
[self setUpDrawShaderWith:@"vertigo"];
}
// 重新开始滤镜动画
[self startRender];
}
#pragma mark - 2
- (void)loaderImage {
//1、准备工作
/*
把一些会重复用到的地方,封装起来,然后剩下的不变的,放在这个方法里面。
上下文&设置当前
设置图层
设置缓冲区
设置视口
设置顶点数据
设置顶点缓冲区
解压图片,拿到纹理id(因为这里面只有一个纹理,如果有多个也要拆分出去,方便复用)
*/
[self setUpConfig];
//2、绘制每一个着色器都需要调用的方法。第一次加载,肯定使用默认着色器
/*
1、加载、编译shader
1)拿到shader路径,转成c字符串
2)创建shader对象
3)把着色器字符串 附着到shader对象上
4)编译shader对象&检验
2、附着、连接program
1)创建一个program对象
2)把顶点、片元shader 附着上
3)链接program&检验
3、use program
4、传递数据
1)顶点坐标数据
2)纹理坐标数据
3)采样器传递纹理id(纹理id在准备工作就拿到了,不放这里是防止重复操作)
*/
[self setUpDrawShaderWith:@"normal"];
}
#pragma mark - 2.1
- (void)setUpConfig {
//1.上下文
self.context = [[EAGLContext alloc] initWithAPI: kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:self.context];
//2、图层-设置一个正方形
CAEAGLLayer *layer = [[CAEAGLLayer alloc] init];
layer.frame = CGRectMake(0, 100, self.view.frame.size.width, self.view.frame.size.width);
layer.contentsScale = [[UIScreen mainScreen] scale];
[self.view.layer addSublayer:layer];
//3、缓冲区
//1)渲染缓冲区
GLuint rBuffer,fBuffer;
glGenRenderbuffers(1, &rBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, rBuffer);
//把layer的存储绑定到渲染缓冲区
[self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer];
//2)帧缓冲区
glGenFramebuffers(1, &fBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, fBuffer);
//把renderBuffer绑定到ATTACHMENT0上
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rBuffer);
//4、视口
glViewport(0, 0, self.drawableWidth, self.drawableHeight);
//5、顶点数据
//1)开辟顶点数组内存空间
self.vertices = malloc(sizeof(SenceVertex) * 4);
//2)
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}};
//6、顶点缓冲区
GLuint vBuffer;
glGenBuffers(1, &vBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(SenceVertex) * 4, self.vertices, GL_STATIC_DRAW);
//保存,退出的时候才释放
self.vertexBuffer = vBuffer;
//7、解压图片,获取纹理id
GLuint textureID = [self createTextureWithImageName:@"mark.jpeg"];
//设置纹理ID
self.textureID = textureID;
}
- (GLuint)createTextureWithImageName:(NSString *)imageName{
//1、拿到图片路径
//这么写的好处是,图片不做缓存处理
NSString *imagePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:imageName];
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
//2、解压图片
CGImageRef imageRef = [image CGImage];
//3、判断图片有没有拿到
if (!imageRef) {
NSLog(@"load image faile");
exit(1);
}
//4、创建上下文
//1)获取宽高
GLuint width = (GLuint)CGImageGetWidth(imageRef);
GLuint height = (GLuint)CGImageGetHeight(imageRef);
//2)拿到图片大小
void *imageData = malloc(width * height * 4);
//3)拿到图片的颜色
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
//3)上下文
/*
参数1:data,指向要渲染的绘制图像的内存地址
参数2:width,bitmap的宽度,单位为像素
参数3:height,bitmap的高度,单位为像素
参数4:bitPerComponent,内存中像素的每个组件的位数,比如32位RGBA,就设置为8
参数5:bytesPerRow,bitmap的没一行的内存所占的比特数
参数6:colorSpace,bitmap上使用的颜色空间 kCGImageAlphaPremultipliedLast:RGBA
*/
CGContextRef imageContext = CGBitmapContextCreate(imageData, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
//5、重新绘制
CGRect rect = CGRectMake(0, 0, width, height);
//1)翻转策略
CGContextTranslateCTM(imageContext, 0, height);
CGContextScaleCTM(imageContext, 1.0f, -1.0f);
//2)对图片重新绘制,得到一张新的解压后的位图
CGContextDrawImage(imageContext, rect, imageRef);
//3)用完之后释放
CGColorSpaceRelease(colorSpace);
CGContextRelease(imageContext);
//6、设置纹理 (因为这个方法需要我们返回一个id,就不穿默认0了,还是写一遍代码吧)
GLuint textureId;
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
//7、载入纹理数据
/*
参数1:纹理模式,GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
参数2:加载的层次,一般设置为0
参数3:纹理的颜色值GL_RGBA
参数4:宽
参数5:高
参数6:border,边界宽度
参数7:format
参数8:type
参数9:纹理数据
*/
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
//8、设置纹理属性
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);
//9、重新绑定一下(用的时候就绑定准没错)
glBindTexture(GL_TEXTURE_2D, textureId);
//10、释放
free(imageData);
return textureId;
}
#pragma mark - 2.2
- (void)setUpDrawShaderWith:(NSString *)shaderName{
//1. 编译顶点着色器/片元着色器
GLuint vertexShader = [self compileShaderWithName:shaderName type:GL_VERTEX_SHADER];
GLuint fragmentShader = [self compileShaderWithName:shaderName type:GL_FRAGMENT_SHADER];
//2、
//1)创建一个program
GLuint program = glCreateProgram();
//2)附着
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
// glDeleteShader(vertexShader);
// glDeleteShader(fragmentShader);
//3)link
glLinkProgram(program);
//4)检查
GLint linkStatus;
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
if (linkStatus == GL_FALSE) {
GLchar messages[256];
glGetProgramInfoLog(program, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(@"program链接失败:%@", messageString);
exit(1);
}
//3、use
glUseProgram(program);
//4、传递数据
//1)先拿到通道名
//顶点坐标
GLuint positionSlot = glGetAttribLocation(program, "Position");
//纹理坐标
GLuint textureCoordsSlot = glGetAttribLocation(program, "TextureCoords");
//纹理
GLuint textureSlot = glGetUniformLocation(program, "Texture");
//2)传顶点坐标
glEnableVertexAttribArray(positionSlot);
glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(SenceVertex), NULL + offsetof(SenceVertex, positionCoord));
//3)传纹理坐标
glEnableVertexAttribArray(textureCoordsSlot);
glVertexAttribPointer(textureCoordsSlot, 2, GL_FLOAT, GL_FALSE, sizeof(SenceVertex), NULL + offsetof(SenceVertex, textureCoord));
//4) 传纹理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, self.textureID);
glUniform1i(textureSlot, 0);
//5.保存program,界面销毁则释放
self.program = program;
}
//编译shader代码
- (GLuint)compileShaderWithName:(NSString *)name type:(GLenum)shaderType {
//1、获得shader路径
NSString *shaderPath = [[NSBundle mainBundle] pathForResource:name ofType:shaderType == GL_VERTEX_SHADER ? @"vsh" : @"fsh"];
//2、转换成c语言字符串
NSError *error;
NSString *shaderString = [NSString stringWithContentsOfFile:shaderPath encoding:NSUTF8StringEncoding error:&error];
if (!shaderString) {
NSLog( @"读取shader失败");
exit(1);
}
// const GLchar* source = (GLchar*)[pathString UTF8String];
const char *shaderStringUTF8 = [shaderString UTF8String];
int shaderStringLength = (int)[shaderString length];
//3、创建shader对象
GLuint shader = glCreateShader(shaderType);
//4、附着
glShaderSource(shader, 1, &shaderStringUTF8, &shaderStringLength);
//5、编译
glCompileShader(shader);
//6、检查编译
GLint compileStatus;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus);
if (compileStatus == GL_FALSE) {
GLchar messages[256];
glGetShaderInfoLog(shader, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(@"shader编译失败:%@", messageString);
exit(1);
}
return shader;
}
#pragma mark - 3
- (void)startRender {
//1.因为会重复调用,严谨一点,先判断一下
if (self.displayLink) {
[self.displayLink invalidate];
self.displayLink = nil;
}
//2. 设置displayLink 的方法
self.startTimeInterval = 0;
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(timeAction)];
//3.将displayLink 添加到runloop 运行循环
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)timeAction{
//DisplayLink 的当前时间撮
if (self.startTimeInterval == 0) {
self.startTimeInterval = self.displayLink.timestamp;
}
//使用program
glUseProgram(self.program);
//绑定buffer
glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer);
// 传入时间
CGFloat currentTime = self.displayLink.timestamp - self.startTimeInterval;
GLuint time = glGetUniformLocation(self.program, "Time");
glUniform1f(time, currentTime);
// 清除画布
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(1, 1, 1, 1);
// 重绘
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
//渲染到屏幕上
[self.context presentRenderbuffer:GL_RENDERBUFFER];
}
//获取渲染缓存区的宽
- (GLint)drawableWidth {
GLint backingWidth;
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
return backingWidth;
}
//获取渲染缓存区的高
- (GLint)drawableHeight {
GLint backingHeight;
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
return backingHeight;
}
@end
Copy the code
Source code link:
Link: pan.baidu.com/s/1MjafIFev… Password: uokx