Here is a simple but effective explanation , basically a buffer object has information which can be interpreted as just simply bits for the vertices in themof raw data, which on its own mean nothing, so it is PURELY the data which can be looked at any way really
i.e float vbo[]+vbo[]={1.0,2.0,34.0...}
however for this system of API asand the way OpenGL , it gives much lower level controlwas designed to the implementer/programmer inwork is that you can tellmust DEFINE what the shader to whichdata that your passing the data ofto the VBO howvarious shaders is going to interpret that datalook like to the shaders
in that you also have to define how it will read that data, what format it is in, and what to do with it and how it will be used and for what,all of this information is stored in the VAO
for example you can passdeclare data that is stored in an array like this float vbo = {11.0,2.0,3.0,4.0}
for example and one of its "attributes"what is required next at this point is how to interpret that informationdata from the vertex array object hasVBO in the VAO, and what that means is as follows
the VAO can be set to read 2 floats per vertex (which would make it an2 vectors with two dimensions x,y vector) or you can tell the vao to interpret it as 1 vector with 4 dimensions i.e x,y,z,w etc as well as
but also other thingsattributes of that data is defined and stored in the VAO such as data format(despite that you storeddeclared an array of float you can tell the shader to read it in as aan integer, with such attributes of how to interpret thatcourse the system converting the raw data all stored in the VAOprocess from float to integer and has its own set of rules what to do in such circumstances)
So basically the VBO is the data, and the VAO stores how to interpret that data, because the shaders and OpenGL server is designed to be very nosey and needs to know everything before it decides how to process it and what to do with it and where to put it
of course its not actually nosey, its actually looking to be most efficient because it needs to store that data on the graphics server memory so that it gets the most efficient and quickest processing(unless it decides that it doesn't need to do this if the data is not to be processed in such a way and used for some other information that isnt accessed often) , and hence why the details of what to do with the data and how to process it is required to be stored in the VAO , so the VAO is like a header and the VBO is like the pure raw data that the header uses and defines(in this case passes to the shader vertex attributes) with the exception that the VBO is not limited only to be used by one VAO, it can be used and reused and bound to many VAO's for example:
also for example what you can do is you can bind one buffer object to VAO1 and also(separately) bind the same buffer object to VAO2 with each interpreting it differently so that if your shader where to process the data, depending on which VAO is the one that siis bound it would procesprocess the same raw data differently to the frambuffer(drawing pixels to window) resulting in different display of the same data which would be based upon how you defined its use in the VAO