Multiple draw calls with single vertex/index buffer? - opengl-es

I want to imlement simple bullet trail (in OpenGL-ES 2-3) system that will allow use different textures or materials for different trails, so it means, that that trails meant to be rendered in different draw calls and each vertex can be modified right before rendering.
Actually, I don't know, how much draw calls will be done in each update, and how much vertices will be passed to this draw call, so I trying to use single vertex buffer and single index buffer for all trails, and fill vertex buffer regions with different trails data every frame. Index buffer filled with simple (0, 1, 2, 3, 3, 4, 4, 5, 6....) values once and won't change anymore.
Could you advice some best practices, how to do this? Can I make draw calls with different render states and different vertex regions for each batch? What indices regions should I use for every draw call? Must index offset take in account vertex offset or maybe indices are applied to vertex region instead of whole buffer, so I can set index buffer offset to 0 for every draw call? Or, maybe, I make it totally wrong, and should do something else?
Thanks!

Ok, so how I made it work:
I still use single big buffer for all batches
For every batch I map just part of buffer (new part for every batch) and change data in this part.
Indices must take in account offset of this part. So, when you render part of buffer with 4th, 5th, 6th, 7th vertices, you have to use part of index buffer with {4, 5, 6, 7} data.

Related

Why Directx11 doesn't support multiple index buffers in IASetIndexBuffer

You can set multiple vertex buffers with IASetVertexBuffers,
but there is no plural version of IASetIndexBuffer.
What is the point of creating (non-interleaved) multiple vertex buffer datas if you wont be able to refer them with individual index buffers
(assume i have a struct called vector3 with 3 floats x,y,z)
let say i have a model of human with 250.000 vertices and 1.000.000 triangles;
i will create a vertex buffer with size of 250.000 * sizeof(vector3)
for vertex LOCATIONS and also,
i will create another vertex buffer with size of 1.000.000 * 3 *
sizeof(vector3) for vertex NORMALS (and propably another for diffuse
texture)
i can set these vertex buffers like:
ID3D11Buffer* vbs[2] = { meshHandle->VertexBuffer_Position, meshHandle->VertexBuffer_Normal };
uint strides[] = { Vector3f_size, Vector3f_size };
uint offsets[] = { 0, 0 };
ImmediateContext->IASetVertexBuffers(0, 2, vbs, strides, offsets);
how can i set seperated index buffers for these vertex datas if IASetIndexBuffer only supports 1 index buffer
and also (i know there are techniques for decals like creating extra triangles from the original model but)
let say i want to render a small texture like a SCAR on this human model's face (let say forehead), and this scar will only spread through 4 triangles,
is it possible creating a uv buffer (with only 4 triangles) and creating 3 different index buffers for locations, normals and UVs for only 4 triangles but using the same original vertex buffers (same data from full human model). i dont want to create tonnes of uv data which will never be rendered beside characters forehead (and i dont want to re-use, re-create vertex position datas for this secondary texture layers (decals))
EDIT:
i realized i didnt properly ask a question, so my question is:
did i misunderstand non-interleaved model structure (is it being used
for some other reason instead having non-aligned vertex components)?
or am i approaching non-interleaved structure wrong (is there a way
defining multiple non-aligned vertex buffers and drawing them with
only one index buffer)?
The reason you can't have more than on Index buffer bound at a time is because you need to specify a Fixed number of primitives when you make a call to DrawIndexed.
So in your example, if you have one index buffer with 3000 primitives and another with 12000 primitives, the pipeline would have no idea how to match the first set to the second set.
If is generally normal that some vertex data (mostly position) eventually requires to be duplicated across your vertex buffer, since your buffers requires to be the same size.
Index buffer works as a "lookup table", so your data across vertex buffers need to be consistent.
Non interleaved model structure has many advantages:
First using separate buffers can lead to better performances if you need to draw the models several times and some draws do not require attributes.
For example, when you render a shadow map, you will need to access only positions, so in interleaved mode, you still need to bind a large data structure and access elements in a non contiguous way (Input Assembler does that). In case of non interleaved data, Position will be contiguous in memory so fetch will be much faster.
Also non interleaved allows to more easily do some processing on some attributes, it is common nowadays to perform some displacement or skinning in a compute shader, so in that case you can also easily create another Position+Normal buffer, perform your skinning on those and attach them in the pipeline once processed (And you can keep UV buffer intact).
If you want to draw non aligned Vertex buffers, you could use Structured Buffers instead (and use SV_VertexID and some custom lookup tables in your shader code).

