美文网首页OpenGL
0013--GLSL和GLKit的索引绘制

0013--GLSL和GLKit的索引绘制

作者: 清风烈酒2157 | 来源:发表于2020-08-08 09:35 被阅读0次

    [toc]

    前言

    OpenGL提供了一些绘图函数。到目前为止我们使用的glDrawArrays绘图函数属于”顺序绘制”。这意味着顶点缓冲区从指定的偏移量开始被扫描,每X(点为1,直线为2等)个顶点构成一个图元。这样使用起来非常方便,缺点是当多个图元共用一个顶点时,这个顶点必须在顶点缓冲区中出现多次。也就是说,这些顶点没有共享的概念。属于”索引绘制”的函数则提供这种共享机制。我们除了一个顶点缓存区外,还有一个索引缓存区用来存放顶点的索引值。索引缓存区的扫描和顶点缓存区类似,以每X个索引对应的顶点构成一个基本图元。共享机制在提高内存使用效率上非常重要,因为计算机中的绝大多数图形对象都是三角形网格构成的,这些三角形有很多都是共用顶点。

    • 顺序绘制,一个金字塔,需要传入18个顶点的信息
    image.png
    • 索引绘制

    1.只需要传入5个顶点的位置和绘制不同面的连接方式.

    image

    GLSL绘制金字塔

    • 0.vsh,fsh自定义着色器的代码

    1..vsh

    ...
    uniform mat4 projectionMatrix;
    uniform mat4 modelViewMatrix;
    ...
    void main(){
      ...
      
      vec4 vPos;
      vPos = projectionMatrix * modelViewMatrix *    position;
     gl_Position = vPos;
    }
    
    
    

    2.fsh

    varying lowp vec4 varyColor;
    
    void main(){
        gl_FragColor = varyColor;
    }
    

    增加两个uniform修饰的4x4矩阵,模型矩阵和投影矩阵.
    将它们的相乘之后的结果返回给gl_Position;

    • 1.设置图层
    - (void)setUpLayer{
        self.myEagLayer = (CAEAGLLayer *)self.layer;
        [self setContentScaleFactor:[[UIScreen mainScreen] scale]];
        self.myEagLayer.opaque = YES;
        NSDictionary *dict = @{
            kEAGLDrawablePropertyRetainedBacking:@(NO),
            kEAGLDrawablePropertyColorFormat:kEAGLColorFormatRGBA8
        };
        self.myEagLayer.drawableProperties = dict;
        
    }
    + (Class)layerClass{
        return [CAEAGLLayer class];
    }
    
    • 2.设置上下文
    - (void)setContext{
        
        EAGLContext *context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES2];
        if (!context){
            NSLog(@"context init error");
            return;
        }
        if (![EAGLContext setCurrentContext:context]){
            NSLog(@"setCurrentContext error");
            return;
        }
        self.myContext = context;
    }
    
    • 3.清空缓冲区
    
    - (void)deleBuffer{
        glDeleteBuffers(1, &_myColorRenderBuffer);
        self.myColorRenderBuffer = 0;
        glDeleteBuffers(1, &_myColorFrameBuffer);
        self.myColorFrameBuffer = 0;
    }
    
    
      1. 设置renderBuffer
    - (void)setUpRenderBuffer{
        GLuint buffer;
        glGenRenderbuffers(1, &buffer);
        self.myColorRenderBuffer = buffer;
        glBindRenderbuffer(GL_RENDERBUFFER, self.myColorRenderBuffer);
        [self.myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.myEagLayer];
    }
    
      1. 设置renderBuffer
    - (void)setUpFrameBuffer{
        GLuint buffer;
        glGenFramebuffers(1, &buffer);
        self.myColorFrameBuffer = buffer;
        glBindFramebuffer(GL_FRAMEBUFFER, self.myColorFrameBuffer);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.myColorRenderBuffer);
    }
    
    
    • 开启绘制
    1. 加载链接自定义着色器
    
    //清屏设置颜色
        glClearColor(0.0, 0.0, 0.3, 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 *shaderVfile = [[NSBundle mainBundle] pathForResource:@"shaderV" ofType:@"vsh"];
        NSString *shaderFfile = [[NSBundle mainBundle] pathForResource:@"shaderF" ofType:@"fsh"];
        //存在清空
        if (self.myProgram){
            glDeleteProgram(self.myProgram);
            self.myProgram = 0;
        }
        //加载 绑定 链接 使用
    //    self.myProgram =  [self loadShader:shaderVfile frag:shaderFfile];
        self.myProgram = [GLESUtils loadProgram:shaderVfile withFragmentShaderFilepath:shaderFfile];
        //链接
        glLinkProgram(self.myProgram);
        GLint linkSuccess;
        
        //获取链接状态
        
        glGetProgramiv(self.myProgram, GL_LINK_STATUS, &linkSuccess);
        if (linkSuccess == GL_FALSE){
            GLchar messages[1024];
            glGetProgramInfoLog(self.myProgram, sizeof(messages), 0, &messages[0]);
            NSString *messageString = [NSString stringWithUTF8String:messages];
            NSLog(@"error%@", messageString);
            
            return ;
        }
        NSLog(@"link success");
        glUseProgram(self.myProgram);
    
    
    • 清屏设置颜色
    • 调整视口
    • 获取顶点着色器程序和片段着色器程序的文件路径
    • 加载 绑定 链接 使用
    1. 传入顶点数据和颜色值
    
     // 创建顶点数组和索引数组
        GLfloat attArr[] = {
           -0.5f, 0.5f, 0.0f,      1.0f, 0.0f, 1.0f, //左上0
           0.5f, 0.5f, 0.0f,       1.0f, 0.0f, 1.0f, //右上1
           -0.5f, -0.5f, 0.0f,     1.0f, 1.0f, 1.0f, //左下2
           0.5f, -0.5f, 0.0f,      1.0f, 1.0f, 1.0f, //右下3
           0.0f, 0.0f, 1.0f,       0.0f, 1.0f, 0.0f, //顶点4
            
        };
        //索引
        GLuint indices[] = {
           0, 3, 2,
           0, 1, 3,
           0, 2, 4,
           0, 4, 1,
           2, 3, 4,
           1, 4, 3,
        };
        
        // 判断顶点缓冲区是否为空,如果为空则申请一个缓冲区标识
        if (self.myVertices == 0){
            glGenBuffers(1, &_myVertices);
        }
        //处理顶点数据
        glBindBuffer(GL_ARRAY_BUFFER, _myVertices);
        glBufferData(GL_ARRAY_BUFFER, sizeof(attArr), attArr, GL_DYNAMIC_DRAW);
        // 获取文件中position
        GLuint position = glGetAttribLocation(self.myProgram, "position");
        //打开position
        glEnableVertexAttribArray(position);
        //读取方式
        glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6, NULL);
        
    
       // 获取文件中positionColor
        GLuint positionColor = glGetAttribLocation(self.myProgram, "positionColor");
        //打开positionColor
        glEnableVertexAttribArray(positionColor);
        //读取方式
        glVertexAttribPointer(positionColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6,(float *)NULL + 3);
    
    
    1. 模型矩阵和投影矩阵的值设置
    //获取myProgram中的projectionMatrix、modelViewMatrix
        GLuint projectionMatrixSlot = glGetUniformLocation(self.myProgram, "projectionMatrix");
        GLuint modelViewMatrixSlot = glGetUniformLocation(self.myProgram, "modelViewMatrix");
        float width = self.frame.size.width;
        float height = self.frame.size.height;
        
        KSMatrix4 _projectionMatrix;
        ksMatrixLoadIdentity(&_projectionMatrix);
        float aspect = width/height;
        
        ksPerspective(&_projectionMatrix, 30, aspect, 5.0f, 20.f);
        glUniformMatrix4fv(projectionMatrixSlot, 1, GL_FALSE, (GLfloat *)&_projectionMatrix.m[0][0]);
        
        KSMatrix4 _modelViewMatrix;
        ksMatrixLoadIdentity(&_modelViewMatrix);
        ksTranslate(&_modelViewMatrix, 0, 0, -10.0);
        
        KSMatrix4 _rotationMatrix;
        ksMatrixLoadIdentity(&_rotationMatrix);
        ksRotate(&_rotationMatrix, xDegree, 1.0, 0.0, 0.0);
        ksRotate(&_rotationMatrix, yDegree, 0.0, 1.0, 0.0);
        ksRotate(&_rotationMatrix, zDegree, 0.0, 0.0, 1.0);
        
        ksMatrixMultiply(&_modelViewMatrix, &_rotationMatrix, &_modelViewMatrix);
        
        glUniformMatrix4fv(modelViewMatrixSlot, 1, GL_FALSE, (GLfloat *)&_modelViewMatrix.m[0][0]);
    

    void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
    参数列表:
    location:指要更改的uniform变量的位置
    count:更改矩阵的个数
    transpose:是否要转置矩阵,并将它作为uniform变量的值。必须为GL_FALSE
    value:执行count个元素的指针,用来更新指定uniform变量

    1. 开始正背面剔除 颜色混合 开始绘制
    
    glEnable(GL_CULL_FACE);
    
        glEnable(GL_BLEND);
        //2.开启组合函数 计算混合颜色因子
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        
        glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(indices[0]), GL_UNSIGNED_INT, indices);
        [self.myContext presentRenderbuffer:GL_RENDERBUFFER];
    
    

    使用索引绘图
    void glDrawElements(GLenum mode,GLsizei count,GLenum type,const GLvoid * indices);
    参数列表:
    mode:要呈现的画图的模型
    GL_POINTS
    GL_LINES
    GL_LINE_LOOP
    GL_LINE_STRIP
    GL_TRIANGLES
    GL_TRIANGLE_STRIP
    GL_TRIANGLE_FAN
    count:绘图个数
    type:类型
    GL_BYTE
    GL_UNSIGNED_BYTE
    GL_SHORT
    GL_UNSIGNED_SHORT
    GL_INT
    GL_UNSIGNED_INT
    indices:绘制索引数组

    5.旋转动画

    
    - (IBAction)roteXAction:(UIButton *)sender {
        if (!myTimer){
            myTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(roteAction) userInfo:nil repeats:YES];
        }
        bX = !bX;
    }
    - (IBAction)roteYAction:(UIButton *)sender {
        if (!myTimer){
            myTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(roteAction) userInfo:nil repeats:YES];
        }
        bY = !bY;
    }
    
    - (IBAction)roteZAction:(UIButton *)sender {
        if (!myTimer){
            myTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(roteAction) userInfo:nil repeats:YES];
        }
        bZ = !bZ;
    }
    
    - (void)roteAction{
        xDegree += bX*5;
        yDegree += bY*5;
        zDegree += bZ*5;
        [self render];
    }
    
    

    点击绕对应轴旋转,再次点击停止.
    如果停止X轴旋转,X = 0则度数就停留在暂停前的度数.

    效果图:

    image

    demo

    GLKit绘制金字塔

    • 1.初始化
    1. OPVertex保存顶点坐标和颜色值
    
    typedef struct OPVertex{
        GLKVector3 positionCoord;
        GLKVector3 colorCoord;
    }OPVertex;
    
    • 2.设置上下文,开启深度测试
    
    - (void)setUpcontext{
        self.myContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
        GLKView *view = (GLKView *)self.view;
        view.context = self.myContext;
        view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
        view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
       BOOL isSuc =  [EAGLContext setCurrentContext:self.myContext];
        if (!isSuc){
            
            NSLog(@"setCurrentContext error");
            return;
        }
        glEnable(GL_DEPTH_TEST);
    }
    
    
      1. 开始绘制
    1. 设置顶点数据和索引绘制数组
    
    self.vertices = (OPVertex *)malloc(sizeof(OPVertex)*kCount);
        self.vertices[0] = (OPVertex){{-0.5f, 0.5f, 0.0f},{1.0f, 0.0f, 1.0f}};
        self.vertices[1] = (OPVertex){{0.5f, 0.5f, 0.0f},{1.0f, 0.0f, 1.0f}};
        self.vertices[2] = (OPVertex){{-0.5f, -0.5f, 0.0f},{1.0f, 1.0f, 1.0f}};
        self.vertices[3] = (OPVertex){{0.5f, -0.5f, 0.0f},{1.0f, 1.0f, 1.0f}};
        self.vertices[4] = (OPVertex){{0.0f, 0.0f, 1.0f},{0.0f, 1.0f, 0.0f}};
        //绘图索引
        GLuint indices[] =
           {
               0, 3, 2,
               0, 1, 3,
               0, 2, 4,
               0, 4, 1,
               2, 3, 4,
               1, 4, 3,
           };
    
    
    1. 计算顶点个数
    self.count = sizeof(indices) /sizeof(GLuint);
    
    1. 将顶点数组放入数组缓冲区中 GL_ARRAY_BUFFER
    
     GLuint buffer;
        glGenBuffers(1, &buffer);
        glBindBuffer(GL_ARRAY_BUFFER, buffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(OPVertex)*kCount, self.vertices, GL_STATIC_DRAW);
    
    
    1. 将索引数组存储到索引缓冲区 GL_ELEMENT_ARRAY_BUFFER
    
    GLuint bufferIndex;
        glGenBuffers(1, &bufferIndex);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferIndex);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    
    1. 使用顶点数据
    glEnableVertexAttribArray(GLKVertexAttribPosition);
        glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, NULL);
    
    
    1. 使用颜色数据
    
    glEnableVertexAttribArray(GLKVertexAttribColor);
        glVertexAttribPointer(GLKVertexAttribColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6, (GLfloat *)NULL + 3);
    
    1. 初始化着色器
     self.mEffect = [[GLKBaseEffect alloc]init];
    
    1. 创建投影视图矩阵
    CGSize size = self.view.bounds.size;
        float aspect = fabs(size.width / size.height);
        GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(90.0), aspect, 0.1f, 100.0);
        projectionMatrix = GLKMatrix4Scale(projectionMatrix, 1.0f, 1.0f, 1.0f);
        self.mEffect.transform.projectionMatrix = projectionMatrix;
    
    
    1. 模型视图矩阵
    
    GLKMatrix4 modelViewMatrix = GLKMatrix4Translate(GLKMatrix4Identity, 0.0f, 0.0f, -2.0f);
        self.mEffect.transform.modelviewMatrix = modelViewMatrix;
    
    1. 创建一个定时器用来旋转金字塔
    [NSTimer scheduledTimerWithTimeInterval:0.1 repeats:YES block:^(NSTimer * _Nonnull timer) {
            self.XDegree += 0.1f * self.XB;
            self.YDegree += 0.1f * self.YB;
            self.ZDegree += 0.1f * self.ZB ;
        }];
    
    
    1. 开始绘制
    - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
        glClearColor(0.1f, 0.3f, 0.5f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        [self.effect prepareToDraw];
        glDrawElements(GL_TRIANGLES, self.count, GL_UNSIGNED_INT, 0);
    }
    

    demo

    效果图:

    image

    总结

    image
    1. API的工作
    • 设置图层
    • 设置上下文
    • 清空缓冲区
    • 设置renderBuffer
    • 设置FrameBuffer
    1. 绘制
    • 把顶点数据,颜色数据等从CPU内存复制到GPU
    • 在可编程顶点着色器中对顶点坐标纹理坐标,颜色数据,模型矩阵,投影矩阵等输入.
    • 输出 gl_position等
    • 然后信息图元装配,视口变换等,生成一组二维数据传递给片段着色器.
    • 片段着色器输出gl_Fragcolor
    • 最后通过片段操作
    • 数据放在帧缓存区

    在开发中,基本上是按照这种模式进行代码编写.

    相关文章

      网友评论

        本文标题:0013--GLSL和GLKit的索引绘制

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