美文网首页
OpenGLES学习---- 绘制一图片(2)

OpenGLES学习---- 绘制一图片(2)

作者: pengxiaochao | 来源:发表于2021-08-24 20:03 被阅读0次

    基于上一个demo 我们已经可以简单绘制一个三角形了,下面我们把一张图片通过opengl的方式绘制到屏幕上

    1.创建一个view 并改变使其 支持 CAEAGLLayer

    {
        CAEAGLLayer *_eaglLayer;
        GLuint _frameBuffer;
        GLuint _renderBuffer;
        GLuint _program;
        EAGLContext *_currentContext;
        
        GLint _frameWidth;
        GLint _frameHeight;
    }
    
    - (instancetype)initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
        if (self) {
            [self setuLayer];
        }
        return self;
    }
    
    - (void)setuLayer{
        _currentContext = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES2];
        _eaglLayer = (CAEAGLLayer *) self.layer;
        _eaglLayer.frame = self.frame;
        _eaglLayer.opaque = YES;
        _eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                        [NSNumber numberWithBool:YES],kEAGLDrawablePropertyRetainedBacking,
                                        kEAGLColorFormatRGBA8,kEAGLDrawablePropertyColorFormat, nil];
        [EAGLContext setCurrentContext:_currentContext];
        
        [self setupRenderBuffer];
        [self setupFrameBuffer];
        [self setupBindFrameBuffer];
        [self setupProgram];
    }
    
    +(Class)layerClass{
        return [CAEAGLLayer class];
    }
    

    2.创建frameBuffer

    
    
    -(void)setupFrameBuffer {
        glGenFramebuffers(1, &_frameBuffer);
        glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _renderBuffer);
    }
    

    3.创建renderBuffer

    - (void)setupRenderBuffer {
        glGenRenderbuffers(1, &_renderBuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);
        [_currentContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
    }
    
    

    4.创建着色器程序

    顶点着色器

    attribute vec4 position;
    attribute vec2 textCoordinate;
    varying lowp vec2 varyTextCoord;
    void main(){
        varyTextCoord = textCoordinate;
        gl_Position = position;
    }
    
    

    片源着色器

    precision highp float;
    varying lowp vec2 varyTextCoord;
    uniform sampler2D colorMap;
    
    void main(){
        gl_FragColor = texture2D(colorMap, varyTextCoord);
    }
    
    
    - (void)setupProgram {
        GLuint vertexShader = [self loadShaderType:GL_VERTEX_SHADER fileName:@"vertex.vsh"];
        GLuint fragmentShader = [self loadShaderType:GL_FRAGMENT_SHADER fileName:@"fragment.fsh"];
        _program =  glCreateProgram();
        glAttachShader(_program, vertexShader);
        glAttachShader(_program, fragmentShader);
        glLinkProgram(_program);
        
        //检查program结果
        GLint linkSuccess;
        glGetProgramiv(_program, GL_LINK_STATUS, &linkSuccess);
        if (linkSuccess == GL_FALSE) {
            GLchar messages[256];
            glGetProgramInfoLog(_program, sizeof(messages), 0, &messages[0]);
            NSString *messageString = [NSString stringWithUTF8String:messages];
            NSLog(@"error message: %@", messageString);
        }
        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);
    }
    
    

    数据源介绍

    GLfloat vertices[]  = {
        0.5f, -0.5f, -1.0f,     1.0f, 0.0f, //右下角A
        -0.5f, 0.5f, -1.0f,     0.0f, 1.0f, //左上角B
        -0.5f, -0.5f, -1.0f,    0.0f, 0.0f, //左下角C
        
        0.5f, 0.5f, -1.0f,      1.0f, 1.0f, //右上角D
        -0.5f, 0.5f, -1.0f,     0.0f, 1.0f, //左上角B
        0.5f, -0.5f, -1.0f,     1.0f, 0.0f, //右下角A
    };
    

    数据部分没什么好讲的,字节找张纸画一下ABCD 几个点,就知道是什么形状;
    纹理坐标的取值范围是(0 ~1);

    image.png

    纹理坐标系如下图所示,左下角是(0,0 ),右上角是( 1,1);


    image.png

    5.加载一张图片并转成纹理

    //     从图片中加载纹理
    - (GLuint)setupTexture: (NSString *)fileName {
        //1、将UIImage转换为CGImageRef & 判断图片是否获取成功
        CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
    
        if (!spriteImage) {
            NSLog(@"Failed to lead image %@", fileName);
            exit(1);
        }
    
        //2、读取图片的大小、宽和高
        size_t width = CGImageGetWidth(spriteImage);
        size_t height = CGImageGetHeight(spriteImage);
    
        //3、获取图片字节数 宽*高*4(RGBA)
        GLubyte *spriteData = (GLubyte *)calloc(width*height*4, sizeof(GLubyte));
    
        // 4、创建上下文
        /*
        参数1:data,指向要渲染的绘制图像的内存地址
        参数2:width,bitmap的宽度,单位为像素
        参数3:height,bitmap的高度,单位为像素
        参数4:bitPerComponent,内存中像素的每个组件的位数,比如32位RGBA,就设置为8
        参数5:bytesPerRow,bitmap的没一行的内存所占的比特数
        参数6:colorSpace,bitmap上使用的颜色空间  kCGImageAlphaPremultipliedLast:RGBA
        */
        CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
        NSLog(@"kCGImageAlphaPremultipliedLast %d", kCGImageAlphaPremultipliedLast);
    
    
    //    5、在CGContextRef上 --- 将图片绘制出来
        /*
        CGContextDrawImage 使用的是Core Graphics框架,坐标系与UIKit 不一样。UIKit框架的原点在屏幕的左上角,Core Graphics框架的原点在屏幕的左下角。
        CGContextDrawImage
        参数1:绘图上下文
        参数2:rect坐标
        参数3:绘制的图片
        */
    
        CGRect rect = CGRectMake(0, 0, width, height);
    
        //6、使用默认方式绘制
        CGContextDrawImage(spriteContext, rect, spriteImage);
    
        //7、画图完毕就释放上下文
        CGContextRelease(spriteContext);
    
        //8、绑定纹理到默认的纹理ID
        glBindTexture(GL_TEXTURE_2D, 1);
    
        //9、设置纹理属性
        /*
        参数1:纹理维度
        参数2:线性过滤、为s,t坐标设置模式
        参数3:wrapMode,环绕模式
        */
        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;
        //10、载入纹理2D数据
        /*
        参数1:纹理模式,GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
        参数2:加载的层次,一般设置为0
        参数3:纹理的颜色值GL_RGBA
        参数4:宽
        参数5:高
        参数6:border,边界宽度
        参数7:format
        参数8:type
        参数9:纹理数据
        */
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fw, fh, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
    
        //11、释放spriteData
        free(spriteData);
        return 0;
    }
    

    6.加载图片对应的纹理并渲染出来

    - (void)layoutSubviews {
        [self renderlayer];
    }
    
    - (void)renderlayer {
        glViewport(0, 0, _frameWidth, _frameHeight);
        GLuint attributeBuffer;
        glGenBuffers(1, &attributeBuffer);
        glBindBuffer(GL_ARRAY_BUFFER, attributeBuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
        
        ///顶点,(向定位置填充数据)
        GLuint position = glGetAttribLocation(_program, "position");
        glEnableVertexAttribArray(position);
        glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, NULL);
        
        ///纹理坐标 (向纹理坐标填充数据)
        GLuint textCoor = glGetAttribLocation(_program, "textCoordinate");
        glEnableVertexAttribArray(textCoor);
        glVertexAttribPointer(textCoor, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, (float *)NULL+3);
        
        GLuint texture =  [self setupTexture:@"mouse"];
        //设置这个纹理对应第0层纹理,
        glUniform1i(glGetUniformLocation(_program, "colorMap"), 0);
        
        [_currentContext presentRenderbuffer:_renderBuffer];
        
        /// 绘制
        glViewport(0, 0, _frameWidth, _frameHeight);
        glClearColor(0, 0, 0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
        glUseProgram(_program);
        glDrawArrays(GL_TRIANGLES, 0, 6);
      
        [_currentContext presentRenderbuffer:GL_RENDERBUFFER];
        
    }
    
    

    效果如下:

    IMG_0091.PNG

    反思:

    绘制的图是和图片本身是反的;这是因为纹理坐标的原点(0,0)是在左下角,而屏幕的坐标原点(0,0)是在左上角,下一节我们将图片反转过来;

    相关文章

      网友评论

          本文标题:OpenGLES学习---- 绘制一图片(2)

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