Draw different length of LINE_LOOPs from buffer

I have many Polylgons. one contains different amount of points.
I'm drawing them using LINE_LOOP in 2d (orthographic).
Now i want to upload all of them to a single buffer.
The problem is when i'm drawing the buffer using glDrawArrays the last point of the first polygon is connected by line to the next point which is the first point of the second polygon, and so on.
I know that with glDrawElements i can send the indices and it is solving the issue. but for this, I need to send allot of data for drawing polygons with allot of points, and changing the LINE_LOOP to LINES.
Is there a way to draw only with the start and end indices of each polygon ?
For example
// My 2d polygons points are
polygons = [
0,0, 10,0, 10,5, 5,10, // polygon 1
20,20, 30,20, 30,30 // polygon 2
]
// First polygon is starting at 0, the second at index 8
// If there was function like this
draw(polygons, [0, 8]);
------ADDITION-----------------
We can do it in OpenGL by calling the glDrawMultiArray - Thanks for ratchet freak answer.
But in WebGL this function does not exists. Is there any alternative ?
you can use glMultiDrawArrays:
starts=[0,4,...]
counts=[4,3,...]
glMultiDrawArrays(GL_LINE_LOOP, starts, counts, starts.length);
otherwise with glDrawElements you can specify a primitive restart index
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(65535);
index = [0,1,2,3,65535,4,5,6,65535,...]
//bind and fill GL_ELEMENT_ARRAY_BUFFER​
glDrawElements(GL_LINE_LOOP, index.size, GL_UNSIGNED_INT, 0);
//will draw lines `0,1 1,2 2,3 3,0 4,5 5,6 6,4`
With WebGL, which AFAIK corresponds to ES 2.0 features, I don't think there's a way around making multiple draw calls. For your example, you would need two draw calls:
glDrawArrays(GL_LINE_LOOP, 0, 8);
glDrawArrays(GL_LINE_LOOP, 8, 6);
You could reduce it to a single draw call by using GL_LINES instead of GL_LINE_LOOP, but that means that your vertex array gets twice as large, because you need the start and end point of each line segment.
If you use an index buffer combined with GL_LINES, the increase is only 50%, as long as you don't have more than 64k vertices in a buffer. The vertex array itself remains at its original size, and you will need two GL_UNSIGNED_SHORT indices per vertex in addition. So that's 4 more bytes per vertex, on top of the 8 bytes (for 2 floats) you have for the vertex coordinates. This is probably your best option with the limited ES 2.0 feature set.

multiple VBOs --> IBO

