c++ - 使用默认构造函数调用的网格类不工作 OpenGL C++

标签 c++ class opengl c++11 mesh

我为 OpenGL 3.3 创建了一个 Mesh 类,当我使用非默认构造函数创建类时,当我创建对象时创建顶点时,它工作正常。
但是,我现在想要有多个对象,我可以通过将它们放在一个 vector 中来动态创建它们,所以我必须添加一个默认构造函数我使用与其他构造函数相同的函数来设置缓冲区数据......但是它不起作用。据我所知,这不是因为它在 vector 中,而是因为它与构造函数有关,或者与稍后创建缓冲区数据有关。我真的不太确定。

这是我的类(class)。 (当我创建一个可以工作的网格时,我调用带有参数的构造函数,当它不起作用时,我构建一个没有参数的网格并调用“changeMes​​h”函数)

网格.h

#ifndef MESH_H
#define MESH_H

#include <iostream>
#include <vector>
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

class mesh
{
    public:
        mesh();
        mesh(std::vector<GLfloat> vertices, std::vector<GLuint> triangles, GLuint shaderProgram);
        ~mesh();
        void changeMesh(std::vector<GLfloat> vertices, std::vector<GLuint> triangles, GLuint shaderProgram);
        void render();
        void Translate(glm::vec3 addVector);
        void Rotate(glm::vec3 rotVector, GLfloat angle);
    protected:
    private:
        GLuint vertexArrayObject, vertexBuffer, elementBuffer, shaderProgram;
        std::vector<GLfloat> vertices;
        std::vector<GLuint> indices;
        glm::mat4 transform;
        void setUpMesh();
        void bindVertices();
};

#endif // MESH_H

网格.cpp

    #include "../include/mesh.h"

mesh::mesh(std::vector<GLfloat> vertices, std::vector<GLuint> indices, GLuint shaderProgram)
{
    this->shaderProgram = shaderProgram;
    this->vertices = vertices;
    this->indices = indices;
    setUpMesh();
}

mesh::mesh(){
    glGenVertexArrays(1, &vertexArrayObject);
    glBindVertexArray(vertexArrayObject);

    glGenBuffers(1, &vertexBuffer);
    glGenBuffers(1, &elementBuffer);
}

mesh::~mesh()
{
    glDeleteBuffers(1, &elementBuffer);
    glDeleteBuffers(1, &vertexBuffer);

    glDeleteVertexArrays(1, &vertexArrayObject);
}

void mesh::changeMesh(std::vector<GLfloat> vertices, std::vector<GLuint> triangles, GLuint shaderProgram){
    this->shaderProgram = shaderProgram;
    this->vertices = vertices;
    this->indices = indices;
    bindVertices();

}

void mesh::setUpMesh(){
    glGenVertexArrays(1, &vertexArrayObject);
    glBindVertexArray(vertexArrayObject);

    glGenBuffers(1, &vertexBuffer);
    glGenBuffers(1, &elementBuffer);

    bindVertices();
    glBindVertexArray(0);

}

void mesh::bindVertices(){
    glBindVertexArray(vertexArrayObject);

    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(GLfloat), this->vertices.data(), GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->indices.size() * sizeof(GLuint), this->indices.data(), GL_STATIC_DRAW);


    GLint amountDataPerVert = 5;

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, amountDataPerVert*sizeof(GLfloat), 0);

    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, amountDataPerVert*sizeof(GLfloat), (void*)(3*sizeof(GLfloat)));


    glBindVertexArray(0);

}
void mesh::render(){
    glBindVertexArray(vertexArrayObject);
    glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "transform"), 1, GL_FALSE, glm::value_ptr(transform));

    glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
}

void mesh::Translate(glm::vec3 addVector){
    transform = glm::translate(transform, addVector);
}

void mesh::Rotate(glm::vec3 rotVector, GLfloat angle){
    transform = glm::rotate(transform, glm::radians(angle), rotVector);
}

最佳答案

虽然您认为问题与将对象存储在 vector 中无关,但我有一种强烈的感觉,它可能确实如此。在 C++ 包装器中封装 OpenGL 对象的方式是一种痛苦的方法,您可能会像之前的许多人一样发现这一点。

