iOS OpenGL ES加载纹理(GLSL)

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];
}
  • 2、设置图层
- (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];
}
  • 3、初始化上下文
- (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");
        }
}
  • 4、清空缓冲区
- (void)deleBuffer{
        //清空缓冲区
        glDeleteBuffers(1, &_mColorRenderBuffer);
        self.mColorRenderBuffer = 0;
        glDeleteBuffers(1, &_mColorFrameBuffer);
        self.mColorFrameBuffer = 0;
}
  • 5、设置渲染缓冲区
- (void)setupRenderBuffer{
        //开辟渲染缓冲区
        GLuint buffer;
        glGenBuffers(1, &buffer);
        self.mColorRenderBuffer = buffer;
        glBindRenderbuffer(GL_RENDERBUFFER, self.mColorRenderBuffer);
        //将被绘制对象存储在上写文对象中,后面渲染需要
        [self.mContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.mEaglLayer];
}
  • 6、设置帧缓冲区
- (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);
}
  • 7、渲染
- (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];
}
  • 8、加载程序
- (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;
}
  • 9、编译着色器程序
- (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);
}
  • 10、加载纹理图片
- (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)

  • 有空会继续更新实现画笔案例
  • 转载请标明出处
  • 如有错误理解,还请各路大神批评指出

iOS OpenGL ES加载纹理(GLSL)

上一篇:iOS OpenGL ES加载纹理(GLKit)


下一篇:2/12 vue-axios 中 onUploadProgress 实现 上传图片 动态进度条