PDA

View Full Version : How can I draw two objects? (Newbie)



TheFearlessHobbit
03-29-2017, 07:29 PM
Hello guys, basically I am trying to draw a background and a player in front of it. I don't know whether I should create a new VBO or VAO for each different object. Here's what I have so far:

In my Sprite Class: (This is currently only drawing the background, which is two triangles)


Sprite::Sprite(Vertex* vertices, unsigned int numVertices)
{
// Create Shaders
m_Program = m_Shader.CreateProgram("VShader.vs", "FShader.fs");

m_NumVertices = numVertices;

GLubyte m_Background[] = { 0, 1, 3, 1, 2, 3 };

glGenVertexArrays(1, &m_VertexArrayObject);
glBindVertexArray(m_VertexArrayObject);

glGenBuffers(1, &m_VertexBufferObject);
glBindBuffer(GL_ARRAY_BUFFER, m_VertexBufferObject);
glBufferData(GL_ARRAY_BUFFER, m_NumVertices * sizeof(vertices[0]), vertices, GL_STATIC_DRAW);

glGenBuffers(1, &m_ElementBufferObject);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ElementBufferObject);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(m_Background), m_Background, GL_STATIC_DRAW);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));

glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
}

In the main header file


// Background
Vertex Position[] =
{
Vertex(glm::vec3(1.0f, 1.0f, 0.0f), glm::vec2(0.0f, 0.0f)),
Vertex(glm::vec3(1.0f, -1.0f, 0.0f), glm::vec2(0.0f, 1.0f)),
Vertex(glm::vec3(-1.0f, -1.0f, 0.0f), glm::vec2(1.0f, 1.0f)),
Vertex(glm::vec3(-1.0f, 1.0f, 0.0f), glm::vec2(1.0f, 0.0f))
};
// Player
Vertex Position2[] =
{
Vertex(glm::vec3(0.5f, 0.5f, 0.0f), glm::vec2(0.0f, 0.0f)),
Vertex(glm::vec3(0.5f, -0.5f, 0.0f), glm::vec2(0.0f, 1.0f)),
Vertex(glm::vec3(-0.5f, -0.5f, 0.0f), glm::vec2(1.0f, 1.0f)),
Vertex(glm::vec3(-0.5f, 0.5f, 0.0f), glm::vec2(1.0f, 0.0f))
};

void Render()
{
CSprite Background(Position, sizeof(Position) / sizeof(Position[0]));
CSprite Player(Position2, sizeof(Position2) / sizeof(Position2[0]));
Background.Draw();
Player.Draw();
}

The result is just a player (that's really just two triangles for now) but no background behind.

How can I make it so that I can draw more than one object on the screen?

Thank you.

john_connor
03-30-2017, 01:22 AM
usualy its done issuing 2 times a drawcall, like "glDrawArrays(...)" or "glDrawElements(...)", with different arguments and / or transformation matrices applied. you are far better of if you remove the drawing part out of the "sprite" class, and put it somewhere else where you draw everything at once, for example in a "renderer" class. that means also that you can share the same resources (buffer / vertexarray / program) for different sprites

TheFearlessHobbit
03-30-2017, 03:33 AM
Hey John, thank you for your answer.

So, here's what I have managed so far:


void Sprite::DrawGeometry(Vertex* Vertices, unsigned int numOfVertices)
{
GLubyte m_Background[] = { 0, 1, 3, 1, 2, 3 };

glGenVertexArrays(1, &m_BackgroundVAO);
glBindVertexArray(m_BackgroundVAO);

glGenBuffers(1, &m_BackgroundVBO);
glBindBuffer(GL_ARRAY_BUFFER, m_BackgroundVBO);
glBufferData(GL_ARRAY_BUFFER, numOfVertices * sizeof(Vertices[0]), Vertices, GL_STATIC_DRAW);

glGenBuffers(1, &m_BackgroundEBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_BackgroundEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(m_Background), m_Background, GL_STATIC_DRAW);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));

glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);

glGenTextures(1, &m_Textures);
glBindTexture(GL_TEXTURE_2D, m_Textures);

// Texture Wrapping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
// Texturing etc...

