iOS OpenGL ES加载纹理(GLSL)
一、准备工作
- 1、创建UIViewController控制器
- 2、创建UIView
- 3、将创建的UIView添加至UIViewController中
- 4、下面的方法是在创建的UIView中调用(依次调用二中2-7的方法)
- 5、准备一张图片(纹理)
- 6、UIView文件中的变量定义
@property (nonatomic,strong) CAEAGLLayer *mEaglLayer;
@property (nonatomic,strong) EAGLContext *mContext;
@property (nonatomic,assign) GLuint mColorRenderBuffer;
@property (nonatomic,assign) GLuint mColorFrameBuffer;
@property (nonatomic,assign) GLuint mProgram;
二、编写代码
- 1、重写+ (Class)layerClass方法(必须重写)
+ (Class)layerClass{
return [CAEAGLLayer class];
}
- (void)setupLayer{
self.mEaglLayer = (CAEAGLLayer *)self.layer;
//设置内容缩放因子
[self setContentScaleFactor:[UIScreen mainScreen].scale];
//设置不透明
self.mEaglLayer.opaque = YES;
/**
设置描述属性
1.kEAGLDrawablePropertyRetainedBacking
表示绘图表面显示后,是否保留其内容,通过一个NSNumber 包装一个bool值。如果是NO,表示
显示内容后,不能依赖于相同的内容;如果是YES,表示显示内容后不变,一般只有在需要内容保存不变的情况下才使用YES,设置为YES,会导致性能降低,内存使用量降低。一般设置为NO。
2.kEAGLDrawablePropertyColorFormat
表示绘制表面的内部颜色缓存区格式
**/
self.mEaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO],kEAGLDrawablePropertyRetainedBacking,kEAGLColorFormatRGBA8,kEAGLDrawablePropertyColorFormat, nil];
}
- (void)setupContext{
//初始化上下文
self.mContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if(!self.mContext){
NSLog(@"create context fail");
}
if(![EAGLContext setCurrentContext:self.mContext]){
NSLog(@"set current context fail");
}
}
- (void)deleBuffer{
//清空缓冲区
glDeleteBuffers(1, &_mColorRenderBuffer);
self.mColorRenderBuffer = 0;
glDeleteBuffers(1, &_mColorFrameBuffer);
self.mColorFrameBuffer = 0;
}
- (void)setupRenderBuffer{
//开辟渲染缓冲区
GLuint buffer;
glGenBuffers(1, &buffer);
self.mColorRenderBuffer = buffer;
glBindRenderbuffer(GL_RENDERBUFFER, self.mColorRenderBuffer);
//将被绘制对象存储在上写文对象中,后面渲染需要
[self.mContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.mEaglLayer];
}
- (void)setupFrameBuffer{
//开辟帧缓冲区
GLuint buffer;
glGenBuffers(1, &buffer);
self.mColorFrameBuffer = buffer;
glBindFramebuffer(GL_FRAMEBUFFER, self.mColorFrameBuffer);
//将渲染缓冲区绑定至GL_COLOR_ATTACHMENT0
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.mColorRenderBuffer);
}
- (void)renderLayer{
//清理屏幕
glClearColor(0.0, 1.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
CGFloat 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 *vertexFile = [[NSBundle mainBundle] pathForResource:@"shader.vsh" ofType:nil];
//获取片元着色器程序文件路径
NSString *fragmentFile = [[NSBundle mainBundle] pathForResource:@"shader.fsh" ofType:nil];
//加载顶点着色器和片元着色器程序
self.mProgram = [self loadProgram:vertexFile fFile:fragmentFile];
//链接程序
glLinkProgram(self.mProgram);
//判断是否链接成功
GLint lintStatus;
glGetProgramiv(self.mProgram, GL_LINK_STATUS, &lintStatus);
if(lintStatus == GL_FALSE){
GLchar message[512];
glGetProgramInfoLog(self.mProgram, sizeof(message), 0, &message[0]);
NSString *str = [NSString stringWithUTF8String:message];
NSLog(@"program link error,reason:%@",str);
return;
}
NSLog(@"program link success");
//使用程序
glUseProgram(self.mProgram);
//设置顶点数据
GLfloat vertexs[] = {
-0.5,0.5,0.0, 0.0,1.0,//左上(x,y,z,r,s) + 纹理做点
0.5,0.5,0.0, 1.0,1.0,//左上(x,y,z,r,s) + 纹理做点
-0.5,-0.5,0.0,0.0,0.0,//左上(x,y,z,r,s) + 纹理做点
0.5,0.5,0.0, 1.0,1.0,//左上(x,y,z,r,s) + 纹理做点
0.5,-0.5,0.0, 1.0,0.0,//左上(x,y,z,r,s) + 纹理做点
-0.5,-0.5,0.0,0.0,0.0//左上(x,y,z,r,s) + 纹理做点
};
//开辟顶点缓冲区
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
//将顶点数据拷贝至顶点缓冲区
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexs), vertexs, GL_DYNAMIC_DRAW);
//获取顶点着色器中的position属性并开启该属性(使其有效)
GLuint position = glGetAttribLocation(self.mProgram, "position");
glEnableVertexAttribArray(position);
/**
设置读取顶点数据的步长
参数1:属性index
参数2: 一个顶点的数值数,例如:上面vertexs顶点数据(x,y,z)
参数3: 数据类型
参数4:步长,顶点到读取下个顶点的间隔
参数5: 顶点数据开始指针
**/
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 0);
//获取顶点着色器的纹理坐标属性并开启该属性(使其有效)
GLuint textCoordinate = glGetAttribLocation(self.mProgram, "textureCoordinate");
glEnableVertexAttribArray(textCoordinate);
/**
设置读取顶点数据的步长
参数1:属性index
参数2: 一个顶点的数值数,例如:上面vertexs顶点数据(r,s)
参数3: 数据类型
参数4:步长,顶点到读取下个顶点的间隔
参数5: 顶点数据开始指针
**/
glVertexAttribPointer(textCoordinate, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 3);
//加载纹理
[self loadTexture:@"timg-3"];
//获取顶点着色器的旋转矩阵属性
GLuint rotateMatrix = glGetUniformLocation(self.mProgram, "rotateMatrix");
float radians = 10 * M_PI / 180.0;
float cos = cosf(radians);
float sin = sinf(radians);
//绕Z轴旋转的矩阵
GLfloat ratateMatrixs[] = {
cos,sin,0.0,0.0,
-sin,-cos,0.0,0.0,
0.0,0.0,1.0,0.0,
0.0,0.0,0.0,1.0
};
//旋转矩阵赋值
glUniformMatrix4fv(rotateMatrix, 1, GL_FALSE, (GLfloat *)&ratateMatrixs[0]);
//显示纹理
glDrawArrays(GL_TRIANGLES, 0, 6);
[self.mContext presentRenderbuffer:GL_RENDERBUFFER];
}
- (GLuint)loadProgram:(NSString *)vFile fFile:(NSString *)fFile{
GLuint vShader,fShader;
//加载顶点着色器
[self compileShader:&vShader type:GL_VERTEX_SHADER file:vFile];
//加载片元着色器
[self compileShader:&fShader type:GL_FRAGMENT_SHADER file:fFile];
GLuint program = glCreateProgram();
glAttachShader(program, vShader);
glAttachShader(program, fShader);
glDeleteShader(vShader);
glDeleteShader(fShader);
return program;
}
- (void)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file{
//编译着色器
NSString *content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
const GLchar *source = [content UTF8String];
*shader = glCreateShader(type);
glShaderSource(*shader, 1, &source, NULL);
glCompileShader(*shader);
}
- (void)loadTexture:(NSString *)fileName{
//加载纹理
CGImageRef image = [UIImage imageNamed:fileName].CGImage;
if(!image){
NSLog(@"texture image load fail");
exit(1);
}
//图片宽高
size_t width = CGImageGetWidth(image);
size_t height = CGImageGetHeight(image);
GLubyte *data = (GLubyte *)calloc(width * height * 4, sizeof(GLubyte));
/**
参数1: 渲染绘制地址
参数2:宽度
参数3:高度
参数4:RGB颜色空间,每个颜色通道一般是8位
参数5:颜色空间
参数6:颜色通道:RGBA=kCGImageAlphaPremultipliedLast
*/
CGContextRef context = CGBitmapContextCreate(data, width, height, 8, width * 4, CGImageGetColorSpace(image), kCGImageAlphaPremultipliedLast);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
CGContextRelease(context);
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);
float fw = width,fh = height;
/**
生成2D纹理
参数1:target,纹理目标,因为你使用的是glTexImage2D函数,所以必须设置为GL_TEXTURE_2D
参数2:level,0,基本图像级
参数3:颜色组件,GL_RGBA,GL_ALPHA,GL_RGBA
参数4:宽度
参数5:高度
参数6:纹理边框宽度
参数7:像素颜色格式
参数8:像素数据类型
参数9:数据地址
*/
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fw, fh, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glBindTexture(GL_TEXTURE_2D, 0);
free(data);
}
- 11、项目中创建顶点着色器文件(文件随便取名,为方便区分而使用vsh为后缀的文件,比如:shader.vsh)
//文件中的代码
//顶点位置
attribute vec4 position;
//纹理坐标
attribute vec2 textureCoordinate;
//旋转矩阵
uniform mat4 rotateMatrix;
//将纹理坐标传到片元着色器,此定义必须与片元着色器文件中定义一样
varying lowp vec2 varyTextCoord;
void main(){
varyTextCoord = textureCoordinate;
vec4 vPos = position;
vPos = vPos * rotateMatrix;
gl_Position = vPos;
}
- 12、项目中创建片元着色器文件(文件随便取名,为方便区分而使用fsh为后缀的文件,比如:shader.fsh)
//文件中的代码
//顶点着色器传过来的纹理坐标
varying lowp vec2 varyTextCoord;
//纹理
uniform sampler2D colorMap;
void main(){
gl_FragColor = texture2D(colorMap,varyTextCoord);
}
三、效果
- 有空会继续更新实现画笔案例
- 转载请标明出处
- 如有错误理解,还请各路大神批评指出
iOS OpenGL ES加载纹理(GLSL)