[…]there is something different about indices when it comes to binding them and using them in a buffer.
First of all, you don’t bind indices, you bind buffers (ok, and textures, and images, ah screw it). Second, you don’t use indices in a buffer. Basically you don’t use indices at all - the GPU does. You specify and store indices and the GPU uses indices to construct geometric primitives.
My question is, what is the difference between creating a buffer object and binding it to GL_ELEMENT_ARRAY_BUFFER target for indices as opposed to specifying the index data in glDrawElements?
It’s a case where the wording makes it unnecessarily confusing. Due to historical reasons, the 4th parameter to the function is void pointer to const since with client-side vertex arrays (and no buffer objects) you really passed in a pointer to the first element of a client-side index buffer - i.e. a pointer to some index array stored in system memory, not video memory as in the case of a buffer object. This is legacy GL!
Nowadays, with buffer objects, the 4th parameter is interpreted as an offset into the buffer bound to GL_ELEMENT_ARRAY_BUFFER.
What is the better approach?
I won’t give you advice to use legacy GL, i.e. clent-side vertex arrays. However, as a conversation with Dark Photon recently showed, there are cases where client-side arrays seem to perform better that vertex buffer objects. Still, in general the advice is to use GL 3.1+ style and functions.
Is this binding stored in a VAO? Or do I have to bind the appropriate buffer for what I want to draw before I call glDrawElements?
Yes, just like the buffer binding for GL_ARRAY_BUFFER. That’s one of the main reasons for VAOs, to nodd having to bind stuff everytime you use it - except for the VAO and binding the VAO is only needed if you actually change the binding somewhere else.