I was just trying to learn how VBOs and IBOs work in WEBGL.
Here is my understanding:
IBOs help reduce the amount of info passed down to the GPU . So we have a VBO and then we create an IBO with its indices pointing to the VBO. I had a doubt as to how WEBGL knows the IBO <--> VBO mapping . In case of a single VBO/IBO , I thought as GL was a state machine it sees the last ARRAY_BUFFER it is bound to and then uses that buffer as the IBO target .Below is a case with multiple VBOs(position buffer and color buffer) as given below:
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexColorBuffer);
gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, cubeVertexColorBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
setMatrixUniforms();
gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
In the above code(which works - i took it from a tutorial), we have two VBOs and one IBO (cubeVertexIndexBuffer) , what i don't understand is how WEBGL knows the indices of the IBO point to the position buffer and not the color buffer(though the color buffer is the last bound ARRAY_BUFFER).
Please let me know as to what I am missing here....
The GL_ELEMENT_ARRAY_BUFFER binding has nothing to do with the GL_ARRAY_BUFFER binding, each binding is used by a different set of commands. This is why people should not learn OpenGL using multiple VBOs in the beginning, interleaved VBOs avoid this sort of confusion. I will attempt to clear up your confusion below.
The GL_ARRAY_BUFFER binding is used for commands like glVertexAttribPointer (...). The GL_ELEMENT_ARRAY_BUFFER binding, on the other hand, by glDrawElements (...).
The indices in the IBO point to neither of the element arrays per-se. What they point to
are the glVertexAttribPointer (...)s you setup while you had a GL_ARRAY_BUFFER bound.
In case it was not already obvious, you cannot have more than one buffer object of the same type bound at any given time. When you set your attrib pointers and you are not using interleaved arrays you have to change the bound VBO. Thus, glDrawElements (...) could not care less which VBO is bound, it only cares about the vertex attrib pointers that you setup and the bound element array. Those pointers are relative to whatever VBO was bound when you set them up, but after the pointer is setup the state of the binding is no longer relevant.
The statefulness in OpenGL is awful. If you want to work with any of the buffers, you almost always need two calls: One to tell OpenGL which buffer you want to use and a second (and third and more) that operate on the buffer. The ambiguous buffer names make the whole thing more confusing. So, let us quickly clear up some things:
As explained here, binding to gl.ARRAY_BUFFER (or GL_ARRAY_BUFFER) indicates that the specified buffer is a VBO.
gl.ELEMENT_ARRAY_BUFFER (or GL_ELEMENT_ARRAY_BUFFER) indicates an IBO (that link is worth checking out). An IBO represents one complete surface. It is a list of indices to all vertices that this surface is made of. The IBO needs to be bound to gl.ELEMENT_ARRAY_BUFFER before a draw call because that's how the pipeline knows which vertices are where.
Since you sometimes need multiple attributes per vertex that you might (for some reason) want to store in separate buffers, you can use a single IBO to index into multiple VBOs.
Finally, (and I think, here is your confusion), the vertexAttribPointer tells the shader to look up the given attribute of given type in the given relative offset in the VBO that is bound when vertexAttribPointer is called. This way, the pipeline can get each attribute once it has the right vertex index, which it reads from the IBO. In your case, vertexAttribPointer says that the vertexPositionAttribute comes from the cubeVertexPositionBuffer, and color from your color buffer. In order to avoid calling vertexAttribPointer before every draw call, you can instead use a VAO to cache that information.

How to implement batches using webgl?

I am working on a small game using webgl. Within this game I have some kind of forest which consists out of many (100+) tree objects. Because I only have a few different tree models, I rotate and scale these models in a different way before I display them.
At the moment I loop over all trees to display them:
for (var tree in trees) {
tree.display();
}
While the display() method of tree looks like:
display : function() { // tree
this.treeModel.setRotation(this.rotation);
this.treeModel.setScale(this.scale);
this.treeModel.setPosition(this.position);
this.treeModel.display();
}
Many tree objects share the same treeModel object, so I have to set rotation/scale/position of the model everytime before I display it. The rotation/scale/position values are different for every tree.
The display method of treeModel does all the gl stuff:
display : function() { // treeModel
// bind texture
// set uniforms for projection/modelview matrix based on rotation/scale/position
// bind buffers
// drawArrays
}
All tree models use the same shader but can use different textures.
Because a single tree model consists only out of a few triangles I want to combine all trees into one VBO and display the whole forest with one drawArrays() call.
Some assumptions to make talking about numbers easier:
There are 250 trees to display
There are 5 different tree models
Every tree model has 50 triangles
Questions I have:
At the moment I have 5 buffers that are 50 * 3 * 8 (position + normal + texCoord) * floatSize bytes large. When i want to display all trees with one vbo i would have a buffer with 250 * 50 * 3 * 8 * floatSize byte size. I think I can't use an index buffer because I have different position values for every tree (computed out of the position value of the tree model and the tree position/scale/rotation). Is this correct or is there still a way I can use index buffers to reduce the buffer size at least a bit? Maybe there are other ways to optimize this?
How to handle different textures of the tree models? I can bind all textures to different texture units but how can I decide within the shader which texture should be used for the fragment that is currently displayed?
When I want to add a new tree (or any other kind of object) to this buffer at runtime: Do I have to create a new buffer and copy the content? I think new values can't be added by using glMapBuffer. Is this correct?
Index element buffers can only reach over attributes that are equal to or below 65535 in length, so you need to use drawArrays instead. It's usually not a big loss.
You can add trees to the end of the buffers using GL.bufferSubData.
If your textures are in reasonable sized (like 128x128 or 256x256), you can probably merge them into one big texture and handle the whole thing with the UV-coords. If not, you can add another attribute saying what texture the vertex belongs to and have a condition in the vertex shader, alternatively an array of sampler2Ds (not sure it works, never tried it). Remember that conditions in shaders are pretty slow.
If you decide to stick to your current solution, make sure to sort the trees so the once using the same textures are rendered after each other - keeping state switching down is essential, always.
A few thoughts:
Once you plant a tree in your world, do you ever modify it? Will it animate at all? Or is it just static geometry? If it's truly static, you could always build a single buffer with several copies of each tree. As you append trees, first apply (in Javascript) that instance's world transform to the vertices. If using triangle strips, you can link trees together using degenerate polygons.
You could roll your own pseudo-instanced drawing:
Encode an instance ID in the array buffer. Just set this to the same value for all vertices that are part of the same tree instance. I seem to recall that you can't have non-floaty vertex attributes in ES GLSL (maybe that's a Chrome limitation), so you will need to bring it in as a float but use it as an int. Since it's coming in as a float, you will have to deal with the fact that it's interpolated across your triangle, and so the value will have minor fluctuations - but simply rounding to the nearest integer fixes that right up.
Use a separate texture (which I will call the data texture) to encode all the per-instance information. In your vertex shader, look at the instance ID of the current vertex and use that to compute a texture coordinate in the data texture. Pull out whatever you need to transform the current vertex, and apply it. I think this is called a "dependent texture read", which is generally frowned upon because it can cause performance issues, but it might help you batch your geometry, which can help solve performance issues. If you're interested, you'll have to try it and see what happens.
Hope for an extension to support real instanced drawing.
Your current approach isn't so bad. I'd say: Stick with it until you hit some wall.
50 triangles is already a reasonable batch size for a single drawElements/drawArrays call. It's not optimal, but also not so bad. So for every tree change the paramters like location, texture and maybe shape through uniforms. Then do a draw call for each tree. Also a total of 250 drawElements calls isn't so bad either.
So I'd use one single VBO that contains all the used tree geometry variants. I'd actually split up the trees into building blocks, so that I could recombine them for added variety. And for each tree set appropriate offsets into the VBO before calling drawArrays or drawElements.
Also don't forget that you can do a very cheap field of view culling of each tree.

