Different texture co-ordinates per object when instancing - opengl

I'm using instancing to draw the same quad multiple times, for the floor in a game engine. Each floor has different texture co-ordinates depending on it's size and my problem is that all instances are using the texture co-ordinates of the first instance.
This is how I am buffering the data.
public static void UploadTextureCooridnates()
{
// Construct texture co-ordinate array
List<Vector2> textureCoords = new List<Vector2>();
foreach (Floor floor in Floor.collection)
{
float xCoordLeft = 0;
float yCoordBottom = 0;
float yCoordTop = floor.scale.X;
float xCoordRight = floor.scale.Z;
textureCoords.Add(new Vector2(xCoordLeft, yCoordBottom));
textureCoords.Add(new Vector2(xCoordRight, yCoordBottom));
textureCoords.Add(new Vector2(xCoordRight, yCoordTop));
textureCoords.Add(new Vector2(xCoordLeft, yCoordBottom));
textureCoords.Add(new Vector2(xCoordRight, yCoordTop));
textureCoords.Add(new Vector2(xCoordLeft, yCoordTop));
}
Vector2[] texCoords = textureCoords.ToArray();
// Buffer data
GL.BindBuffer(BufferTarget.ArrayBuffer, VBOtexcoordsInstanced);
GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(texCoords.Length * Vector2.SizeInBytes), texCoords, BufferUsageHint.StaticDraw);
GL.EnableVertexAttribArray(1);
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 0, 0);
GL.VertexAttribDivisor(1, 0);
}

If you really are using instancing to draw multiple quads, then clearly you must have:
A buffer object containing the positions of a single quad.
Some mechanism of getting per-instance data to offset those positions.
Your problem is your expectation. You're instancing with quads. That means your render call only uses 4-6 vertices. This will be just as true for your texture coordinates as your positions.
Your problem is that you're treating per-instance data as though it were per-vertex data. Texture coordinates change with each instance; therefore, they are per-instance data. But you don't have them use instancing, so they're treated as per-vertex data. So only the texture coordinates from the first 6 vertices will be used.
Of course, you can't really make them per-instance data either. Each vertex+instance pair has a separate texture coordinate value. And instancing doesn't provide a way to do that directly. Instead, you would have to use gl_VertexID and gl_InstanceID to fetch data directly from a buffer object; either an SSBO or a buffer texture.
And therefore, your problem really is this:
I'm using instancing to draw the same quad multiple times
Stop doing that. It's not worthwhile. Just put the positions in the CPU buffer data. Use proper buffer streaming techniques, and your performance will be reasonable.

Related

glDrawElements with per facet color

