使用带有移动对象的 OpenGL 实例化绘图

Posted

技术标签:

【中文标题】使用带有移动对象的 OpenGL 实例化绘图【英文标题】:Using OpenGL Instanced Drawing With Moving Objects 【发布时间】:2017-05-13 19:15:43 【问题描述】:

对于不熟悉的人来说,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);

【问题讨论】:

"如何在 OpenGL 3 中为顶点不断变化的对象实现实例化渲染?" 我真的不明白你的意思。并且显示一个头文件并不完全有启发性。 已编辑以解释实例化渲染。至于头文件,我想展示我每帧尝试绘制数百次的对象,以及各种状态数据;位置每帧都会发生变化,并且每个对象之间的速度可能会有所不同。 我知道什么是实例化渲染。我不明白的是关于“不断变化的顶点”的部分,或者这与实例化有什么关系。如果您更改一个实例的顶点数据,那么您将更改 所有 个实例。这就是实例化的用途:使用相同的逐顶点数据来渲染具有不同逐实例数据的多个对象(通常用于定义实例的位置,但它也会影响其他事物,例如颜色)。请描述您想要实现的目标,而不是您用来实现目标的手段。 我可能把那部分弄不清楚了。当我谈论改变顶点时,我指的是改变游戏世界中对象的位置,在正交投影中的笛卡尔坐标。有没有办法通过实例化渲染对不断变化的对象向量中的许多对象执行此操作? 【参考方案1】:

您寻找的概念是属性除数。见glVertexAttribDivisor

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

【讨论】:

谢谢!我会调查的。【参考方案2】:

这是我认为您正在寻找的代码示例。我为我的粒子系统使用了实例渲染,它支持纹理、颜色和运动。适用于 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;
    

【讨论】:

以上是关于使用带有移动对象的 OpenGL 实例化绘图的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL实例化数组绘图

如何在 Qt 5 中使用带有实例化的 VAO

在 OpenGL 中实例化数百万个对象

在 OpenGL 中实例化数百万个对象:提高每秒帧数

OpenGL实例化数组奇怪的顶点位置

在 GPU 上实例化更快吗?