美文网首页
OpenGL ES 自定义着色器-绘制图片

OpenGL ES 自定义着色器-绘制图片

作者: lkm_0bdc | 来源:发表于2020-08-04 23:09 被阅读0次

    上一篇我们提到固定着色器同时继承GLKViewController
    绘制图片,那么现在,将讲述如何自定义着色器绘制图片。

    由于苹果没有提供·相应的文件,因此我们需要自己创建文件使用(相当于一个字符串),一般后缀使用\color{red}{vsh}代表顶点着色器,\color{red}{fsh}表示片元着色器,现在看看两个文件的内容。

    在顶点着色器中position定义了图片的顶点位置,textCoordinate是纹理坐标,varyTextCoodi其实也是一个纹理坐标,但是它的作用是一个\color{red}{侨接}的角色,把顶点着色器的纹理传递到片元着色器中,为了达到这个目地,我们需要注意,\color{red}{传递的坐标名字必须一模一样才能完成},如果不需要传递,可以不用保持一致。
    那么在\color{red}{main}函数中,我们进行纹理赋值,由于案例中的图片不需要做形变,因此直接将坐标传出去。

    //顶点坐标
    attribute vec4 position;
    //纹理坐标
    attribute vec2 textCoordinate;
    varying lowp vec2 varyTextCoord;
    void main(){
        varyTextCoord = textCoordinate;
        gl_Position = position;
    }
    

    下面,让我们看看片元着色器需要做的事情,首先定义了一片元着色器的精度,然后varyTextCoord获得顶点着色器的纹理坐标,并且定义了纹理。这是两个必须要获取的数据。
    main函数中我们获取当前像素下面的纹素。
    \color{red}{Textture2d}函数是一个常用的函数,当我们调用这个内建函数之后,会返回一个\color{red}{vec4四维向量}(指的颜色),最后要给到最终结果的内建变量\color{red}{gl_FragColor}

    precision highp float;
    varying lowp vec2 varyTextCoord;
    uniform sampler2D colorMap;
    void main()
    {
        //lowp vec4 temp = texture2D(colorMap, varyTextCoord);
    //gl_FragColor = temp;
    //参数1:纹理
    //参数2:纹理坐标
        gl_FragColor = texture2D(colorMap, varyTextCoord);
    }
    

    顶点着色器/片元着色器的手动编译连接使用

    在开始绘制之前,我们需要加载编译shader

    /加载shader
    -(GLuint)loadShaders:(NSString *)vert Withfrag:(NSString *)frag
    {
        //1.定义2个零时着色器对象
        GLuint verShader, fragShader;
        //创建program
        GLint program = glCreateProgram();
        
        //2.编译顶点着色程序、片元着色器程序
        //参数1:编译完存储的底层地址
        //参数2:编译的类型,GL_VERTEX_SHADER(顶点)、GL_FRAGMENT_SHADER(片元)
        //参数3:文件路径
        [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.释放不需要的shader
        glDeleteShader(verShader);
        glDeleteShader(fragShader);
    
        return program;
    }
    //编译shader
    - (void)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file{
        
        //1.读取文件路径字符串
        NSString* content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
        const GLchar* source = (GLchar *)[content UTF8String];
        
        //2.创建一个shader(根据type类型)
        *shader = glCreateShader(type);
        
        //3.将着色器源码附加到着色器对象上。
        //参数1:shader,要编译的着色器对象 *shader
        //参数2:numOfStrings,传递的源码字符串数量 1个
        //参数3:strings,着色器程序的源码(真正的着色器程序源码)
        //参数4:lenOfStrings,长度,具有每个字符串长度的数组,或NULL,这意味着字符串是NULL终止的
        glShaderSource(*shader, 1, &source,NULL);
    
        //4.把着色器源代码编译成目标代码
        glCompileShader(*shader);    
    }
    

    接下来,就开始我们绘制图片的思路:

    1.创建图层
    2.创建上下文
    3.清空缓存区
    4.设置RenderBuffer
    5.设置FrameBuffer
    6.开始绘制

    创建图层

    //1.设置图层
    -(void)setupLayer
    {
        //1.创建特殊图层
        /*
         重写layerClass,将CCView返回的图层从CALayer替换成CAEAGLLayer
         */
        self.myEagLayer = (CAEAGLLayer *)self.layer;
        
        //2.设置scale
        [self setContentScaleFactor:[[UIScreen mainScreen]scale]];
        NSLog(@"%f",[[UIScreen mainScreen]scale]);
        //3.设置描述属性,这里设置不维持渲染内容以及颜色格式为RGBA8
        /*
         kEAGLDrawablePropertyRetainedBacking  表示绘图表面显示后,是否保留其内容。
         kEAGLDrawablePropertyColorFormat
             可绘制表面的内部颜色缓存区格式,这个key对应的值是一个NSString指定特定颜色缓存区对象。默认是kEAGLColorFormatRGBA8;  
             kEAGLColorFormatRGBA8:32位RGBA的颜色,4*8=32位
             kEAGLColorFormatRGB565:16位RGB的颜色,
             kEAGLColorFormatSRGBA8:sRGB代表了标准的红、绿、蓝,即CRT显示器、LCD显示器、投影机、打印机以及其他设备中色彩再现所使用的三个基本色素。sRGB的色彩空间基于独立的色彩坐标,可以使色彩在不同的设备使用传输中对应于同一个色彩坐标体系,而不受这些设备各自具有的不同色彩坐标的影响。
         */
        self.myEagLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:@false,kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8,kEAGLDrawablePropertyColorFormat,nil]; 
    }
    +(Class)layerClass
    {
        return [CAEAGLLayer class];
    }
    

    设置上下文

    我们依然使用EAGLContext创建上下文,这个必须要做事情。

    //2.设置上下文
    -(void)setupContext
    {
        //1.指定OpenGL ES 渲染API版本,我们使用2.0
        EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
        //2.创建图形上下文
        EAGLContext *context = [[EAGLContext alloc]initWithAPI:api];
        //3.判断是否创建成功
        if (!context) {
            NSLog(@"Create context failed!");
            return;
        }
        //4.设置图形上下文
        if (![EAGLContext setCurrentContext:context]) {
            NSLog(@"setCurrentContext failed!");
            return;
        }
    //5.将局部context,变成全局的
    self.myContext = context;
    

    清空缓存区

    缓存区使用完之后,是需要清空的,不然会照成不必要的内存浪费。

    //3.清空缓存区
    -(void)deleteRenderAndFrameBuffer
    {
        /*
         buffer分为frame buffer 和 render buffer2个大类。
         其中frame buffer 相当于render buffer的管理者。
         frame buffer object即称FBO。
         render buffer则又可分为3类。colorBuffer、depthBuffer、stencilBuffer。
         */
        glDeleteBuffers(1, &_myColorRenderBuffer);
        self.myColorRenderBuffer = 0;
    
        glDeleteBuffers(1, &_myColorFrameBuffer);
        self.myColorFrameBuffer = 0; 
    }
    

    设置RenderBuffer

    RenderBuffer是由FrameBuffer管理的,所以先设置RenderBuffer

    //4.设置RenderBuffer
    -(void)setupRenderBuffer
    {
        //1.定义一个缓存区ID
        GLuint buffer;
        
        //2.申请一个缓存区标志
        glGenRenderbuffers(1, &buffer);
        
        //3.
        self.myColorRenderBuffer = buffer;
        
        //4.将标识符绑定到GL_RENDERBUFFER
        glBindRenderbuffer(GL_RENDERBUFFER, self.myColorRenderBuffer);
        
        //5.将可绘制对象drawable object's  CAEAGLLayer的存储绑定到OpenGL ES renderBuffer对象
        [self.myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.myEagLayer];  
    }
    

    设置FrameBuffer

    //5.设置FrameBuffer
    -(void)setupFrameBuffer
    {
        //1.定义一个缓存区ID
        GLuint buffer;
        
        //2.申请一个缓存区标志
        //glGenRenderbuffers(1, &buffer);
        //glGenFramebuffers(1, &buffer);
        glGenBuffers(1, &buffer);
        
        //3.
        self.myColorFrameBuffer = buffer;
        
        //4.
        glBindFramebuffer(GL_FRAMEBUFFER, self.myColorFrameBuffer);
        
        /*生成帧缓存区之后,则需要将renderbuffer跟framebuffer进行绑定,
         调用glFramebufferRenderbuffer函数进行绑定到对应的附着点上,后面的绘制才能起作用
         */
        
        //5.将渲染缓存区myColorRenderBuffer 通过glFramebufferRenderbuffer函数绑定到 GL_COLOR_ATTACHMENT0上。
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.myColorRenderBuffer);  
    }
    

    纹理加载流程

    纹理加载.png

    开始绘制

    绘制流程图

    这就是我们开始绘制图片的流程,我们最开始加载的shader也是在这里调用

    相关文章

      网友评论

          本文标题:OpenGL ES 自定义着色器-绘制图片

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