将 OpenGL 实例化图形与移动对象一起使用



对于不熟悉的每个人来说,OpenGL 实例化绘制是使用一个着色器调用绘制许多对象的地方,因此 glDrawArrays 只对屏幕上的一千个对象调用一次,而不是为每个对象调用一次。

现在的问题是:如何在 OpenGL 3 中为顶点不断变化的对象实现实例化渲染?在顶点着色器上创建一个数组或指定一个专门用于对象所在位置的位置是行不通的,因为我正在处理一个不断变化的对象向量,这些对象每帧以不同的速度移动坐标。

下面介绍了我正在使用的对象类的标头以及我拥有的顶点着色器以供参考。

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

你要找的概念是属性除数。请参阅glVertexAttribDivisor

简而言之:将模型矩阵从uniform更改为从缓冲区读取的实例化属性。使用每个实例的新位置更新该缓冲区的每一帧。实现这一点时要考虑的一件事是对模型矩阵使用(vec3 偏移量,quat4 方向(表示,以便将消耗的属性数量减少一半。此外,根据您手头的确切问题,您可以使用计算着色器直接在 GPU 上更新该缓冲区。

这是我认为您正在寻找的代码示例。我为我的粒子系统使用了实例化渲染,它支持纹理、颜色和运动。适用于android opengl es和windows opengl。 这段代码需要一些工作才能运行,但它应该相当容易上手。

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

下面是着色器:

顶点着色器:

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

片段着色器:

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

最新更新