SOIL_free_image_data(image);
glBindTexture(GL_TEXTURE_2D, 0);
}
void Sprite::DrawGeometryPlayer(Vertex* Vertices, unsigned int numOfVertices)
{
GLubyte m_Player[] = { 0, 1, 3, 1, 2, 3 };

glGenVertexArrays(1, &m_PlayerVAO);
glBindVertexArray(m_PlayerVAO);

glGenBuffers(1, &m_PlayerVBO);
glBindBuffer(GL_ARRAY_BUFFER, m_PlayerVBO);
glBufferData(GL_ARRAY_BUFFER, numOfVertices * sizeof(Vertices[0]), Vertices, GL_STATIC_DRAW);

glGenBuffers(1, &m_PlayerEBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_PlayerEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(m_Player), m_Player, GL_STATIC_DRAW);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));

glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);

glGenTextures(1, &m_Textures);
glBindTexture(GL_TEXTURE_2D, m_Textures);

// Texture etc....

SOIL_free_image_data(image);
glBindTexture(GL_TEXTURE_2D, 0);
}

void Sprite::Render()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glUseProgram(m_Program);

// Player
glBindVertexArray(m_PlayerVAO);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_Textures);
glUniform1i(glGetUniformLocation(m_Program, "Texture1"), 0);

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, 0);

// Background
glBindVertexArray(m_BackgroundVAO);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_Textures);
glUniform1i(glGetUniformLocation(m_Program, "Texture1"), 0);

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, 0);

glBindTexture(GL_TEXTURE_2D, 0);
glBindVertexArray(0);

glutSwapBuffers();
}
Result is just a player. If I call draw background first, then the result will be the background. This is frustrating... :(

john_connor
03-30-2017, 04:17 AM
you only need to initialize once, you dont need to create the resources each frame again:

void Initialize()
{
// create 1 program, comipile shader + link program
// create vertexarray, vertexbuffer + fill vertexbuffer with all vertices
// store {vertexoffset, vertexcount} for each model
}

void Render(Scene& myscene)
{
glUseProgram()
glBindVertexArray()

for each (element in myscene)
{
mat4 transformation = element.GetModelToWorldTransformation();
// set uniform to this transformation
glDrawArrays(GL_TRIANGLES, ..., ...) with the arguments depending on what model they are using (which you've stored previously)
}

}

take a look at my examples:
https://sites.google.com/site/john87connor/advanced-opengl-tutorials/tutorial-02-model-obj
https://sites.google.com/site/john87connor/advanced-opengl-tutorials/tutorial-07-multiple-models

another this is that you need to have glEnable(GL_DEPTH_TEST); called for a 3D scene, otherwise the most recent drawcall will overwrite previous once

TheFearlessHobbit
03-30-2017, 04:56 AM
you only need to initialize once, you dont need to create the resources each frame again:

void Initialize()
{
// create 1 program, comipile shader + link program
// create vertexarray, vertexbuffer + fill vertexbuffer with all vertices
// store {vertexoffset, vertexcount} for each model
}

void Render(Scene& myscene)
{
glUseProgram()
glBindVertexArray()

for each (element in myscene)
{
mat4 transformation = element.GetModelToWorldTransformation();
// set uniform to this transformation
glDrawArrays(GL_TRIANGLES, ..., ...) with the arguments depending on what model they are using (which you've stored previously)
}

}

take a look at my examples:
https://sites.google.com/site/john87connor/advanced-opengl-tutorials/tutorial-02-model-obj
https://sites.google.com/site/john87connor/advanced-opengl-tutorials/tutorial-07-multiple-models

another this is that you need to have glEnable(GL_DEPTH_TEST); called for a 3D scene, otherwise the most recent drawcall will overwrite previous once


Fixed it thank you for the examples! Your work looks really nice. I hope I'll be able to code like that soon. :)

8Observer8
03-30-2017, 12:14 PM
This is an useful example how to draw two objects with
different shaders (with solid colour and textured) in WebGL: https://jsfiddle.net/8Observer8/jnd0j6w0/

It is simple to port it in C++, because I ported it in C#/OpenTK