Performance of using a large number of small VBOs

I wanted to know whether OpenGL VBOs are meant to be used only for large geometry arrays, or whether it makes sense to use them even for small arrays. I have code that positions a variety of geometry relative to other geometry, but some of the "leaf" geometry objects are quite small, 10x10 quad spheres and the like (200 triangles apiece). I may have a very large number of these small leaf objects. I want to be able to have a different transform matrix for each of these small leaf objects. It seems I have 2 options:
Use a separate VBO for each leaf object. I may end up with a large number of VBOs.
I could store data for multiple objects in one VBO, and apply the appropriate transforms myself when changing the data for a given leaf object. This seems strange because part of the point of OpenGL is to efficiently do large numbers of matrix ops in hardware. I would be doing in software something OpenGL is designed to do very well in hardware.
Is having a large number of VBOs inefficient, or should I just go ahead and choose option 1? Are there any better ways to deal with the situation of having lots of small objects? Should I instead be using vertex arrays or anything like that?
The most optimal solution if your data is static and consists of one object would be to use display lists and one VBO for only one mesh. Otherwise the general rule is that you want to avoid doing anything other than rendering in the main draw loop.
If you'll never need to add or remove an object after initialization (or morph any object) then it's probably more efficient to bind a single buffer permanently and change the stride/offset values to render different objects.
If you've only got a base set of geometry that will remain static, use a single VBO for that and separate VBOs for the geometry that can be added/removed/morphed.
If you can drop objects in or remove objects at will, each object should have it's own VBO to make memory management much simpler.
some good info is located at: http://www.opengl.org/wiki/Vertex_Specification_Best_Practices
I think that 200 triangles per one mesh is not so small number and maybe the performance with VBO for each of that meshes will not decrease so much. Unfortunately it is depended on hardware spec.
One huge buffer will no gain huge performance difference... I think that the best option would be to store several (but not all) objects per one VBO.
renderin using one buffer:
there is no problem with that... you simply have one buffer that is bound and then you can use glDrawArrays with different parameters. For instance if one mesh consists of 100 verts, and in th buffer you have 10 of those meshes you can use
glDrawArrays(triangles, 0, 100);
glDrawArrays(triangles, 100, 100);
glDrawArrays(triangles, ..., 100);
glDrawArrays(triangles, 900, 100);
in that way you minimize changing buffers and still you are able to render it quite efficient.
Are those "small" objects the same? do they have the same geometry but different transformations/materials? Because maybe it is worth using "instancing"?

Resources