美文网首页
OpenGLES(六)-综合案例:索引绘图

OpenGLES(六)-综合案例:索引绘图

作者: Henry________ | 来源:发表于2020-08-16 19:33 被阅读0次

    OpenGLES(六)-综合案例:索引绘图

    效果图

    索引绘图

    相信看这篇文章的同学应该对图元装配方式很熟悉了吧?提供一个参考资料,及时使用合理的图元连接方式,还是难以避免顶点的重复声明,不可避免的会占据额外的缓存区内存。列如这种图形:

    图出自:月月

    五个面需要申明18个顶点(6 * 3),根据观察却只是用了5个顶点,永远可以相信OpenGL一定会提供一种简便方式:索引绘图

    索引绘图: 我们除了一个顶点缓存区外,还有一个索引缓存区用来存放顶点的索引值。通过索引的顺序加之图元连接方式就可以构成一个基本图元(多数情况为三角形)。共享机制在提高内存使用效率上非常重要。

    根绝索引绘图的原理,绘制前只需要将5个顶点坐标传入顶点缓存区,除此之外还需要定义一下索引数组:

    [{2,3,4},{3,1,4},{0,4,1},{2,4,0},{2,3,0},{3,1,0}]
    //当然索引数组不是唯一的
    
    • 这里一样需要注意正背面的顶点顺序,将当前能看到的面设置为逆时针,看不到的面按照顺时针来设置。
    • 即使设置了索引数组,也不能违背图元连接方式.

    GLSL实现

    1.fsh

    precision highp float;
    varying lowp vec2 varyingCoord;
    uniform sampler2D colorMap;
    
    void main() {
        gl_FragColor = texture2D(colorMap, varyingCoord);
    }
    
    • 片元着色器只包含最基础功能,从纹理中按照纹理坐标取出对应纹素的色值,传递给内建变量gl_FragColor
    1. vsh
    attribute vec4 position;
    attribute vec2 textureCoord;
    uniform mat4 projectionMatrix;
    uniform mat4 modelViewMatrix;
    
    varying lowp vec2 varyingCoord;
    
    void main() {
        varyingCoord = textureCoord;
        gl_Position = projectionMatrix * modelViewMatrix * position;
    }
    
    • 增加两个uniform修饰的4x4矩阵,模型矩阵和投影矩阵MVP. 将它们矩阵相乘后,将顶点坐标的偏移结果传递给内建变量gl_Position;
    1. Layer & Content 初始化
        self.myLayer = (CAEAGLLayer *)self.layer;
        [self.myLayer setContentsScale:[[UIScreen mainScreen] scale]];
        self.myLayer.opaque = YES;
        self.myLayer.drawableProperties = @{
            kEAGLDrawablePropertyRetainedBacking:@false,
            kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8,
        };
        
        self.myContent = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
        if (!self.myContent){
            NSLog(@"content init failed");
            exit(1);
        }
        if(![EAGLContext setCurrentContext:self.myContent]){
            NSLog(@"Set Current Context failed");
            exit(1);
        }
    
    1. RenderBuffer & FrameBuffer 初始化
        glGenRenderbuffers(1, &_myRenderBuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, self.myRenderBuffer);
        if (self.myRenderBuffer == GL_FALSE) {
            NSLog(@"create Render Buffer Failed");
            exit(1);
        }
        [self.myContent renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.myLayer];
        
        GLuint frame;
        glGenFramebuffers(1, &frame);
        glBindFramebuffer(GL_FRAMEBUFFER, frame);
        if (frame == GL_FALSE) {
            NSLog(@"create Frame Buffer Failed");
            exit(1);
        }
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, frame);
    
    1. shader的编译
      这部分就不放出代码了,这里有详细的注释iOS- OpenGLES中本地着色器编译

    2. render

    ...省略部分clean代码
    //顶点数组, 前3位顶点, 后3位颜色(RGB,A默认为1.0)
        GLfloat vertex[] = {
            -0.5f, 0.0f, -0.5f, 0, 1,  //左上
            -0.5f, 0.0f,  0.5f, 0, 0,  //左下
             0.5f, 0.0f,  0.5f, 1, 0,  //右下
             0.5f, 0.0f, -0.5f, 1, 1,  //右上
             0.0f, 1.0f,  0.0f, 0.5, 0.5,  //顶点
        };
        
        //索引数组
        //需要根据初始位置的正背面,来确定绘制顺序(逆时针为正面)
        GLuint indices[] = {
            0, 2, 1,    //下左
            3, 2, 0,    //下右
            0, 1, 4,    //上左
            1, 2, 4,    //上前
            2, 3, 4,    //上右
            0, 4, 3,    //上后
        };
        //顶点缓存区初始化
        GLuint verBuffer;
        glGenBuffers(1, &verBuffer);
        glBindBuffer(GL_ARRAY_BUFFER, verBuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), &vertex, GL_DYNAMIC_DRAW);
        //获取shader中的句柄
        //着色器从顶点缓存区的读取方式
        GLuint positon = glGetAttribLocation(self.myProgram, "position");
        glEnableVertexAttribArray(positon);
        glVertexAttribPointer(positon, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 0);
        GLuint textureCoord = glGetAttribLocation(self.myProgram, "textureCoord");
        glEnableVertexAttribArray(textureCoord);
        glVertexAttribPointer(textureCoord, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 3);
        //开启正背面剔除
        glEnable(GL_CULL_FACE);
        //将图形变换相关的矩阵按Uniform的方式传入
        GLKMatrix4 projectMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(30.0), self.frame.size.width / self.frame.size.height, 1.0, 100.0);
        GLKMatrix4 viewModelMatrix = GLKMatrix4Translate(GLKMatrix4Identity, 0, 0, -15.0);
        viewModelMatrix = GLKMatrix4RotateX(viewModelMatrix, GLKMathDegreesToRadians(xDegree));
        viewModelMatrix = GLKMatrix4RotateY(viewModelMatrix, GLKMathDegreesToRadians(yDegree));
        viewModelMatrix = GLKMatrix4RotateZ(viewModelMatrix, GLKMathDegreesToRadians(zDegree));
        GLuint pro = glGetUniformLocation(self.myProgram, "projectionMatrix");
        glUniformMatrix4fv(pro, 1, GL_FALSE, (GLfloat *)&projectMatrix.m00);
        GLuint viewM = glGetUniformLocation(self.myProgram, "modelViewMatrix");
        glUniformMatrix4fv(viewM, 1, GL_FALSE, (GLfloat *)&viewModelMatrix.m00);
        
        //加压缩图片
        GLuint texture = [self loadImage:@"cat"];
        if(texture == GL_FALSE){
            NSLog(@"Load Image Failed");
            return;
        }
        
        //载入纹理
        //激活当前纹理
        glActiveTexture(texture);
        //0 代表第一个纹理图片
        //若有多个纹理需要载入,需先激活当前纹理,然后根据顺序从0开始算。
        glUniform1i(glGetUniformLocation(self.myProgram, "colorMap"), 0);
        
        glDrawElements(GL_TRIANGLES, sizeof(indices) / sizeof(indices[0]), GL_UNSIGNED_INT, indices);
        
        [self.myContent presentRenderbuffer:GL_RENDERBUFFER];
    
    • 使用coreGraphics来完成图片解压缩,这里有详细的注释iOS-使用coreGraphics进行图片解压缩
    • 相比顶点绘制方式而言,索引绘图只有在最后的绘制API的选择上不同:glDrawElements
    放出以上代码的部分详细注释:传送门

    GLKit实现

    相比GLSL来说代码量会小很多

    1.EAGLContext、GLKBaseEffect初始化

    - (instancetype)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
        //EAGLContext
            EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
            [EAGLContext setCurrentContext:context];
            self = [super initWithFrame:frame context:context];
            self.delegate = self;
        self.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
        self.drawableDepthFormat = GLKViewDrawableDepthFormat24;
        //GLKBaseEffect初始化
        self.myEffect = [[GLKBaseEffect alloc] init];
        self.myEffect.texture2d0.enabled = YES;
        glEnable(GL_DEPTH_TEST);
        }
        return self;
    }
    
    1. render
    //顶点、索引数据与GLSL部分一致
        //顶点缓存区、着色器读取数据的方式
        GLuint vertexBuffer;
        glGenBuffers(1, &vertexBuffer);
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_DYNAMIC_DRAW);
        
        glEnableVertexAttribArray(GLKVertexAttribPosition);
        glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5,(GLfloat *)NULL + 0);
        
        glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
        glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5,(GLfloat *)NULL + 3);
        //纹理图片加载
        NSError *error;
        GLKTextureInfo *texInfo = [GLKTextureLoader textureWithCGImage:[[UIImage imageNamed:@"cat"] CGImage]  options:@{GLKTextureLoaderOriginBottomLeft: @(YES)} error:&error];
        self.myEffect.texture2d0.name = texInfo.name;
        self.myEffect.texture2d0.target = texInfo.target;
        //绘制
        [self display];
    
    1. glkView绘制代理
    - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
        glClearColor(0.5, 0.3, 0.2, 1.0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        //准备绘制
        [self.myEffect prepareToDraw];
        
        //索引数组
        //需要根据初始位置的正背面,来确定绘制顺序(逆时针为正面)
        GLuint indices[] = {
            0, 2, 1,    //下左
            3, 2, 0,    //下右
            0, 1, 4,    //上左
            1, 2, 4,    //上下
            2, 3, 4,    //上左
            0, 4, 3,    //上后
        };
        glDrawElements(GL_TRIANGLES, sizeof(indices) / sizeof(indices[0]), GL_UNSIGNED_INT, indices);
    }
    
    • 放出以上代码的部分详细注释:传送门

    完整DEMO地址: Github

    相关文章

      网友评论

          本文标题:OpenGLES(六)-综合案例:索引绘图

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