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

2021-08-09 11:18:39 浏览数 (1)

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

0 人点赞