据我所知,顶点获取阶段是由 VAO 封装的,并且 VAO 需要包含顶点获取阶段状态,以便在缓冲区对象和顶点属性之间进行管道传输以及格式化缓冲区对象中的数据。
我读过的关于这个主题的两本书 i.Red book, Blue book 都明确提到 VAO 必须包含顶点获取阶段状态数据
然而,当我实际创建 2 个纹理对象并简单地格式化数据一次而不使用 VAO 来存储有关缓冲区的信息时,它仍然运行良好,没有任何问题,然后我再次重新加载第一个对象,并且再次运行良好没有任何问题,那么有关缓冲区对象中数据格式的信息是从哪里提取的呢?
我什至第二次将缓冲区数据上传到同一个缓冲区对象,这意味着之前保存在那里的信息将被重置?并且图片在窗口上仍然呈现良好
那么究竟发生了什么?书上说了一件事,现实中发生的事情完全不同且相反
有人能真正解释一下这里真正需要什么,不需要什么吗?究竟发生了什么?
我们什么时候真正需要 VAO,什么时候可以不需要?当不需要时额外的代码处理有什么意义呢?
代码如下:
int main(){
int scrW=1280, scrH=720;
//create context and shader program
init(scrW, scrH);
createShaders();
//create texture objects and load data from image to server memory
char object[2][25];
strcpy(object[0], "back.bmp");
strcpy(object[1], "256x256.bmp");
//triangle 1
GLfloat vertices[] =
// X Y U V
{ -1.0, -1.0, 0.0, 0.0,
1.0, -1.0, 1.0, 0.0,
1.0, 1.0, 1.0, 1.0,
-1.0, 1.0, 0.0, 1.0};
//glPointSize(40.0f);
//create and bound vertex buffer object(memory buffers)
GLuint vbo1 = createVbo();
//The state set by glVertexAttribPointer() is stored in the currently bound vertex array object (VAO) if vertex array object bound
//associates the format of the data for the currently bound buffer object to the vertex attribute so opengl knows how much and how to read it
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), 0);
glEnableVertexAttribArray(0);
//shader vertex attribute for texture coordinates
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (const GLvoid*)(2 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
//upload vertices to buffer memory
//will upload data to currently bound/active buffer object
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//load and create texture object from image data
GLuint tex1 = createTexture(object[0]);
glDrawArrays(GL_QUADS, 0, 4);
glXSwapBuffers ( dpy, glxWin );
sleep(3);
GLuint tex2 = createTexture(object[1]);
glDrawArrays(GL_QUADS, 0, 4);
glXSwapBuffers ( dpy, glxWin );
sleep(3);
glBindTexture(GL_TEXTURE_2D, tex1);
glDrawArrays(GL_QUADS, 0, 4);
glXSwapBuffers ( dpy, glxWin );
sleep(3);
//////////////de-initialize
glXMakeContextCurrent( dpy, 0, 0, NULL );
glXDestroyContext( dpy, context );
glXDestroyWindow(dpy, glxWin);
XDestroyWindow( dpy, win );
XCloseDisplay( dpy );
return 0;
}
Run Code Online (Sandbox Code Playgroud)
和着色器
const char* vertex_shader =
"#version 400\n"
"layout(location = 0) in vec2 vp;"
"layout(location = 1) in vec2 tex;"
"out vec2 texCoord;"
"void main () {"
" gl_Position = vec4 (vp, 0.0f, 1.0f);"
" texCoord = tex; "
"}";
const char* fragment_shader =
"#version 400\n"
"uniform sampler2D s;"
"in vec2 texCoord;"
"out vec4 color;"
"void main () {"
"color = texture(s, texCoord);"
"}";
Run Code Online (Sandbox Code Playgroud)
为了避免任何混淆,这里是 init() 过程
static int att[] =
{
GLX_X_RENDERABLE , True,
GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT,
GLX_RENDER_TYPE , GLX_RGBA_BIT,
GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR,
GLX_RED_SIZE , 8,
GLX_GREEN_SIZE , 8,
GLX_BLUE_SIZE , 8,
GLX_ALPHA_SIZE , 8,
GLX_DEPTH_SIZE , 24,
GLX_STENCIL_SIZE , 8,
GLX_DOUBLEBUFFER , True,
//GLX_SAMPLE_BUFFERS , 1,
//GLX_SAMPLES , 4,
None
};
Display *dpy;
Window root;
XVisualInfo *vi;
Colormap cmap;
XSetWindowAttributes swa;
Window win;
GLXContext context;
GLXFBConfig *fbc;
GLXWindow glxWin;
int fbcount;
void init(int width, int height){
//set and choose displays for creating window
dpy = XOpenDisplay(NULL);
if (!dpy){
printf("Failed to open X display\n");
exit(1);
}
root = DefaultRootWindow(dpy);
//request a framebuffer configuration
fbc = glXChooseFBConfig(dpy, DefaultScreen(dpy), att, &fbcount);
if (!fbc){
printf( "Failed to retrieve a framebuffer config\n" );
exit(1);
}
vi = glXGetVisualFromFBConfig( dpy, fbc[0] );
if(vi==NULL){
printf("Error getting visual info\n");
exit(1);
}
swa.colormap = XCreateColormap( dpy, RootWindow( dpy, vi->screen ), vi->visual, AllocNone );
swa.background_pixmap = None ;
swa.border_pixel = 0;
swa.event_mask = StructureNotifyMask;
//Window XCreateWindow(display, parent, x, y, width, height, border_width, depth, class, visual, valuemask, attributes)
win = XCreateWindow( dpy, RootWindow( dpy, vi->screen ), 0, 0, width, height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel|CWColormap|CWEventMask, &swa );
if ( !win ){
printf( "Failed to create window.\n" );
exit(1);
}
context = glXCreateNewContext( dpy, fbc[0], GLX_RGBA_TYPE, NULL, True );
glxWin = glXCreateWindow(dpy, fbc[0], win, NULL);
XMapWindow(dpy, win);
glXMakeContextCurrent(dpy, glxWin, glxWin, context);
// start GLEW extension handler
glewExperimental = GL_TRUE;
GLuint err = glewInit();
if(err!=GLEW_OK){
fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
exit(1);
}
XSelectInput(dpy, win, ButtonPressMask|KeyPressMask);
// tell GL to only draw onto a pixel if the shape is closer to the viewer
//glEnable (GL_DEPTH_TEST); // enable depth-testing
//glDepthFunc (GL_LESS); // depth-testing interprets a smaller value as "closer"
}
Run Code Online (Sandbox Code Playgroud)
如果您使用兼容性 OpenGL 上下文,则不需要 VAO。从某种意义上说,存在一个始终绑定的“默认”VAO。这就是它在 OpenGL 2.x 中的工作方式,也是“兼容性配置文件”中“兼容性”含义的一部分。
在使用核心 OpenGL 上下文时,您确实需要 VAO。如果不这样做,您的代码根本无法工作。如果您想继续假装不需要 VAO,您可以创建一个 VAO 并将其在整个程序持续时间内绑定。
选择核心配置文件与兼容性配置文件的问题有其细微差别,但一般来说,如果您正在开发新程序,建议请求核心配置文件。无论如何,并非所有系统都对兼容性配置文件有很好的支持。Mesa 将兼容性配置文件限制为 3.0,OS X 将兼容性配置文件限制为 2.1。如果您想要核心配置文件,则必须在创建上下文时显式请求核心配置文件。