前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OpenGLES(六)-综合案例:索引绘图OpenGLES(六)-综合案例:索引绘图

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

作者头像
用户8893176
发布2021-08-09 11:18:39
5160
发布2021-08-09 11:18:39
举报
文章被收录于专栏:小黑娃Henry

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

效果图

索引绘图

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

图出自:月月

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

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

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

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

GLSL实现

1.fsh

代码语言:javascript
复制
precision highp float;
varying lowp vec2 varyingCoord;
uniform sampler2D colorMap;

void main() {
    gl_FragColor = texture2D(colorMap, varyingCoord);
}
  • 片元着色器只包含最基础功能,从纹理中按照纹理坐标取出对应纹素的色值,传递给内建变量gl_FragColor
  • vsh
代码语言:javascript
复制
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;
  • Layer & Content 初始化
代码语言:javascript
复制
    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 初始化
代码语言:javascript
复制
    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
代码语言:javascript
复制
...省略部分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初始化

代码语言:javascript
复制
- (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
代码语言:javascript
复制
//顶点、索引数据与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绘制代理
代码语言:javascript
复制
- (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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/8/16 下,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • OpenGLES(六)-综合案例:索引绘图
    • 索引绘图
      • GLSL实现
        • GLKit实现
          • 完整DEMO地址: Github
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档