简体   繁体   中英

Having difficulty with OpenGL in C++, when rendering multiple objects

I'm having trouble rendering multiple cubes to screen. I have a cube class which sets up the cubes vertices and loads the appropriate shader. The problem that happens is that when multiple instances of my class are created like this:

Cube * cube = new Cube();
Cube * cube1 = new Cube();

And then I initialise the cube objects like so:

cube->setPos(0, 0, 0);
cube->setType(Cube::Type::Green);
cube->createCube();

cube1->setPos(1, 0, 0);
cube1->setType(Cube::Type::Red);
cube1->createCube();

Then I draw the cubes to screen in my render method like so:

cube->draw();
cube1->draw();

My cube class looks like this:

include "cube.h"

Shader* shader;

GLuint tex;

GLuint shaderProgram, fragmentShader, vertexShader;

GLuint vbo, ebo;

GLfloat x, y, z;
GLfloat r, g, b;

Cube::Cube() {
}

void Cube::setPos(GLfloat X, GLfloat Y, GLfloat Z) {
    x = X;
    y = Y;
    z = Z;
}

    void Cube::setType(Type type) {
    switch (type) {
        case Type::Red:
        r = 1.0f;
        g = 0.0f;
        b = 0.0f;
        break;

    case Type::Green:
        r = 0.0f;
        g = 1.0f;
        b = 0.0f;
        break;

    case Type::Blue:
        r = 0.0f;
        g = 1.0f;
        b = 0.0f;
        break;

    default:
        r = 0.0f;
        g = 0.0f;
        b = 0.0f;
        break;
}
}

Cube::~Cube() {
glDeleteProgram(shaderProgram);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);

glDeleteBuffers(1, &ebo);
glDeleteBuffers(1, &vbo);
}

void Cube::createCube() {

createShader();

GLfloat vertices[] = {
        //X,    Y,     Z,    R,    G,    B
        x - 0.5f, y - 0.5f, z - 0.5f, r, g, b,      // 0
        x + 0.5f, y - 0.5f, z - 0.5f, r, g, b,      // 1
        x + 0.5f, y + 0.5f, z - 0.5f, r, g, b,      // 2
        x - 0.5f, y + 0.5f, z - 0.5f, r, g, b,      // 3

        x - 0.5f, y - 0.5f, z + 0.5f, r, g, b,      // 4
        x + 0.5f, y - 0.5f, z + 0.5f, r, g, b,      // 5
        x + 0.5f, y + 0.5f, z + 0.5f, r, g, b,      // 6
        x - 0.5f, y + 0.5f, z + 0.5f, r, g, b,      // 7
 };

GLuint elements[] = {
        0, 1, 2, 2, 3, 0,
        4, 5, 6, 6, 7, 4,
        7, 3, 0, 0, 4, 7,
        6, 2, 1, 1, 5, 6,
        0, 1, 5, 5, 4, 0,
        3, 2, 6, 6, 7, 3
};

// Create a Vertex Buffer Object and copy the vertex data to it
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);

glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

// Create an element array
glGenBuffers(1, &ebo);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements,
GL_STATIC_DRAW);

glBindFragDataLocation(shaderProgram, 0, "outColor");
glLinkProgram(shaderProgram);
glUseProgram(shaderProgram);

// Specify the layout of the vertex data
GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), 0);

GLint colAttrib = glGetAttribLocation(shaderProgram, "color");
glEnableVertexAttribArray(colAttrib);
glVertexAttribPointer(colAttrib, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat),
        (void*) (3 * sizeof(GLfloat)));
}

void Cube::update() {
// Calculate transformation
glm::mat4 trans;
trans = glm::rotate(trans, (float) clock() / (float) CLOCKS_PER_SEC * 1.0f,
        glm::vec3(0.0f, 0.0f, 1.0f));

GLint uniTrans = glGetUniformLocation(shaderProgram, "model");
glUniformMatrix4fv(uniTrans, 1, GL_FALSE, glm::value_ptr(trans));
glUniformMatrix4fv(uniTrans, 1, GL_FALSE, glm::value_ptr(trans));

glm::mat4 view = glm::lookAt(glm::vec3(1.2f, 1.2f, 1.2f), glm::vec3(0.0f, 0.0f, 0.0f),
        glm::vec3(0.0f, 0.0f, 1.0f));
GLint uniView = glGetUniformLocation(shaderProgram, "view");
glUniformMatrix4fv(uniView, 1, GL_FALSE, glm::value_ptr(view));

glm::mat4 proj = glm::perspective(45.0f, 800.0f / 600.0f, 1.0f, 10.0f);
GLint uniProj = glGetUniformLocation(shaderProgram, "proj");
glUniformMatrix4fv(uniProj, 1, GL_FALSE, glm::value_ptr(proj));
}

void Cube::draw() {
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
}


void Cube::createShader() {
// load vertex shader source
const GLchar* vertexSource = shader->fileRead("src/shaders/vertex.vs");
if (vertexSource != NULL) std::cout << "vertexSource" << std::endl;

// load fragment shader source
const GLchar* fragmentSource = shader->fileRead("src/shaders/fragment.fs");
if (fragmentSource != NULL) std::cout << "fragmentSource" << std::endl;

// Create and compile the vertex shader
vertexShader = shader->compileShader(vertexSource, GL_VERTEX_SHADER);

// Create and compile the fragment shader
fragmentShader =  shader->compileShader(fragmentSource, GL_FRAGMENT_SHADER);

// Link the vertex and fragment shader into a shader program
shaderProgram = shader->compileProgram(vertexShader, fragmentShader);
}

Long story short. I create and initialise two cubes. Both are drawn to screen in code yet when teh compiled program is run only the second cube is shown on screen. A copy of my full code is available from My GitHub , if you'd like to clone and build it.

I've spent hours upon hours searching for a way to fix this and can't so I'm reaching out to the community. I have a feeling it's something to do with needing a VAO but I can't find out how or where to implement this I've tried a few different ways already.

Any helps greatly appreciated. Thanks in advance!

none of the state you set in the create function will persist beyond another call to it.

First you should move the values to members of the cube class.

Then in create you should create and use a vao:

glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);

Before specifying the vertex layout.

Then during the draw call:

void Cube::draw() {
    glUseProgram(shaderProgram);
    glBindVertexArray(vao);
    glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
}

Add a call to glUseProgram(shaderProgram); in update as well.

However it's more efficient to use a single static program and then load the model matrix in the uniform;

void Cube::draw() {
    //assume program is already bound and non-cube-specific uniforms are already set
    glBindVertexArray(vao);
    glUniformMatrix4fv(uniTrans, 1, GL_FALSE, glm::value_ptr(trans));//kept in a field
    glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
}

Without going into depth on your OpenGL usage (which @ratchetfreak already did in a separate answer), you have a very fundamental C++ problem.

You're declaring a bunch of variables at the start of your Cube.cpp file:

Shader* shader;

GLuint tex;

GLuint shaderProgram, fragmentShader, vertexShader;

GLuint vbo, ebo;

GLfloat x, y, z;
GLfloat r, g, b;

With the variables declared like this, you will have only a single copy of these values, which are shared between all instances of the Cube class. For example, all your cubes will have the same position, which is the one of the last cube you created, because they all share the same x , y and z variables that hold the position.

You need to define these variables as class members so that each Cube instance has separate values. This means that they need to go into the header file, typically in the private section of the class. For example:

class Cube {
public:
    // Declaration of constructor, methods, etc.

private:
    GLfloat x, y, z;
};

You'll probably want to use more descriptive names for the variables.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM