Search code examples
openglglslopentk

Different texture co-ordinates per object when instancing


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.

enter image description here

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);
}

Solution

  • If you really are using instancing to draw multiple quads, then clearly you must have:

    1. A buffer object containing the positions of a single quad.
    2. 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.