I'm using glDrawElements to draw an index mesh:
voi
d init()
{
//allocate vbo, copy actual data (m_indices,m_vertices).
//m_indices is a vector of mx3 indices of the facet.
//m_vertices is nx6 xyzrgb vertex points
m_vBuff.create();
m_iBuff.create();
m_vBuff.setUsagePattern(QOpenGLBuffer::StaticDraw);
m_iBuff.setUsagePattern(QOpenGLBuffer::StaticDraw);
m_vBuff.bind();
m_vBuff.allocate(m_vertices.data(), int(m_vertices.size() * sizeof(VertData)));
m_vBuff.release();
m_iBuff.bind();
m_iBuff.allocate(m_indices.data(), int(m_indices.size() * sizeof(uint32_t)));
m_iBuff.release();
}
and the usage:
...
m_vBuff.bind();
m_iBuff.bind();
//define attribute which can be used in the shader
int vp = shader->attributeLocation("a_xyz");
shader->enableAttributeArray(vp);
shader->setAttributeBuffer(vp, GL_FLOAT, 0, 3, sizeof(VertData));
int vc = shader->attributeLocation("a_rgb");
shader->enableAttributeArray(vc);
shader->setAttributeBuffer(vc, GL_FLOAT, 3 * sizeof(float), 3, sizeof(VertData));
glDrawElements(GL_TRIANGLES, m_iBuff.size() / sizeof(uint32_t), GL_UNSIGNED_INT, NULL);
m_vBuff.release();
m_iBuff.release();
the data in m_vBuff is organuzed s.t. I can have an attribute in the shader per vertex. How can I add color data which is per facet?
the data in m_vBuff is organuzed s.t. I can have an attribute in the shader per vertex. How can I add color data which is per facet?
There come three different strategies to mind:
Properly duplicate the vertex data.
Use of the flat interpolation qualifier and provoking vertex:
If you qualify your varyings as flat, the data will not be interpolated across the primitive, but the data from a single vertex is used for the whole primitive. The vertex from which the data is taked is called the provoking vertex, and you can choose between different conventions via glProvokingVertex(). If you have the vertices A,B,C,D and E and the triangles ABC, BCD and CDE, and forward the per-vertex color to the fragment shader as flat, and use for example use the GL_FIRST_VERTEX convention, then the first triangle will get A's color, the second triangle will use B's color and so on. This strategy will only work to a certain degree and not for arbitrary meshes, because the a single vertex could be connected to more than three triangles. This strategy still might be applied to reduce the amount of duplication which would occur in option 1.
Put the per-face data into some other storage and use the gl_PrimitiveID fragment shader input as an index into that storage. Depending on the amount of storage you need (and the GL version you're targeting), a Uniform Buffer Object, Texture Buffer Object or Shader Storage Buffer Object seems most appropriate.

How can I draw surface normals in OpenGL?

I have a vertex format which are all floats, and looks like this:
POSITION POSITION POSITION NORMAL NORMAL NORMAL TEXCOORD TEXCOORD
I was thinking I need to draw lines from the first three floats to the next three floats, then I need to skip the next two floats and continue on. Is there any way of doing this without creating another buffer for each object that's in the correct layout?
I know I can draw just one line per draw call, and just loop over, but that is many draw calls? How is the general way normals are drawn for stuff like debugging?
Also I've thought about indexing, but indexing only helps selecting specific vertices, in this case I want to draw between two attributes of my normal vertex layout.
This cannot be done just by setting appropriate glVertexAttribPointer, since you have to skip the texcoords. Additionally, you don't want to draw a line from position to normal, but from position to position + normal, since normals just describe a direction, not a point in space.
What you can do is to use a geometry shader. Basically, you set up two attributes, one for position, one for normal (as you would do for rendering the model) and issue a draw command with GL_POINTS primitive type. In the geometry shader you then generate a line from position to position + normal.
Normally to draw surface normals you would set up a separate buffer or a geometry shader to do the work. Setting a separate buffer for a mesh to draw just the normals is trivial and doesn't require a draw call for every normal, all of your surface normals would be drawn in a single drawcall
Since you'll be doing it for debugging purposes, there's no need to worry too much about performance and just stick with the quicker method that gets things on screen
The way I'd personally do it depends on whether the mesh has vertex or face normals, we could for instance fill a buffer with a line for each vertex in the mesh whose offset from the vertex itself represent the normal you need to debug with the following pseudocode
var normal_buffer = [];
//tweak to your liking
var normal_length = 10.0;
//this assumes your mesh has 2 arrays of the same length
//containing structs of vertices and normals
for(var i = 0; i < mesh.vertices.length; i++) {
//retrieving the normal associated with this vertex
var nx = mesh.normals[i].x;
var ny = mesh.normals[i].y;
var nz = mesh.normals[i].z;
//retrieving the vertex itself, it'll be the first point of our line
var v1x = mesh.vertices[i].x;
var v1y = mesh.vertices[i].y;
var v1z = mesh.vertices[i].z;
//second point of our line representing the normal direction
var v2x = v1x + nx * normal_length;
var v2y = v1y + ny * normal_length;
var v2z = v1z + nz * normal_length;
buffer.push(v1x, v1y, v1z, v2x, v2y, v2z);
}
You can later on proceed as normal and attach the buffer to a vertex buffer object and use whatever program you like to issue one single draw call that will draw all of your mesh normals
vertbuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertbuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(buffer), gl.STATIC_DRAW);
/* later on in your program */
gl.drawArrays(gl.LINES, 0, buffer.length / 3);
A cool feature of normal debugging is that you can use the normal itself in a fragment shader as an output color to quickly check if it points to the expected direction

Wrapping texture co-ordinates on a variable-size quad?

