本文檔討論glEnableVertexAttribArray的作用及調(diào)用位置問題。
對于OpenGL ES 3.0,可使用至少16個頂點屬性,可查詢具體實現(xiàn)的支持數(shù)量。
GLint maxVertexAttribs;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
默認情況下,出于性能考慮,所有頂點著色器的屬性(Attribute)變量都是關(guān)閉的,意味著數(shù)據(jù)在著色器端是不可見的,哪怕數(shù)據(jù)已經(jīng)上傳到GPU,由glEnableVertexAttribArray啟用指定屬性,才可在頂點著色器中訪問逐頂點的屬性數(shù)據(jù)。glVertexAttribPointer或VBO只是建立CPU和GPU之間的邏輯連接,從而實現(xiàn)了CPU數(shù)據(jù)上傳至GPU。但是,數(shù)據(jù)在GPU端是否可見,即,著色器能否讀取到數(shù)據(jù),由是否啟用了對應(yīng)的屬性決定,這就是glEnableVertexAttribArray的功能,允許頂點著色器讀取GPU(服務(wù)器端)數(shù)據(jù)。
那么,glEnableVertexAttribArray應(yīng)該在glVertexAttribPointer之前還是之后調(diào)用?答案是都可以,只要在繪圖調(diào)用(glDraw*系列函數(shù))前調(diào)用即可。
下面示例代碼驗證了不用glEnableVertexAttribArray對應(yīng)屬性時繪制內(nèi)容為清除緩沖區(qū)顏色。在取消glEnableVertexAttribArray(0);注釋后,得到正常的繪制結(jié)果。
正常情況
#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