2
votes

For everyone unfamiliar, OpenGL instanced drawing is where many objects are drawn with one shader call, so glDrawArrays is only called once for a thousand objects on the screen instead of once for every object.

Now the question is: how do I implement instanced rendering in OpenGL 3 for objects which have constantly changing vertices? Creating an array or specifying a position on the vertex shader dedicated specifically to where the objects are won't work, as I'm dealing with a constantly changing vector of objects which shift coordinates in different velocities every frame.

The header for the object class I'm working with, and the vertex shader I have, are described below for reference.

//CLASS
class Laser {

public:

    GLfloat x, y, xVelocity, yVelocity;
    GLuint texture;
    GLfloat angle;
    GLfloat velocity;
    GLfloat width, height;
    GLfloat drawWidth = 16;
    GLfloat drawHeight = 16;
    GLfloat damage;
    GLint actsToDissapear = -1;
    GLint actsExisting = 0;
    GLboolean expired = false;
    GLboolean isRotated = false;
    GLboolean variableColor = false;
    glm::vec3 color;
    std::string type = "Laser";

    Laser(GLfloat damage, GLfloat width, GLfloat height, GLuint texture, GLfloat x, GLfloat y, GLfloat xVelocity, GLfloat yVelocity, GLfloat drawWidth, GLfloat drawHeight, GLfloat actsToDissapear, GLboolean isRotated, GLfloat angle, GLboolean variableColor, glm::vec3 color);
    virtual void draw(SpriteRenderer* s);
    virtual void move(Rachel* player);
};

//VERTEX SHADER
#version 330 core
layout (location = 0) in vec4 vertex;

uniform mat4 model;
uniform mat4 projection;
out vec2 TexCoords;

void main() {
    TexCoords = vec2(vertex.z, vertex.w);
    gl_Position = projection * model * vec4(vertex.xy, 0.0, 1.0);
}
2
"How do I implement instanced rendering in OpenGL 3 for objects which have constantly changing vertices?" I don't really know what you mean by that. And showing a header file is not exactly illuminating.Nicol Bolas
Edited to explain instanced rendering. As for the header file, I wanted to show the object that I'm trying to draw hundreds of times every frame, with the various state data; the position changes every frame and the velocities can vary between each object.Fergus Mo
I am aware of what instanced rendering is. What I don't understand is the part about "constantly changing vertices" or how that exactly relates to instancing. If you change the vertex data for one instance, then you change it for all of them. That's what instancing is for: using the same per-vertex data to render multiple objects with different per-instance data (usually used to define the position of the instance, but it can also affect other things like colors). Please describe what you want to achieve, not the means you use to achieve it.Nicol Bolas
I might've made that part unclear. When I'm talking about changing the vertices, I'm talking about changing the position of the object in the game world, in terms of Cartesian coordinates in an orthographic projection. Is there any way to do this for many objects in a constantly changing vector of objects through instanced rendering?Fergus Mo

2 Answers

1
votes

The concept you look for is attribute divisor. See glVertexAttribDivisor.

In a few words: you change your model matrix from uniform to an instanced attribute that's read from a buffer. Each frame you update that buffer with the new positions of each instance. One thing to consider when implementing this is to use (vec3 offset, quat4 orientation) representation for the model matrix in order to reduce the number of consumed attributes by half. Also, depending on the exact problem you have at hand, you can update that buffer directly on the GPU with compute shaders.

0
votes

