OpenGL ES 3.0 glEnableVertexAttribArray的作用

本文档讨论glEnableVertexAttribArray的作用及调用位置问题。

对于OpenGL ES 3.0,可使用至少16个顶点属性,可查询具体实现的支持数量。

GLint maxVertexAttribs;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);

默认情况下,出于性能考虑,所有顶点着色器的属性(Attribute)变量都是关闭的,意味着数据在着色器端是不可见的,哪怕数据已经上传到GPU,由glEnableVertexAttribArray启用指定属性,才可在顶点着色器中访问逐顶点的属性数据。glVertexAttribPointer或VBO只是建立CPU和GPU之间的逻辑连接,从而实现了CPU数据上传至GPU。但是,数据在GPU端是否可见,即,着色器能否读取到数据,由是否启用了对应的属性决定,这就是glEnableVertexAttribArray的功能,允许顶点着色器读取GPU(服务器端)数据。

那么,glEnableVertexAttribArray应该在glVertexAttribPointer之前还是之后调用?答案是都可以,只要在绘图调用(glDraw*系列函数)前调用即可。

下面示例代码验证了不用glEnableVertexAttribArray对应属性时绘制内容为清除缓冲区颜色。在取消glEnableVertexAttribArray(0);注释后,得到正常的绘制结果。

正常情况
#import <UIKit/UIKit.h>
#import <OpenGLES/ES3/gl.h>

@interface MyGLView : UIView

@end

@implementation MyGLView

+ (Class)layerClass {
    return [CAEAGLLayer class];
}

- (void)layoutSubviews {
    [super layoutSubviews];
    
    EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
    [EAGLContext setCurrentContext:context];
    
    GLuint renderbuffer;
    glGenRenderbuffers(1, &renderbuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
    [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.layer];
    
    GLuint framebuffer;
    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
    
    NSString *vertexShaderString = @"#version 300 es  \n"
    "layout (location = 0) in vec4 a_position;  \n"
    "void main() {  \n"
    "gl_Position = a_position;  \n"
    "gl_PointSize = 20.0;}";
    NSString *fragmentShaderString = @"#version 300 es  \n"
    "precision lowp float;  \n"
    "out vec4 o_color;  \n"
    "void main() {  \n"
    "o_color = vec4(1.0, 0, 1.0, 1.0);}";
    
    GLint vertexShader = [self compileShaderWithString:vertexShaderString withType:GL_VERTEX_SHADER];
    GLint fragmentShader = [self compileShaderWithString:fragmentShaderString withType:GL_FRAGMENT_SHADER];
    
    GLint program = glCreateProgram();
    glAttachShader(program, vertexShader);
    glAttachShader(program, fragmentShader);
    glLinkProgram(program);
    GLint linkStatus;
    glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
    if (linkStatus == GL_FALSE) {
        GLint length;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
        if (length > 0) {
            GLchar *infolog = (GLchar *)malloc(sizeof(GLchar) * length);
            glGetProgramInfoLog(program, length, NULL, infolog);
            fprintf(stderr, "link error = %s", infolog);
            if (infolog) {
                free(infolog);
            }
        }
    }
    glValidateProgram(program);
    glUseProgram(program);
    
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    CGRect frame = [UIScreen mainScreen].bounds;
    glViewport(0, 0, frame.size.width * self.layer.contentsScale, frame.size.height * self.layer.contentsScale);
    
    GLfloat vertexs[] = {
        -0.5f,  0.5f, 0.0f,  // Position 0
        -0.5f, -0.5f, 0.0f,  // Position 1
    };
    
    glLineWidth(20);
    
    //glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertexs);
    
    glDrawArrays(GL_LINES, 0, 2/* 1 line = 2 points */);

    [context presentRenderbuffer:GL_RENDERBUFFER];
}

- (GLuint)compileShaderWithString:(NSString *)content withType:(GLenum)type {
    GLuint shader;
    const char *shaderString = content.UTF8String;
    shader = glCreateShader(type);
    glShaderSource(shader, 1, &shaderString, NULL);
    glCompileShader(shader);
    GLint status;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
    if (status == GL_FALSE) {
        GLint length;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
        if (length > 0) {
            GLchar *infolog = (GLchar *)malloc(sizeof(GLchar) * length);
            glGetShaderInfoLog(shader, length, NULL, infolog);
            fprintf(stderr, "%s -> compile error = %s", type == GL_VERTEX_SHADER ? "vertex shader" : "fragment shader", infolog);
            if (infolog) {
                free(infolog);
            }
        }
    }
    return shader;
}

@end

推荐阅读更多精彩内容