Here's my situation: I need to draw a rectangle on the screen for my game's Gui. I don't really care how big this rectangle is or might be, I want to be able to handle any situation. How I'm doing it right now is I store a single VAO that contains only a very basic quad, then I re-draw this quad using uniforms to modify the size and position of it on the screen each time.
The VAO contains 4 vec4 vertices:
0, 0, 0, 0;
1, 0, 1, 0;
0, 1, 0, 1;
1, 1, 1, 1;
And then I draw it as a GL_TRIANGLE_STRIP. The XY of each vertex is it's position, and the ZW is it's texture co-ordinates*. I pass in the rect for the gui element I'm currently drawing as a uniform vec4, which offsets the vertex positions in the vertex shader like so:
vertex.xy *= guiRect.zw;
vertex.xy += guiRect.xy;
And then I convert the vertex from screen pixel co-ordinates into OpenGL NDC co-ordinates:
gl_Position = vec4(((vertex.xy / screenSize) * 2) -1, 0, 1);
This changes the range from [0, screenWidth | screenHeight] to [-1, 1].
My problem comes in when I want to do texture wrapping. Simply passing vTexCoord = vertex.zw; is fine when I want to stretch a texture, but not for wrapping. Ideally, I want to modify the texture co-ordinates such that 1 pixel on the screen is equal to 1 texel in the gui texture. Texture co-ordinates going beyond [0, 1] is fine at this stage, and is in fact exactly what I'm looking for.
I plan to implement texture atlasses for my gui textures, but managing the offsets and bounds of the appropriate sub-texture will be handled in the fragment shader - as far as the vertex shader is concerned, our quad is using one solid texture with [0, 1] co-ordinates, and wrapping accordingly.
*Note: I'm aware that this particular vertex format isn't neccesarily useful for this particular case, I could be using vec2 vertices instead. For the sake of convenience I'm using the same vertex format for all of my 2D rendering, and other objects ie text actually do need those ZW components. I might change this in the future.
TL/DR: Given the size of the screen, the size of a texture, and the location/size of a quad, how do you calculate texture co-ordinates in a vertex shader such that pixels and texels have a 1:1 correspondence, with wrapping?
That is really very easy math: You just need to relate the two spaces in some way. And you already formulated a rule which allows you to do so: a window space pixel is to map to a texel.
Let's assume we have both vec2 screenSize and vec2 texSize which are the unnormalized dimensions in pixels/texels.
I'm not 100% sure what exactly you wan't to achieve. There is something missing: you actaully did not specify where the origin of the texture shall lie. Should it always be but to the bottom left corner of the quad? Or should it be just gloablly at the bottom left corner of the viewport? I'll assume the lattter here, but it should be easy to adjust this for the first case.
What we now need is a mapping between the [-1,1]^2 NDC in x and y to s and t. Let's first map it to [0,1]^2. If we have that, we can simply multiply the coords by screenSize/texSize to get the desired effect. So in the end, you get
vec2 texcoords = ((gl_Position.xy * 0.5) + 0.5) * screenSize/texSize;
You of course already have caclulated (gl_Position.xy * 0.5) + 0.5) * screenSize implicitely, so this could be changed to:
vec2 texcoords = vertex.xy / texSize;

While drawing in orthographic view, Is there any performance advantage of using glDrawElements