Heres a code example of what I think you're looking for. I used instanced rendering for my particle system, it supports textures, colors and movement. Works both on android opengl es and windows opengl. This code requires some work to run, but it should be fairly easy to get going.

    #include "ParticleSystem.h"
    #include "Engine.h"
    #include "Transform.h"
    #include "Shader.h"
    #include "Texture.h"
    #include "Mesh.h"
    #include "ShaderHandler.h"

    ParticleSystem::ParticleSystem()
    {
    }

    ParticleSystem::~ParticleSystem()
    {
        shader = nullptr;
        texture = nullptr;
        glDeleteVertexArrays(1, &vertexArrayObject);
    }

    void ParticleSystem::init(Engine * engine, float size, Texture * texture, float maxVelocity, bool gravity)
    {
        this->maxVelocity = maxVelocity;
        this->gravity = gravity;

        this->size = size;
        vertex =
        {
            -size, -size, 0.0f,
            -size, size, 0.0f,
            size, size, 0.0f,
            size, -size, 0.0f
        };

        indices =
        {
            1, 0, 2, 3
        };

        this->shader = engine->getShaderHandler()->loadShader("res/shaders/texturedInstancedShader");
        this->texture = texture;

        glGenVertexArrays(1, &this->vertexArrayObject);
        glBindVertexArray(this->vertexArrayObject);

        glGenBuffers(ParticleSystem::NUM_BUFFERS, this->vertexArrayBuffer);

        glBindBuffer(GL_ARRAY_BUFFER, this->vertexArrayBuffer[this->VERTEX_VB]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * this->vertex.size(), &this->vertex[0], GL_STATIC_DRAW); //send model to GPU

        glBindBuffer(GL_ARRAY_BUFFER, this->vertexArrayBuffer[this->TEXTURE_VB]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec2) * this->texCoords.size(), &this->texCoords[0], GL_STATIC_DRAW);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->vertexArrayBuffer[this->INDEX_VB]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * indices.size(), &this->indices[0], GL_STATIC_DRAW);

        glBindBuffer(GL_ARRAY_BUFFER, this->vertexArrayBuffer[this->POSITION_VB]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * this->positions.size(), NULL, GL_STREAM_DRAW);    //NULL (empty) buffer

        glBindBuffer(GL_ARRAY_BUFFER, this->vertexArrayBuffer[this->COLOR_VB]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec4) * this->colors.size(), NULL, GL_STREAM_DRAW);   //NULL (empty) buffer

        glBindVertexArray(0);
    }


    void ParticleSystem::createPoint(float pps, float deltaTime, glm::vec3 position, float maxLife, glm::vec4 color, glm::vec3 velocity)
    {
        Particle particle;
        float amountPerSecond = pps * deltaTime;
        for (float i = 0; i < amountPerSecond; i++)
        {
            particle.life = (rand() % static_cast<int>(maxLife * 100)) / 100.f;
            particle.velocity = 
            {
                ((rand() % 200 / 100.f) - 1.f) * velocity.x,
                ((rand() % 200 / 100.f) - 1.f) * velocity.y,
                ((rand() % 200 / 100.f) - 1.f) * velocity.z
            };
            particles.emplace_back(particle);
            positions.emplace_back(position);
            colors.emplace_back(color);
        }
    }

    void ParticleSystem::draw(glm::mat4 view)
    {
        if (particles.size() > 0)
        {
            Transform transform;
            this->shader->bind();
            this->shader->loadTransform(transform, view);
            this->shader->loadInt(U_TEXTURE0, 0);
            this->texture->bind(0);

            glBindVertexArray(vertexArrayObject);

            glVertexAttribDivisor(0, 0);
            glVertexAttribDivisor(1, 1);
            glVertexAttribDivisor(2, 1);
            glVertexAttribDivisor(3, 0);

            glEnableVertexAttribArray(0);
            glBindBuffer(GL_ARRAY_BUFFER, this->vertexArrayBuffer[this->VERTEX_VB]);
            glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);

            glEnableVertexAttribArray(1);
            glBindBuffer(GL_ARRAY_BUFFER, this->vertexArrayBuffer[this->POSITION_VB]);
            glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * positions.size(), &positions[0], GL_STREAM_DRAW);
            glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);

            glEnableVertexAttribArray(2);
            glBindBuffer(GL_ARRAY_BUFFER, this->vertexArrayBuffer[this->COLOR_VB]);
            glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec4) * colors.size(), &colors[0], GL_STREAM_DRAW);
            glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 0, (void*)0);

            glEnableVertexAttribArray(3);
            glBindBuffer(GL_ARRAY_BUFFER, this->vertexArrayBuffer[this->TEXTURE_VB]);
            glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);

            glDrawElementsInstanced(GL_TRIANGLE_STRIP, indices.size(), GL_UNSIGNED_INT, 0, positions.size());

            glDisableVertexAttribArray(0);
            glDisableVertexAttribArray(1);
            glDisableVertexAttribArray(2);

            glBindVertexArray(0);
        }
    }

    void ParticleSystem::update(float deltaTime)
    {
        for (std::size_t i = 0; i < particles.size(); i++)
        {
            particles[i].life -= (1.f * deltaTime); //decrease life with 1 per second
            if (particles[i].life <= 0.f)   //dead
            {
                particles.erase(particles.begin() + i);
                colors.erase(colors.begin() + i);
                positions.erase(positions.begin() + i);
                continue;
            }

            if (this->gravity == true)
            {
                if (particles[i].velocity.y > -maxVelocity)
                {
                    particles[i].velocity.y -= maxVelocity * deltaTime; //1 second to reach maximum velocity
                }
                else
                {
                    particles[i].velocity.y = -maxVelocity;
                }
            }
            positions[i] += (particles[i].velocity * deltaTime);
        }
    }

Heres the shader:

vertex shader:

    #version 330 core

    layout(location = 0) in vec3 vertex;
    layout(location = 1) in vec3 positions;
    layout(location = 2) in vec4 colors;
    layout(location = 3) in vec2 texCoords;

    out vec2 texCoord;
    out vec4 color;

    uniform mat4 transform;

    void main()
    {
        color = colors;
        texCoord = texCoords;
        gl_Position = transform * vec4(vertex + positions, 1.0);
    }

fragment shader:

    #version 330 core

    in vec4 color;
    in vec2 texCoord;

    out vec4 colors;

    uniform sampler2D texture0;

    void main()
    {
        vec4 texel = texture2D(texture0, texCoord);
        if (texel.a <= 0.5)
        {
            discard;
        }
        colors = color * texel;
    }