Either you need to draw portions (see the second and third parameter of glDrawArrays) of your vertex buffer where after each part you can perform a state switch (such as adjusting the bound textures, adjusting the shader), or you have to program your shader so it can render without ‘state switches’. The extend that this is possible entirely depends on the type of state switches and current possibilities of the ‘programmable pipeline’.
For example, if the only state switch you currently have, are textures. You could put the textures in a texture array (or an array of samplers); in your objects geometry you need to add an attribute that determines which ‘texture layer’ is used for sampling. Since the attribute has to be stored in your vertex buffer, it also becomes a complex issues of how much space it consumes to make it batchable versus the net performance gain.
Original:
sampler2d texture;
varying vec2 uv;
void main()
{
vec4 color = texture2D(texture, uv);
}
Array of samplers:
sampler2d texture[32];
varying vec2 uv;
varying float layer;
void main()
{
vec4 color = texture2D(texture[layer], uv);
}
Texture Array version:
sampler2dArray texture;
varying vec2 uv;
varying float layer;
void main()
{
vec4 color = texture3D(texture, vec3(uv, layer));
}
Please note that the code above is just rough pseudocode.