PDA

View Full Version : Nothing shows up when putting operations into a class draw() method!



wangdw406
10-16-2016, 04:59 AM
Hi, everyone:

I am new to opengl and was trying to draw a 3D box, and everything went well when I drew in the glut display callback function directly. But inspired by the object oriented idea, I decided to create a "box" class
and made this class object draw itself. However, this turned out to be not working.

As a comparison, I will list these two version of codes. First, the properly working code (glut display callback function):


void display(){
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

//create shaders and link to a program
Shader boxShader("shaders/shader.vert.glsl", "shaders/shader.frag.glsl");

GLfloat vert[] = {
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,

-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,

-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,

0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,

-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,

-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f
};
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vert), vert, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(2);

glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
int imgWidth, imgHeight;
GLubyte *image = nullptr;
loadImage("resource/container.jpg", image, imgWidth, imgHeight);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imgWidth, imgHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);

boxShader.useShaderProgram();

mat4 modelTrans;
modelTrans = glm::rotate(modelTrans, 20.0f, vec3(0.0f, 1.0f, 0.0f));
glUniformMatrix4fv(glGetUniformLocation(boxShader. program, "modelTrans"), 1,
GL_FALSE, glm::value_ptr(modelTrans));

mat4 viewTrans;
viewTrans = glm::translate(viewTrans, vec3(0.0f, 0.0f, -5.0f));
glUniformMatrix4fv(glGetUniformLocation(boxShader. program, "viewTrans"), 1,
GL_FALSE, glm::value_ptr(viewTrans));

mat4 projectionTrans;
projectionTrans = glm::perspective(45.0f, 8.0f / 6.0f, 0.1f, 100.0f);
glUniformMatrix4fv(glGetUniformLocation(boxShader. program, "projectionTrans"), 1,
GL_FALSE, glm::value_ptr(projectionTrans));

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(glGetUniformLocation(boxShader.program , "myTexture"), 0);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);

glutSwapBuffers();
}


The above code worked correctly. Then I tried to created a Box class:


class Box
{
public:
Box();
~Box();
void draw();
friend void loadImage(std::string filenameString,
GLubyte*& imgData, int &imgWidth, int &imgHeight);

private:
GLfloat *vertexAttrib;
GLuint VAO, VBO, texture;
//void transformation(Shader boxShader);
};


and in the box draw() method, which is almost the same as the above code in display function, I impleted:


void Box::draw(){
Shader boxShader("shaders/shader.vert.glsl", "shaders/shader.frag.glsl");

glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexAttrib), vertexAttrib, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(2);

glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
int imgWidth, imgHeight;
GLubyte *image = nullptr;
loadImage("resource/container.jpg", image, imgWidth, imgHeight);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imgWidth, imgHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);

boxShader.useShaderProgram();

mat4 modelTrans;
modelTrans = glm::rotate(modelTrans, 20.0f, vec3(0.0f, 1.0f, 0.0f));
modelTrans = glm::rotate(modelTrans, 20.0f, vec3(1.0f, 0.0f, 0.0f));
glUniformMatrix4fv(glGetUniformLocation(boxShader. program, "modelTrans"), 1,
GL_FALSE, glm::value_ptr(modelTrans));

mat4 viewTrans;
viewTrans = glm::translate(viewTrans, vec3(0.0f, 0.0f, -5.0f));
glUniformMatrix4fv(glGetUniformLocation(boxShader. program, "viewTrans"), 1,
GL_FALSE, glm::value_ptr(viewTrans));

mat4 projectionTrans;
projectionTrans = glm::perspective(45.0f, 8.0f / 6.0f, 0.1f, 100.0f);
glUniformMatrix4fv(glGetUniformLocation(boxShader. program, "projectionTrans"), 1,
GL_FALSE, glm::value_ptr(projectionTrans));

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(glGetUniformLocation(boxShader.program , "myTexture"), 0);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}

Finally, I created a box object in the glut display callback function and call its draw method, but this doesn't work. I have been thinking about this problem and debugging for many days but still couldn't figure it out!
I'm guesing sth might be wrong with the opengl object scope, but I'm not sure.

Could anyone please take a look and give some suggestions?
Thanks in advance for any help!!!

Best,
David.

