本文档讨论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
#import
@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