典型的问题是由复制和销毁对象时发生的组合引起的。 C++ 包装器拥有的 OpenGL 对象在析构函数中被删除:

mesh::~mesh()
{
    glDeleteBuffers(1, &elementBuffer);
    glDeleteBuffers(1, &vertexBuffer);

    glDeleteVertexArrays(1, &vertexArrayObject);
}

为了说明这个问题,让我们看一个典型的序列。假设您有一个网格对象 vector ,以及一种向该 vector 添加新网格的方法(注释点供以后引用):

std::vector<mesh> m_meshes;

void createMesh(...) {
    mesh newMesh;  // point 1
    newMesh.changeMesh(...);
    m_meshes.push_back(newMesh);  // point 2
}  // point 3

看起来无害?根本不是。这里发生了不好的事情:

  • 要点 1:创建了新对象。构造函数创建 OpenGL 对象,并将它们的名称存储在成员变量中。
  • 要点 2:将网格对象的拷贝 添加到 vector ,其中使用默认复制构造函数创建拷贝。这意味着包含 OpenGL 对象名称的成员变量被复制。
  • 第 3 点:网格对象超出范围。调用析构函数,删除 OpenGL 对象。

在所有这些之后,您拥有的是一个存储在 vector 中的网格对象,OpenGL 对象名称存储在其成员变量中,而实际的 OpenGL 对象已被删除。这意味着存储在该网格对象中的对象名称现在无效。

根本问题是您的类没有适当的复制构造函数和赋值运算符。不幸的是,在成员变量中存储 OpenGL 对象名称并在构造函数/析构函数中生成/删除对象名称时,实现它们并不容易。

有很多方法可以解决这个问题。它们都不是非常漂亮:

  1. 不要在构造函数/析构函数中生成/删除 OpenGL 对象。相反,使用某种形式的 init()/cleanup()您显式调用的方法。缺点是您必须小心才能正确调用这些方法。例如,如果您有一个对象 vector ,并且想要删除该 vector ,则必须调用 cleanup()手动在 vector 的所有成员上。

  2. 始终使用指针引用对象。不使用网格对象 vector ,而是使用网格对象指针 vector 。这样,对象就不会被复制。您还必须注意正确管理对象的生命周期,而不是泄漏它们。如果您使用某种形式的智能指针而不是裸指针,这是最简单的。

  3. 使用某种形式的混合,您仍然使用实际的 C++ 对象,但它们将底层 OpenGL 对象的名称存储在引用计数的嵌套对象中。这样,他们就可以实现正确的复制/分配语义。

我认为最简单和最干净的方法是使用智能指针的选项 2。较新版本的 C++ 在标准库中具有智能指针,因此您无需执行任何操作。例如在 C++11 中,您可以使用类型 std::shared_ptr<mesh>引用您的网格对象。上面的代码片段将如下所示:

std::vector<std::shared_ptr<mesh> > m_meshes;

void createMesh(...) {
    std::shared_ptr<mesh> newMesh = std::make_shared<mesh>();
    newMesh->changeMesh(...);
    m_meshes.push_back(newMesh);
}

为确保您不会无意中复制对象,为类声明未实现的(私有(private))复制构造函数和赋值运算符也是一个好主意。本主题解释了如何在 C++11 中做到最好:With explicitly deleted member functions in C++11, is it still worthwhile to inherit from a noncopyable base class? .

关于c++ - 使用默认构造函数调用的网格类不工作 OpenGL C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28929452/

相关文章:

c++ - 相当于 `using` s 的 `template` 别名

c++ - 重载赋值运算符,rhs是函数调用

excel - 未触发 VBA 组合框更改事件

opengl - Vulkan 可以做哪些 OpenGL 4.6+ 不能做的具体事情?

c++ - 行为根据它所链接的类而改变的程序

c++ - 从 UTF-8 转换为 unicode C++

c++ - 命令行上的大输入行在 C++ 中被截断

java通用集合函数

c - Quake2中qglTexImage2D的第三种internalFormat

performance - 在 OpenGL 中不断向 GPU 上传新纹理的开销是多少?