I am drawing some orthographic representations in bulk around one million in my model drawing.
(I will draw these things with some flag)
Camera is also implemented. rotation etc are possible.
All these orthograhic representations will change their positions when I rotate the model.
So that, it looks like, all these are in the same place on the model.
Now I would like to draw these orthographic things through graphics card, because, when these are huge in number, model rotation is very very slow.
I feel like there would not be any advantage, because, every time I have to recompute the postions based on the projection matrix.
1) Am I correct?
2) And also please let me know, how to improve performance when i am drawing bulk orthographic representations using opengl.
3) I also feel instancing will not work here, because for each orthographic rep is drawn between 2/3 positions. Am I correct ?
Usually, OpenGL does the projection calculation for you while drawing: The positions handed over to GL are world or model coordinates, and GL rendering uses the model-view-projection matrix (while rendering) to calculate the screen coordinates for the current projection etc. If the camera moves, the only thing that changes is the MVP matrix handed to GL.
This shouldn't really depend on the kind of projection you are using. So I don't think you need to / should update the positions in your array.
Here is my approach:
You create a vertex buffer that contains each vertex position 6 times and 6 texture coordinates (that you need anyways if you want to draw your representation with textures) from which you make a quad in the vertex shader. In that you would emulate the openGL projection and then offset the vertex by its texture coordinate to create the quad with constant size.
When constructing the model:
vector<vec3>* positionList = new vector<vec3>();
vector<vec2>* texCoordList = new vector<vec2>();
for (vector<vec3>::iterator it = originalPositions->begin(); it != originalPositions->end(); ++it) {
for (int i = 0; i < 6; i++) //each quad consists of 2 triangles, therefore 6 vertices
positionList->push_back(vec3(*it));
texCoordList->push_back(vec2(0, 0)); //corresponding texture coordinates
texCoordList->push_back(vec2(1, 0));
texCoordList->push_back(vec2(0, 1));
texCoordList->push_back(vec2(1, 0));
texCoordList->push_back(vec2(1, 1));
texCoordList->push_back(vec2(0, 1));
}
vertexCount = positionList->size();
glGenBuffers(1, &VBO_Positions); //Generate the buffer for the vertex positions
glBindBuffer(GL_ARRAY_BUFFER, VBO_Positions);
glBufferData(GL_ARRAY_BUFFER, positionList->size() * sizeof(vec3), positionList->data(), GL_STATIC_DRAW);
glGenBuffers(1, &VBO_texCoord); //Generate the buffer for texture coordinates, which we are also going to use as offset values
glBindBuffer(GL_ARRAY_BUFFER, VBO_texCoord);
glBufferData(GL_ARRAY_BUFFER, texCoordList->size() * sizeof(vec2), texCoordList->data(), GL_STATIC_DRAW);
Vertex Shader:
void main() {
fs_texCoord = vs_texCoord;
vec4 transformed = (transform * vec4(vs_position, 1));
transformed.xyz /= transformed.w; //This is how the openGL pipeline does projection
vec2 offset = (vs_texCoord * 2 - 1) * offsetScale;
//Map the texture coordinates from [0, 1] to [-offsetScale, offsetScale]
offset.x *= invAspectRatio;
gl_Position = vec4(transformed.xy + offset, 0, 1);
//We pass the new position to the pipeline with w = 1 so openGL keeps the position we calculated
}
}
Note that you need to adapt to the aspect ratio yourself, since there is no actual orthogonal matrix in this that would do this for you, which is this line:
offset.x *= invAspectRatio;

OpenGL color buffers per frame adding too much frame time

I have written a simple Particle class that stores positions, directions, velocities, colors etc for a particle demo. Each particle is a pyramid (4 triangles) and has a single color in all it's vertices.
Every frame I loop all the particles to find their new position etc and I need each one to render on screen with it's specific color. The way I know how to do this so far is, fill a color buffer for each of the particle's vertex colors, bind it to an attribute and send it to the shaders.
This is how I do it:
GLfloat g_color_buffer_data[3*3*4];
for (int j = 0; j < 3*4 ; j++)
{
g_color_buffer_data[j*3]=particles[i].color.r;
g_color_buffer_data[j*3+1]=particles[i].color.g;
g_color_buffer_data[j*3+2]=particles[i].color.b;
}
// 2nd attribute buffer : colors
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_color_buffer_data), g_color_buffer_data, GL_STATIC_DRAW);
glVertexAttribPointer(
1, // attribute. No particular reason for 1, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
This means I have to send a color for all 12 vertices of each particle, where I would ideally only send 1 RGB triplet to color the entire particle. The vertex shader gets a color for each vertex and sends it to the fragment shader.
Now this code inserted into my per-particle loop slows down my frames by 40 ms. Without it, I'm having a frame time of 6 ms which gets raised to 45 ms.
Questions begin:
Is there a way to send a single color per-primitive/particle to color the entire thing inside the shaders (possibly by modifying the data structure of the color buffer or something else)?
Is it normal for this code to be causing such a big performance hit?
Since I'm new to all of this, can you point to me which call is the one that has this massive execution time?
Maybe this happens because of using 4 samples per fragment in addition to having big color buffers sent for each particle to the shaders?
note: I am testing with 512 particles. I been changing the code around for hours, but at some point earlier I think I did have a frame time of 6 ms WITH 512 particles, and something like 50 ms with 2048. I was using the same color buffer and attribute binding code that I provided above.
Yes you could use a 3D vector(such as glm::vec3) and use the x,y,z components as the rgb values. If you use a glm::vec4 then you can also have alpha values.
You can then send this to your shader using something like in vec3 in_colours; where in_colours is the glm::vec3 in the code.
glm::vec3 in_colours= glm::vec3(r, g, b); // Will need to do in a loop or something for each particle, or add to a std::vector and send that to shader
glUniform3fv(glGetUniformLocation(shader_program, "in_colours"), 1, glm::value_ptr(in_colours));
Then in the shader you can access the x, y, z component individually if needed or as a whole.