思维导图
openGLES渲染一张图片的思维导图
OpenGL ES渲染图片.png
代码
//粘出主要部分代码
- (void)layoutSubviews
{
//设置图层
[self setupLayer];
//设置上下文
[self setupContext];
//清空缓冲区
[self clearBufferBit];
//设置renderBuffers
[self setRenderBuffers];
//设置frameBuffers
[self setFrameBuffers];
//开始绘制
[self renderLayer];
}
#pragma mark - 加载shader
- (GLuint)loadShader:(NSString *)vert withFragment:(NSString *)frag
{
//定义两个临时着色器对象标识
GLuint verShader, fragShader;
GLuint program = glCreateProgram();
//编译顶点着色器和片源着色器
[self compileShader:&verShader type:GL_VERTEX_SHADER file:vert];
[self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:frag];
//创建最终的程序
glAttachShader(program, verShader);
glAttachShader(program, fragShader);
//删除无用的对象标识
glDeleteShader(verShader);
glDeleteShader(fragShader);
return program;
}
//编译GLSL文件
- (void)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file
{
//读取GLSL文件内容
NSString *content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
const GLchar * source = [content UTF8String];
//根据内容创建一个shader,着色器标识
*shader = glCreateShader(type);
//把着色器源码附着到着色器对象上
glShaderSource(*shader, 1, &source, NULL);
//把着色器代码编译成目标代码
glCompileShader(*shader);
}
//设置纹理
- (GLuint)setupTexture:(NSString *)fileName
{
CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
if (!spriteImage)
{
NSLog(@"failed to load Image : %@", fileName);
exit(1);
}
size_t width = CGImageGetWidth(spriteImage);
size_t height = CGImageGetHeight(spriteImage);
//图像存储空间 = 图像width * 图像height * 每个像素的字节数(kEAGLColorFormatRGBA8)
//1byte = 8位 , 其中一个像素包含RGBA, 每个颜色占8位,也就是1byte。所以RGBA所占byte = 4 * 1 = 4
//所以说:图像的存储空间 = width * height * 4
GLubyte *spriteData = (GLubyte *)calloc(width * height * 4, sizeof(GLubyte));
//创建上下文
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width * 4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
//坐标反转(UIKit的原点坐标在屏幕左上角, Core Graphics的原点在屏幕的左下角)
CGRect contextFrame = CGRectMake(0, 0, width, height);
CGContextTranslateCTM(spriteContext, contextFrame.origin.x, contextFrame.origin.y);
CGContextTranslateCTM(spriteContext, 0, contextFrame.size.height);
CGContextScaleCTM(spriteContext, 1.0, -1.0);
CGContextTranslateCTM(spriteContext, -contextFrame.origin.x, -contextFrame.origin.y);
//绘制上下文
CGContextDrawImage(spriteContext, contextFrame, spriteImage);
CGContextRelease(spriteContext);
//绑定纹理到默认的纹理ID(这里只有一张图片,故而相当于默认于片元着色器里面的colorMap,如果有多张图不可以这么做)
glBindTexture(GL_TEXTURE_2D, 0);
//设置纹理参数
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);
//载入纹理
GLfloat fw = width, fh = height;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fw, fh, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
//绑定纹理
glBindTexture(GL_TEXTURE_2D, 0);
free(spriteData);
return 0;
}
//开始绘制
- (void)renderLayer
{
glClearColor(0.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//设置视口大小
GLfloat scale = [UIScreen mainScreen].scale;
glViewport(self.frame.origin.x * scale,
self.frame.origin.y * scale,
self.frame.size.width * scale,
self.frame.size.height * scale);
//读取顶点着色器和片源着色器
NSString *vertFile = [[NSBundle mainBundle] pathForResource:@"shaderv" ofType:@"vsh"];
NSLog(@"vertFile : %@", vertFile);
NSString *fragFile = [[NSBundle mainBundle] pathForResource:@"shaderf" ofType:@"fsh"];
NSLog(@"fragFile : %@", fragFile);
//加载shader, 获取执行程序
self.myProgram = [self loadShader:vertFile withFragment:fragFile];
//链接
glLinkProgram(self.myProgram);
//获取链接状态
GLint linkStatus;
glGetProgramiv(self.myProgram, GL_LINK_STATUS, &linkStatus);
if (linkStatus == GL_FALSE)
{
GLchar message[256];
glGetProgramInfoLog(self.myProgram, sizeof(message), 0, message);
NSString *messageString = [NSString stringWithUTF8String:message];
NSLog(@"message String : %@", messageString);
return;
}
NSLog(@"link Program success");
//5.使用program
glUseProgram(self.myProgram);
//6.加载顶点数据
//前3个是顶点坐标,后2个是纹理坐标
GLfloat attrArr[] =
{
0.5f, -0.5f, -1.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
0.5f, 0.5f, -1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, -1.0f, 0.0f, 1.0f,
0.5f, -0.5f, -1.0f, 1.0f, 0.0f,
};
//处理顶点数据
GLuint attriBuffer;
//申请一个缓存区标识符
glGenBuffers(1, &attriBuffer);
//把缓存标识符绑定GL_ARRAY_BUFFER上
glBindBuffer(GL_ARRAY_BUFFER, attriBuffer);
//把顶点数据从cpu拷贝到gpu(GL_DYNAMIC_DRAW参考图1-1)
glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);
//将顶点数据通过myPrograme中的传递到顶点着色程序的position
//1.glGetAttribLocation,用来获取vertex attribute的入口的.2.告诉OpenGL ES,通过glEnableVertexAttribArray,3.最后数据是通过glVertexAttribPointer传递过去的。
//注意:第二参数字符串必须和shaderv.vsh中的输入变量:position保持一致
GLuint position = glGetAttribLocation(self.myProgram, "position");
//2.设置合适的格式从buffer里面读取数据
glEnableVertexAttribArray(position);
//3.设置读取方式
//参数1:index,顶点数据的索引
//参数2:size,每个顶点属性的组件数量,1,2,3,或者4.默认初始值是4.
//参数3:type,数据中的每个组件的类型,常用的有GL_FLOAT,GL_BYTE,GL_SHORT。默认初始值为GL_FLOAT
//参数4:normalized,固定点数据值是否应该归一化,或者直接转换为固定值。(GL_FALSE)
//参数5:stride,连续顶点属性之间的偏移量,默认为0;
//参数6:指定一个指针,指向数组中的第一个顶点属性的第一个组件。默认为0
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, NULL);
//7.处理纹理数据
GLuint textureCoordinate = glGetAttribLocation(self.myProgram, "textureCoordinate");
glEnableVertexAttribArray(textureCoordinate);
glVertexAttribPointer(textureCoordinate, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) *5, (GLfloat *)NULL + 3);
//加载纹理
[self setupTexture:@"timg-3"];
GLuint rotate = glGetUniformLocation(self.myProgram, "rotateMatrix");
//获取渲染的弧度
float radians = 10 * 3.14159f / 180.0f;
//求得弧度对于的sin\cos值
float s = sin(radians);
float c = cos(radians);
//z轴旋转矩阵 参考3D数学第二节课的围绕z轴渲染矩阵公式
GLfloat zRotation[16] = {
c, -s, 0, 0,
s, c, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
};
// GLfloat zRotation[16] = {
// 1, 0, 0, 0,
// 0, 1, 0, 0,
// 0, 0, 1, 0,
// 0, 0, 0, 1,
// };
glUniformMatrix4fv(rotate, 1, GL_FALSE, zRotation);
glDrawArrays(GL_TRIANGLES, 0, 6);
[self.context presentRenderbuffer:GL_RENDERBUFFER];
}
//设置frameBuffers
- (void)setFrameBuffers
{
GLuint buffer;
glGenFramebuffers(1, &buffer);
self.myColorFrameBuffers = buffer;
glBindFramebuffer(GL_FRAMEBUFFER, self.myColorFrameBuffers);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.myColorRenderBuffers);
}
//设置renderBuffers
- (void)setRenderBuffers
{
//声明一个buffer标识
GLuint buffer;
//申请标识
glGenRenderbuffers(1, &buffer);
//赋值自己的属性标识
self.myColorRenderBuffers = buffer;
//绑定缓冲区
glBindRenderbuffer(GL_RENDERBUFFER, self.myColorRenderBuffers);
//分配缓冲区空间, 由图形上下文完成
[self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.eaglLayer];
}
//清空缓冲区
- (void)clearBufferBit
{
glDeleteRenderbuffers(1, &_myColorRenderBuffers);
self.myColorRenderBuffers = 0;
glDeleteFramebuffers(1, &(_myColorFrameBuffers));
self.myColorFrameBuffers = 0;
}
//设置上下文
- (void)setupContext
{
//初始化
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (self.context == nil)
{
NSLog(@"创建图形上下文失败");
return;
}
//设置当前的图形上下文
if (![EAGLContext setCurrentContext:self.context])
{
NSLog(@"设置图形上下文失败");
self.context = nil;
}
}
//设置图层
- (void)setupLayer
{
//重写layerClass方法,返回CAEAGLLayer图层
self.eaglLayer = (CAEAGLLayer *)self.layer;
//设置屏幕的scale
[self setContentScaleFactor:[UIScreen mainScreen].scale];
//CALayer默认是透明的,需要设置为不透明
self.eaglLayer.opaque = YES;
//设置描述的属性,这里设置不维持渲染内容,以及颜色格式为RGBA8
NSDictionary *properties = @{
kEAGLDrawablePropertyRetainedBacking : @(false),
kEAGLDrawablePropertyColorFormat : kEAGLColorFormatRGBA8,
};
self.eaglLayer.drawableProperties = properties;
}
+ (Class)layerClass
{
return CAEAGLLayer.class;
}
网友评论