美文网首页
4.OpenGL ES 加载图片

4.OpenGL ES 加载图片

作者: 亟措辰沧丶 | 来源:发表于2018-03-13 11:13 被阅读67次

    尝试用可编程管线简单地加载一张图片。

    准备工作

    修改main.storyboard默认View、Controller,关联View,CCViewController继承自UIViewController,CCView继承自UIView image.png

    创建可编程管线的顶点着色器文件以及片元着色器文件 image.png

    值得注意的是,文件命名及后缀不重要,只要不与系统的文件后缀冲突,以及不影响自己辨别均可

    顶点着色器(shaderv.vsh)

    自己写记得把中文注释去掉,因为随时可能发生报错的情况

    //Vertex shader
    //顶点数据
    attribute vec4 position;
    //纹理
    attribute vec2 textCoordinate;
    
    //旋转矩阵
    uniform mat4 rotateMatrix;
    
    //将纹理数据传递到片元着色器去
    varying lowp vec2 varyTextCoord;
    
    void main(){
        //将textCoordinate 通过 varyTextCoord 传递到片元着色器中去
        varyTextCoord = textCoordinate;
        
        vec4 vPos = position;
        
        //将顶点应用旋转变换
        vPos = vPos * rotateMatrix;
        
        //内建变量,gl_Position 必须赋值
        gl_Position = vPos;   
    }
    
    片元着色器(shaderf.fsh)
    //OpenGL ES 中的三种修饰类型 uniform attribute varying
    
    //GLSL是一门写着色器程序的语言OpenGL shading language
    //OpenGL 固定管线:高级API,通过传递参数实现效果
    //可编程管线:能让你对OpenGL渲染过程中其中一些着色环节进行自定义
    //可以自定义的环节
    /*
     1.顶点着色器->处理每个顶点,确定位置,以及变换
     2.片元着色器->片元,定义每一个像素
    
     不采用GLBaseEffect,使用编译链接自定义shader,用简单GLSL来实现顶点着色器\片元着色器,并且实现图形简单变换
     思路:
        1.创建图层
        2.创建上下文
        3.清空缓存区
        4.设置RenderBuffer,FrameBuffer
        5.kai'shi'hui'zhi
     */
     
    /*
     -------------------------uniform--------------------------------------
        uniform是由外部application程序传递给Vertex Shader,fragment Shader变量
        A.由application通过glUniform**()函数传递值的
        B.在Vertex Shader,fragment Shader内部中,类似C语言的const,它不能被shader修改
     注意:Uniform变量,shader只能用,不能改!!!
     
     例如:
        Uniform mat4 viewProjectMatrix;
        Uniform mat4 viewMatrix;
        Uniform vec3 lightPosition;
     
     
     -------------------------attribute--------------------------------------
     
     一般attribute来表示顶点坐标\法线\纹理坐标\顶点颜色
     
     attribute vec4 a_position;
     attribute vec2 a_textCoord0;
     
     注意:attribute只能在Vertex shader中使用,不能再fragment shader中声明attribute变量,也不能被fragment shader使用
     
     -------------------------------varying-------------------------------------
     varying,在vertex和fragement shader之间传递数据用
     varying vec2 a_textCoord0
     */
    
    varying lowp vec2 varyTextCoord;
    
    //2D纹理贴图
    uniform sampler2D colorMap;
    
    void main(){
        //内建变量gl_FragColor必须赋值
        gl_FragColor = texture2D(colorMap,varyTextCoord);
    }
    
    CCView.m
    #import <OpenGLES/ES2/gl.h>
    
    @interface CCView ()
    
    /** CAEAGLLayer */
    @property(nonatomic,strong)CAEAGLLayer *myEAGLayer;
    
    /** Context */
    @property(nonatomic,strong)EAGLContext *myContext;
    
    /** RenderColor */
    @property(nonatomic,assign)GLuint myColorRenderBuffer;
    
    /** FrameColor */
    @property(nonatomic,assign)GLuint myColorFrameBuffer;
    
    /** program */
    @property(nonatomic,assign)GLuint myProgram;
    
    @end
    
    //入口函数,需要调用的步骤
    -(void)layoutSubviews{
        //1.设置图层
        [self setUpLayer];
     
        //2.创建上下文
        [self setUpContext];
        
        //3.清空缓冲区
        [self deleteRenderAndFrameBuffer];
        //4.设置RenderBuffer
        [self setUPRenderBuffer];
        //5.设置FrameBuffer
        [self setUpFrameBuffer];
        
        //6.开始绘制
        [self renderLayer];
    }
    
    1.设置图层
    -(void)setUpLayer{
        self.myEAGLayer = (CAEAGLLayer *)self.layer;
        
        //2.设置比例因子
        [self setContentScaleFactor:[UIScreen mainScreen].scale];
        
        //3.默认不透明,想要其可见,需设置为不透明
        self.myEAGLayer.opaque = YES;
        
        //4.描述因子
        /*
         kEAGLDrawablePropertyRetainedBacking
         绘图表面显示之后是否保留其内容,一般设置为false;
         它是一个key值,通过一个nsnumber包装bool值
         kEAGLDrawablePropertyColorFormat
         绘制对象内部的颜色缓冲区格式
         kEAGLColorFormatRGBA8  ,4*8,32位的颜色
         kEAGLColorFormatRGB565; 16位
         const kEAGLColorFormatSRGBA8 SRGB
         */
        self.myEAGLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:false],kEAGLDrawablePropertyRetainedBacking,kEAGLColorFormatRGBA8,kEAGLDrawablePropertyColorFormat, nil];
    }
    
    
    +(Class)layerClass{
        return [CAEAGLLayer class];
    }
    
    2.创建上下文
    -(void)setUpContext{
        //1.指定API版本
        EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
        
        //2.创建图形上下文
        EAGLContext *context = [[EAGLContext alloc] initWithAPI:api];
        
        //3.创建是否成功
        if (context == NULL) {
            NSLog(@"Create Context Failed!!!");
            return;
        }
        
        //4.设置图形上下文
        if (![EAGLContext setCurrentContext:context]) {
            NSLog(@"setCurrentContext Failed!");
            return;
        }
        //5.将局部变成全局
        self.myContext = context;
    }
    
    3.清空缓冲区
     
    -(void)deleteRenderAndFrameBuffer{
        /*
         可参考PPT
         buffer氛围FrameBuffer和RenderBuffer 2大类
         frameBuffer(FBO)相当于RenderBuffer的管理者
         
         rendeerbuffer又分为三类:colorbuffer,depthbuffer,setncilbuffer
         
         常用函数
         1.绑定buffer标识
         glGenBuffers(<#GLsizei n#>, <#GLuint *buffers#>)
         glGenRenderbuffers(<#GLsizei n#>, <#GLuint *renderbuffers#>)
         glGenFramebuffers(<#GLsizei n#>, <#GLuint *framebuffers#>)
         2.绑定空间
         glBindBuffer(<#GLenum target#>, <#GLuint buffer#>)
         glBindRenderbuffer(<#GLenum target#>, <#GLuint renderbuffer#>)
         glBindFramebuffer(<#GLenum target#>, <#GLuint framebuffer#>)
         3.删除缓冲区空间
         glDeleteBuffers(1, &_myColorRenderBuffer);
         */
        glDeleteBuffers(1, &_myColorRenderBuffer);
        self.myColorRenderBuffer = 0;
        glDeleteBuffers(1, &_myColorFrameBuffer);
        self.myColorFrameBuffer = 0;
    }
    
    4.设置RenderBuffer
    -(void)setUPRenderBuffer{
        //1.定义一个缓冲区标记
        GLuint buffer;
        //2.申请一个缓冲区标记
        glGenRenderbuffers(1, &buffer);
        
        //3.
        self.myColorRenderBuffer = buffer;
        
        //4将标识符绑定GL_RENDERBUFFER
        glBindRenderbuffer(GL_RENDERBUFFER, self.myColorRenderBuffer);
        
        //5.
        [self.myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.myEAGLayer];
    }
    
    5. 设置FrameBuffer
    -(void)setUpFrameBuffer{
        //1.定义一个缓存区标记
        GLuint buffer;
    
        //2.三个作用都一样都是申请缓冲区标记
        glGenFramebuffers(1, &buffer);
    //    glGenRenderbuffers(<#GLsizei n#>, <#GLuint *renderbuffers#>)
    //    glGenBuffers(<#GLsizei n#>, <#GLuint *buffers#>)
        //3.
        self.myColorFrameBuffer = buffer;
        //4.
        glBindFramebuffer(GL_FRAMEBUFFER, self.myColorFrameBuffer);
        
        //5.将myColorRenderBuffer通过glFrameBufferRenderBuffer绑定到附着点上,GL_COLOR_ATTACHEMENT0(颜色附着点)
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.myColorRenderBuffer);
        
        //6.frame buffer仅仅是管理者,不需要分配空间;render buffer的存储空间的分配,对于不同的render buffer,使用不同的API进行分配,而只有分配空间的时候,render buffer句柄才确定其类型
        
        //myColorRenderBuffer渲染缓存区分配存储空间
        [self.myContext renderbufferStorage:GL_FRAMEBUFFER fromDrawable:self.myEAGLayer];
    }
    
    6. 开始绘制
    -(void)renderLayer{
        //开始写顶点着色器/片元着色器
        //Vertex shader
        //fragment Shader
        
        //已经写好了顶点shaderv.vsh/片元shaderf.fsh
        glClearColor(0, 1, 0, 1);
        glClear(GL_COLOR_BUFFER_BIT);
        
        
        //2.设置视口大小
        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);
        //3.读取顶点、片元着色器程序
        //获取路径
        NSString *vertFile = [[NSBundle mainBundle] pathForResource:@"shaderv" ofType:@"vsh"];
        NSString *fragFile = [[NSBundle mainBundle] pathForResource:@"shaderf" ofType:@"fsh"];
        NSLog(@"vertFile:%@",vertFile);
        NSLog(@"fragFile:%@",fragFile);
        
        //4.加载shader
        self.myProgram = [self LoadShader:vertFile withFrag:fragFile];
        
        //5.链接
        glLinkProgram(self.myProgram);
        
        //获取link的状态是否失败
        GLint linkStatus;
        glGetProgramiv(self.myProgram, GL_LINK_STATUS, &linkStatus);
        if (linkStatus == GL_FALSE) {
            //获取失败信息
            GLchar message[512];
            glGetProgramInfoLog(self.myProgram, sizeof(message), 0, &message[0]);
            //将C字符串转换为OC字符串
            NSString *messageStr = [NSString stringWithUTF8String:message];
            NSLog(@"Program Link Error: %@",messageStr);
            return;
        }
        //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 attrBuffer;
        //申请一个缓冲标记
        glGenBuffers(1, &attrBuffer);
        //绑定缓冲区
        glBindBuffer(GL_ARRAY_BUFFER, attrBuffer);
        //将顶点缓冲区从CPU内存复制到GPU内存中
        glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);
        
        //1.
        GLuint position = glGetAttribLocation(self.myProgram, "position");
        //2.
        glEnableVertexAttribArray(position);
        //3.设置读取方式
        glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, NULL);
    
        //处理纹理数据
        //1.获取纹理的位置-Program
        GLuint textCoor = glGetAttribLocation(self.myProgram, "textCoordinate");
        
        //2.同顶点
        glEnableVertexAttribArray(textCoor);
        //3.//最后一个参数,起点
        glVertexAttribPointer(textCoor, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 3);
        
        //加载纹理
        //通过一个自定义的方法加载纹理
        [self setupTexture:@"timg-2"];
        
        //1.直接用3D数学的公式来实现旋转
        //2.Uniform
        
        //旋转!!!矩阵 -> Uniform 传递到vsh,fsh
        
        //旋转180度-->转弧度
        float radius = 180 * 3.1415925f/180.0f;
        //旋转矩阵公式
        float s = sin(radius);
        float c = cos(radius);
        //构建旋转矩阵-围绕z轴旋转-列矩阵
        GLfloat zRotation[16] = {
            c,-s,0  ,  0,
            s, c,0  ,  0,
            0, 0,1.0,  0,
            0, 0,0  ,1.0,
        };
        //获取位置
        GLuint rotate = glGetUniformLocation(self.myProgram, "rotateMatrix");
        
        //将旋转矩阵通过Uniform传递进去
        
        glUniformMatrix4fv(rotate, 1, GL_FALSE, (GLfloat *)&zRotation[0]);
        
        //绘制,三角形绘制,从0开始,6个顶点
        glDrawArrays(GL_TRIANGLES, 0, 6);
        
        [self.myContext presentRenderbuffer:GL_RENDERBUFFER];
    }
    

    封装的辅助函数

    -(GLuint)LoadShader:(NSString *)vert withFrag:(NSString *)frag{
        //1.定义2个临时着色器变量对象
        GLuint verShader,fragShader;
        GLuint program = glCreateProgram();
        
        //2.编译shader
        [self compileShader:&verShader type:GL_VERTEX_SHADER file:vert];
        [self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:frag];
        
        //3.创建目标程序
        glAttachShader(program, verShader);
        glAttachShader(program, fragShader);
        
        //4.释放
        glDeleteShader(verShader);
        glDeleteShader(fragShader);
        
        return program;
    }
    
    //编译shader
    -(void)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file{
        //读取shader路径
        NSString *content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
        //将oc字符串转为C字符串
        const GLchar *source = (GLchar *)[content UTF8String];
        //创建shader
        *shader = glCreateShader(type);
        //将着色器的代码,附到shader上
        glShaderSource(*shader, 1, &source, NULL);
        
        //将着色器代码编译成目标代码
        glCompileShader(*shader);
    }
    

    相关文章

      网友评论

          本文标题:4.OpenGL ES 加载图片

          本文链接:https://www.haomeiwen.com/subject/fuwafftx.html