首先先看一下实现的效果吧
最终实现的效果图
原图是一张完整的图,我们把原图做成一个四宫格的样式,当然了,9宫格也是同样的道理
实现的过程
步骤一:初始化
- (instancetype)initWithFrame:(CGRect)frame
{
self= [superinitWithFrame:frame];
if(self) {
/// 设置显示层
[selfsetUpLayer];
/// 设置上下文
[self setUpContext];
[self setUpProgram];
[selfsetupVBO];
[self setUpTexture];
}
return self;
}
其中Opengl的显示使用CAEAGLLayer来绘制完成,它是CALayer的一个子类,用来显示任意的OpenGL图形。
+ (Class)layerClass{
return [CAEAGLLayer class];
}
对于CAEAGLLayer的属性配置
- (void)setUpLayer{
myLayer = (CAEAGLLayer *)self.layer;
myLayer.opaque=YES;
myLayer.drawableProperties = @{
kEAGLDrawablePropertyRetainedBacking:@NO,
kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8
};
}
- (void)layoutSubviews{
[super layoutSubviews];
[EAGLContext setCurrentContext:myContext];
[self destoryRenderAndFrameBuffer];
[self setUpBuffer];
[selfrender];
}
EAGLContext
在iOS应用程序中,每个线程都会维护一个当前的上下文,在使用Opengl-ES进行绘制时也不例外,Opengl-es是使用EAGLContext进行管理上下文的,一看名字就能知道了,相当好记。当然了,这其中也要对Opengl版本的设置
- (void)setUpContext{
myContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
if(myContext==nil) {
NSLog(@"初始化上下文失败");
return;
}
if (![EAGLContext setCurrentContext:myContext]) {
NSLog(@"设置上下文失败");
return;
}
}
清除缓冲空间
- (void)destoryRenderAndFrameBuffer {
glDeleteFramebuffers(1, &myColorFrameBuffer);
myColorFrameBuffer = 0;
glDeleteRenderbuffers(1, &myColorRenderBuffer);
myColorRenderBuffer = 0;
}
设置缓冲空间
- (void)setUpBuffer{
//// 1. 创建颜色缓冲对象
GLuintbuffer =0;
/// 2. 申请一个缓冲区标记
glGenRenderbuffers(1, &buffer);
/// 3.
myColorRenderBuffer = buffer;
/// 4. 将缓冲区绑定到指定的空间中,把myColorRenderBuffer绑定在OpenGL ES的渲染缓存GL_RENDERBUFFER上
glBindRenderbuffer(GL_RENDERBUFFER, myColorRenderBuffer);
/// 将可绘制对象的存储绑定到OpenGL ES renderbuffer对象
// target OpenGL ES绑定点用于当前绑定的renderbuffer。该参数的值必须是GL_RENDERBUFFER
/// drawable 管理renderbuffer的数据存储的对象。在iOS中,此参数的值必须是一个 CAEAGLLayer 对象
///赋值
[myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
//// 下面创建帧缓冲对象并绑定
// 2、申请一个缓存区标记
glGenFramebuffers(1, &buffer);
myColorFrameBuffer = buffer;
// 将缓冲区绑定到指定的空间中
glBindFramebuffer(GL_FRAMEBUFFER, myColorFrameBuffer);
//// 将颜色渲染内存 配到 GL_COLOR_ATTACHMENT0 配置点上
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, myColorRenderBuffer);
}
为opengl的绘制写一个程序
- (void)setUpProgram{
myProgram = [GLESUtils loadProgram:@"texture_shaderv.glsl" fragShader:@"texture_shaderf.glsl"];
if(myProgram==0) {
return;
}
glUseProgram(myProgram);
position_loc = glGetAttribLocation(myProgram, "in_position");
texture_loc = glGetAttribLocation(myProgram, "in_tex_coord");
tex1_loc = glGetUniformLocation(myProgram, "tex1");
// tex2_loc = glGetUniformLocation(myProgram, "tex2");
}
下面是重磅,这个是设置如何进行操作顶点坐标和纹理坐标的,插入一条OpenGL-ES的顶点坐标系是从-1~1,中心点为(0.0f, 0.0f, 0.0f)纹理坐标是左上角为中心(0.0f, 0.0f),大小为0~1
- (void)setupVBO {
GLfloatvertices[] = {
/// 左上边
0.0f, 1.0f, 1.0f, 0.5f, // 右上 0,1
0.0f, 0.0f, 1.0f, 1.0f, // 右下 1,1
-1.0f,0.0f,0.5f,1.0f,// 左下 1,0
-1.0f,0.0f,0.5f,1.0f,// 左下 1,0
-1.0f,1.0f,0.5f,0.5f, // 左上 0,0
0.0f,1.0f,1.0f,0.5f, // 右上 0,1
///右上边
1.0f, 1.0f, 0.5f, 0.5f, // 右上 0,1
1.0f, 0.0f, 0.5f, 1.0f, // 右下 1,1
0.0f, 0.0f, 0.0f, 1.0f, // 左下 1,0
0.0f, 0.0f, 0.0f, 1.0f, // 左下 1,0
0.0f,1.0f,0.0f,0.5f, // 左上 0,0
1.0f,1.0f,0.5f,0.5f, // 右上 0,1
/// 左下角
0.0f, 0.0f, 1.0f, 0.0f, // 右上 0,1
0.0f, -1.0f,1.0f,0.5f,// 右下 1,1
-1.0f, -1.0f,0.5f,0.5f,// 左下 1,0
-1.0f, -1.0f,0.5f,0.5f,// 左下 1,0
-1.0f,0.0f,0.5f,0.0f, // 左上 0,0
0.0f,0.0f,1.0f,0.0f, // 右上 0,1
/// 右下角
1.0f,0.0f,0.5f,0.0f, // 右上 0,1
1.0f, -1.0f,0.5f,0.5f,// 右下 1,1
0.0f, -1.0f,0.0f,0.5f,// 左下 1,0
0.0f, -1.0f,0.0f,0.5f,// 左下 1,0
0.0f,0.0f,0.0f,0.0f, // 左上 0,0
1.0f,0.0f,0.5f,0.0f // 右上 0,1
};
vertCount=sizeof(vertices)/4;
vbo = [GLESUtils createVBO:GL_ARRAY_BUFFER
usage:GL_STATIC_DRAW
datSize:sizeof(vertices)
data:vertices];
glEnableVertexAttribArray(position_loc);
glVertexAttribPointer(position_loc, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, (void *)0);
glEnableVertexAttribArray(texture_loc);
glVertexAttribPointer(texture_loc, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, (void *)(sizeof(GLfloat) * 2));
}
根据图片生成纹理单元
- (void)setUpTexture{
text1 = [GLESUtils createTexture2D:@"test.jpg"];
// text2 = [GLESUtils createTexture2D:@"mixture.jpg"];
}
其中
+ (GLuint)createTexture2D:(NSString*)fileName{
GLuinttexture =0;
// 将图片转换成可操作位图
CGImageRefspriteImage = [[UIImageimageNamed:fileName]CGImage];
if(spriteImage ==nil) {
NSLog(@"failed to load image %@", fileName);
return0;
}
//获取横向的像素点的个数
size_twidth =CGImageGetWidth(spriteImage);
size_theight =CGImageGetHeight(spriteImage);
size_tbitsPerComponent =CHAR_BIT;
//每一行的像素点占用的字节数,每个像素点的ARGB四个通道各占8个bit(0-255)的空间
size_tbytesPerRow =4* width;
CGColorSpaceRef colorSpaceDeviceRGBRef = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
//指定bitmap是否包含alpha通道,像素中alpha通道的相对位置,像素组件是整形还是浮点型等信息的字符串。
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(spriteImage);
if(alphaInfo ==kCGImageAlphaNone|| alphaInfo ==kCGImageAlphaOnly) {
alphaInfo =kCGImageAlphaNoneSkipFirst;
}elseif(alphaInfo ==kCGImageAlphaFirst) {
alphaInfo =kCGImageAlphaPremultipliedFirst;
}elseif(alphaInfo ==kCGImageAlphaLast) {
alphaInfo =kCGImageAlphaPremultipliedLast;
}
bitmapInfo |= alphaInfo;
//NSData *spriteData =
//计算整张图占用的字节数
NSIntegerbitmapByteCount = (bytesPerRow * height);
//内存空间的指针,该内存空间的大小等于图像使用RGB通道所占用的字节数。
void*spriteData =malloc(bitmapByteCount);
// 创建CoreGraphic的图形上下文,该上下文描述了bitmaData指向的内存空间需要绘制的图像的一些绘制参数
CGContextRefspriteContext =CGBitmapContextCreate(spriteData, width, height, bitsPerComponent, bytesPerRow, colorSpaceDeviceRGBRef, bitmapInfo);
//Core Foundation中通过含有Create、Alloc的方法名字创建的指针,需要使用CFRelease()函数释放
CGColorSpaceRelease(colorSpaceDeviceRGBRef);
/// 在CGContextRef上绘图
CGContextDrawImage(spriteContext,CGRectMake(0,0, width, height), spriteImage);
/// 4. 绑定纹理到默认的纹理ID
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA, (int)width, (int)height,0,GL_RGBA,GL_UNSIGNED_BYTE, spriteData);
glBindTexture(GL_TEXTURE_2D, 0);
free(spriteData);
returntexture;
}
最后一步就是绘制了
- (void)render{
glClearColor(0.0, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, self.bounds.size.width, self.bounds.size.height);
// 激活纹理单元
glActiveTexture(GL_TEXTURE0);
// 绑定纹理到指定纹理单元
glBindTexture(GL_TEXTURE_2D, text1);
// 给采样器分配位置值
glUniform1i(tex1_loc, 0);
// // 激活纹理单元
// glActiveTexture(GL_TEXTURE1);
// // 绑定纹理到指定纹理单元
// glBindTexture(GL_TEXTURE_2D, text2);
// // 给采样器分配位置值
// glUniform1i(tex2_loc, 1);
glDrawArrays(GL_TRIANGLES,0, (int)vertCount);
[myContext presentRenderbuffer:GL_RENDERBUFFER];
}