GClements
10-16-2016, 05:49 AM
GLfloat vert[] = {




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







private:
GLfloat *vertexAttrib;




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



In the first version, sizeof(vert) is the size of the vertex data. In the second, sizeof(vertexAttrib) is the size of a pointer. So you're only copying the first 4 or 8 bytes of the vertex data into the buffer.

john_connor
10-16-2016, 08:15 AM
another point is, that you shouldnt create buffers/vertexarrays/textures each frame, do that only once when initializing (unless you have a good reason for that)
what you have to call each frame is:


glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


boxShader.useShaderProgram();

mat4 modelTrans;
modelTrans = glm::rotate(modelTrans, 20.0f, vec3(0.0f, 1.0f, 0.0f));
glUniformMatrix4fv(glGetUniformLocation(boxShader. program, "modelTrans"), 1,
GL_FALSE, glm::value_ptr(modelTrans));

mat4 viewTrans;
viewTrans = glm::translate(viewTrans, vec3(0.0f, 0.0f, -5.0f));
glUniformMatrix4fv(glGetUniformLocation(boxShader. program, "viewTrans"), 1,
GL_FALSE, glm::value_ptr(viewTrans));

mat4 projectionTrans;
projectionTrans = glm::perspective(45.0f, 8.0f / 6.0f, 0.1f, 100.0f);
glUniformMatrix4fv(glGetUniformLocation(boxShader. program, "projectionTrans"), 1,
GL_FALSE, glm::value_ptr(projectionTrans));

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);

glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);

glutSwapBuffers();


you can further simplify that by pulling the part out where you set the texture unit to the shader:
(you need to do that only once, the program object then stores that "uniform" value)


glUniform1i(glGetUniformLocation(boxShader.program , "myTexture"), 0);


you can further simplify that by pulling the part out where you bind the texture to a texture unit:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);


unless you want to use that texture unit ("GL_TEXTURE0") for another texture within the same frame
but you can also simply use another texture unit, e.g. GL_TEXTURE1, GL_TEXTURE2, ...




But inspired by the object oriented idea, I decided to create a "box" class
and made this class object draw itself.

not a very good idea
each object of that class would use the same resources (shaders/buffers/etc)
a better idea is to put all the resources (shaders/buffers/etc) into a separate class (e.g. "Graphics") which has only 1 member function "void Render(something);"

i've done an example for that:
https://sites.google.com/site/john87connor/advanced-opengl-tutorials/tutorial-01-3d-scene-camera



class Renderer
{
public:

virtual ~Renderer() {}

virtual void Render(const Scene& scene) = 0;

};


"scene" contains everything you want to draw, once you call render(...), the renderer will draw everything, not just 1 box or so
that way you can share the resources you would otherwise have per instanciated class object ("box")

wangdw406
10-16-2016, 07:44 PM
In the first version, sizeof(vert) is the size of the vertex data. In the second, sizeof(vertexAttrib) is the size of a pointer. So you're only copying the first 4 or 8 bytes of the vertex data into the buffer.

Thank you so much GClements.
Yes, you are correct, so I changed that part to


glBufferData(GL_ARRAY_BUFFER, 180*sizeof(GLFloat), vertexAttrib, GL_STATIC_DRAW);

But this still doesn't help! Any idea?

wangdw406
10-16-2016, 07:49 PM
another point is, that you shouldnt create buffers/vertexarrays/textures each frame, do that only once when initializing (unless you have a good reason for that)

Thank you John_connor for your comments, I will check your post and change my code.

Silence
10-16-2016, 11:01 PM
One thing you can easily do is to start again from your working version and move things "slowly", with ensuring each time that each change keep the program working.

And as john_connor mentioned you should only keep the drawing calls in you draw function. VAO, shader and texture creation/generation should be done once, provided a valid and made-current OpenGL context.

GClements
10-17-2016, 04:19 AM
But this still doesn't help! Any idea?
Apart from that, the only difference between the two versions is that the second version uses two rotations in the construction of modelTrans.

If that isn't relevant, then either the issue is in some code which you haven't shown, or the code which you have shown doesn't match the code you're testing.

In any case, I suggest following Silence's advice: revert to the version which does work then make changes one at a time, testing after each change.

john_connor
10-17-2016, 04:25 AM
Thank you so much GClements.
Yes, you are correct, so I changed that part to


glBufferData(GL_ARRAY_BUFFER, 180*sizeof(GLFloat), vertexAttrib, GL_STATIC_DRAW);

But this still doesn't help! Any idea?

i assume you didnt remove the unnecessary parts in you render function ...
and i assume you still create a new shader/vertexarray/buffer each frame
and i assume you dont check for any errors (as described here (https://www.opengl.org/wiki/OpenGL_Error))

it might be that you quickly exceed any limitation, since you create about 60 different shaders/buffers/etc each frame!

wangdw406
10-19-2016, 06:29 AM
Thank you for all of your help, I finally made it correct.

I just created the vertexAttrib array in a different way and still use


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

Then it just worked, although I don't know why. Any way, thank you all!