I am new to modern OpenGL VBO/VAO and I struggle with one thing: I have coded a RectangleAsset based on this tutorial, but I am not sure how to move information about texture UVs to the RactangleAssetInstance (my rectangles can have different textures).
Do I have to create new VAO for it or can I just pass the UVs by some other means? Or add second VBO for UVs? And most imporantly: what would be best practice solving this?
struct RectangleAsset {
GLuint VBO;
GLuint VAO;
};
struct RectangleAssetInstance { //this is actually more complex class in my code
RectangleAsset rect; //but tried to extract the most imporatant code
glm::mat4 transform;
Texture * texture;
void UpdateTransform(int,int,int,int);
private:
int x,y,width,height;
};
and function loading the RectangleAsset:
void GUIRenderer::init()
{
image = new Program ("vs.glsl", "fs.glsl");
glGenVertexArrays(1, &rect.VAO);
glBindVertexArray(rect.VAO);
glGenBuffers(1, &rect.VBO);
glBindBuffer(GL_ARRAY_BUFFER, rect.VAO);
GLfloat vertexData[] = {
// X Y Z U V
0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
};
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
glEnableVertexAttribArray(image->attrib("vert"));
glVertexAttribPointer(image->attrib("vert"), 3, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), NULL);
glEnableVertexAttribArray(image->attrib("vertTexCoord"));
glVertexAttribPointer(image->attrib("vertTexCoord"), 2, GL_FLOAT, GL_TRUE, 5*sizeof(GLfloat), (const GLvoid*)(3 * sizeof(GLfloat)));
glBindVertexArray(0);
}
NOTE: I plan to use RectangleAssetInstances only at one place, in one std::vector for GUI rendering(non-static gui). Might it be good idea to merge all rectangles in one VBO and VAO (and re-create it whenever UIElement is added/removed)?
Any advices learning best practices with OpenGL are welcomed.
VAOs store both the format of input data and the location that that input data is sourced from. This is actually two separate concepts. If you want to change where the UVs come from you must call glVertexAttribPointer again. This call would look something like glVertexAttribPointer(uvLoc, GL_FLOAT, false, sizeof(float) * 5, (const GLvoid*)(sizeof(float) * 3)) Note that this will NOT change the VBO that your position information is coming from.
Now you mentioned that you wanted to do this because your rectangle instances may have different textures. You need not change the UVs in order to make this happen. In general positions, UVs, and normals are all part of the mesh and you only need one copy of them. To change the texture just call glActiveTexture(GL_TEXTURE0 + i) followed by glBindTexture(GL_TEXTURE_2D, tex) and then set the sampler uniform in your shader to use the correct image unit with glUniform1i(samplerLoc, i)
There is also the ARB_vertex_attrib_binding extension, which became core in OpenGL 4.3. This allows you to separate attribute layout from data location. The article at the OpenGL wiki provides information on how to do this, but again it is probably better to author all the textures for a given mesh using the same UVs.
In regards to your question about merging everything into one VAO and VBO: If you only want rectangles than this is not necessary, since you can get any kind of rectangle you would like using an affine transform with non-uniform scaling component. Thus you only need one VAO and one VBO in total, and there is no need to merge anything.
Related
Do i need to have separate draw call for each object i would like to render and then swap buffers?
As i understand i have one VBO rendered, then another VBO is bidden and is drawn, and after all that i am swapping buffers to present the back buffer.
for Example
//Then Render
float vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
//Create Vertex Buffer
GLint VBO = 0;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glUseProgram(ShaderProgram);
glDrawArrays(GL_TRIANGLES, 0, 3);
Here i draw a single triangle, how would one go about drawing a second triangle with different shader program
To draw different objects, you need different VBOs and that means a different draw call for every different object. If you want to draw a particular object more than once, you can use instancing or bind and draw the same VBO more than once. You can create one gigantic VBO for all objects but since you are a beginner, stick to one VBO per object for now.
I searched for this and only found a post from 2014 asking about a somewhat similar situation. However, as I couldn't understand what was done there, I'm asking again, specifically for my implementation, hoping this sheds some light on the topic in general as well. I am fairly new to c++ and openGL, so please be so kkind as to excuse stupid mistakes.
I'm trying to implement a simple 2D HUD for my 3D game. Now, my game is fully rendered, due to having a bloom effect in my game, I even rendered my game on a screen quad.
What I now want to do ist placing a HUD over this rendered scene, I, however, can't seem to do that.
My screen quad for the game is drawn like so:
unsigned int quadVAO = 0;
unsigned int quadVBO;
void renderQuad()
{
if (quadVAO == 0)
{
float quadVertices[] = {
// vertex attributes for a quad that fills the entire screen in Normalized Device Coordinates.
// texCoords
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f
};
// setup plane VAO
glGenVertexArrays(1, &quadVAO);
glGenBuffers(1, &quadVBO);
glBindVertexArray(quadVAO);
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
}
glBindVertexArray(quadVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
}
What I tried to do, ist change my renderQuad method to a renderHUDquad one by basically just changing the dimensions of the quad to make it appear in the bottom left corner of the screen.
The code looks as follows:
unsigned int HUDquadVAO = 0;
unsigned int HUDquadVBO;
void renderHUDQuad()
{
if (HUDquadVAO == 0)
{
float HUDquadVertices[] = {
// vertex attributes for a quad that fills the entire screen in Normalized Device Coordinates.
// texCoords
0.0f, 0.02f,
0.0f, 0.0f,
0.2f, 0.0f,
0.0f, 0.02f,
0.2f, 0.0f,
0.2f, 0.02f
};
// setup plane VAO
glGenVertexArrays(1, &HUDquadVAO);
glGenBuffers(1, &HUDquadVBO);
glBindVertexArray(HUDquadVAO);
glBindBuffer(GL_ARRAY_BUFFER, HUDquadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(HUDquadVertices), &HUDquadVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
}
glBindVertexArray(HUDquadVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
}
As this only needs to be a small green quad, i.e. a health bar for the player, I was thinking about just assigning it a green texture or sth..
However, when drawing my two quads like this:
// Third pass = Combined bloom pictures
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
bloomShader->use();
// Set uniform for multiple layout uniforms
bloomShader->setUniform("scene", 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, colorAndLightBuffers[0]);
// Set uniform for multiple layout uniforms
bloomShader->setUniform("bloomBlur", 1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, pingpongBuffer[horizontal == 0 ? 1 : 0]);
bloomShader->setUniform("bloom", bloom);
bloomShader->setUniform("exposure", exposure);
renderQuad();
renderHUDQuad();
// Swap buffers
glfwSwapBuffers(window);
I only get the HUD element without any of the stuff I drew before as if the rest of the screen was rendered black. I thought I could just add this to the old buffer, as there a way to do this?
You did screw up your GL state very badly:
void renderHUDQuad() {
if (HUDquadVAO == 0)
{
[...]
glGenVertexArrays(1, &quadVAO);
You actually use quadVAO in the rest of this function, so you overwrite your fullscreen quad by the smaller one, which means the rest of your scene will be scaled down to this quad from the next frame on...
I'm trying to show a texture(yes it is a pot) with opengl 2.1 and glsl 120, but i'm not sure on how to do it, all i can get is a black quad, i've been following this tutorials: A Textured Cube, OpenGl - Textures and what i have understood is that i need to:
Specify the texture coordinates to attach to each vertex(in my case are 6 vertices, a cube without indexing)
Load the texture and bind it in a texture unit(default is 0)
call glDrawArrays
Inside the shaders i need to:
Receive the texture coords in an attribute in the vertex shader and pass it to the fragment shader through a varying variable
In the fragment shader use a sampler object to sample a pixel, in the position specified by the varying variable, from the texture.
Is it all correct?
Here is how i create the texture VBO and load the texture:
void Application::onStart(){
unsigned int format;
SDL_Surface* img;
float quadCoords[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
-0.5f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f};
const float texCoords[] = {
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f,
1.0f, 1.0f,
0.0f, 1.0f,
0.0f, 0.0f};
//shader loading omitted ...
sprogram.bind(); // call glUseProgram(programId)
//set the sampler value to 0 -> use texture unit 0
sprogram.loadValue(sprogram.getUniformLocation(SAMPLER), 0);
//quad
glGenBuffers(1, &quadBuffer);
glBindBuffer(GL_ARRAY_BUFFER, quadBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*18, quadCoords, GL_STATIC_DRAW);
//texture
glGenBuffers(1, &textureBuffer);
glBindBuffer(GL_ARRAY_BUFFER, textureBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*12, texCoords, GL_STATIC_DRAW);
//load texture
img = IMG_Load("resources/images/crate.jpg");
if(img == nullptr)
throw std::runtime_error(SDL_GetError());
glGenTextures(1, &this->texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, this->texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img->w, img->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, img->pixels);
SDL_FreeSurface(img);
}
rendering phase:
glClear(GL_COLOR_BUFFER_BIT);
glEnableVertexAttribArray(COORDS);
glBindBuffer(GL_ARRAY_BUFFER, quadBuffer);
glVertexAttribPointer(COORDS, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(TEX_COORDS);
glBindBuffer(GL_ARRAY_BUFFER, textureBuffer);
glVertexAttribPointer(TEX_COORDS, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
//draw the vertices
glDrawArrays(GL_TRIANGLES, 0, 6);
vertex shader:
#version 120
attribute vec3 coord;
attribute vec2 texCoord;
varying vec2 UV;
void main(){
gl_Position = vec4(coord.x, coord.y, coord.z, 1.0);
UV = texCoord;
}
fragment shader:
#version 120
uniform sampler2D tex;
varying vec2 UV;
void main(){
gl_FragColor.rgb = texture2D(tex, UV).rgb;
gl_FragColor.a = 1.0;
}
I know that the tutorials use out instead of varying so i tried to "convert" the code, also there is this tutorial: Simple Texture - LightHouse that explain the gl_MultiTexCoord0 attribute and gl_TexCoord array wich are built in, but this is almost the same thing i'm doing. I want to know if 'm doing it all right and if not, i would like to know how to show a simple 2d texture in the screen with opengl 2.1 and glsl 120
Do you have a particular reason to use opengl 2.1 with glsl version 1.2 ? If not stick to the openGl 3.0 because its easier to understand imho.
My guess is you have 2 big problems :
First of all getting a black quad: If its size occupies your hole app then its the background color. That means it doesn't draw anything at all .
I think(by testing this) OpenGL has a default program which will activate and even if you have already set a vertex array/buffer object on the gpu.It should render as a white quad in your window... So that might be ur 1st problem . I dont know if opengl 2.1 has vertex buffer arrays but opengl 3.0 has and you should definetly make use of that!
Second : you don't use your shader program in the rendering phase;
Call this function before drawing your quad:
glUseProgram(myProgram); // The myProgram variable is your compiled shader program
If by any chance you would like me to explain how to draw your quad using OpegGL 3.0 ++ let me know :) ...It is not far from what you already wrote in your code .
I began to learn OpenGL about a week ago and I now want to create a mesh class. The code I'm about to show gives me a black screen (that's the color I fill it with).
I basically stripped the code from my main function and placed it inside a class, it worked inside main.
mesh.cpp
#include "mesh.h"
mesh::mesh(std::vector<GLfloat> vertices, std::vector<GLuint> indices)
{
this->vertices = vertices;
this->indices = indices;
glGenVertexArrays(1, &vertexArrayObject);
glGenBuffers(1, &vertexBuffer);
glGenBuffers(1, &triangleBuffer);
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, triangleBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->indices.size() * sizeof(GLuint), this->indices.data(), GL_STATIC_DRAW);
glBindVertexArray(0);
}
mesh::~mesh()
{
glDeleteBuffers(1, &triangleBuffer);
glDeleteBuffers(1, &vertexBuffer);
glDeleteVertexArrays(1, &vertexArrayObject);
}
void mesh::update(){
glBindVertexArray(vertexArrayObject);
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
mesh.h
#ifndef MESH_H
#define MESH_H
#include <iostream>
#include <vector>
#include <GL/glew.h>
#include <glm/glm.hpp>
class mesh
{
public:
mesh(std::vector<GLfloat> vertices, std::vector<GLuint> triangles);
~mesh();
void update();
protected:
private:
GLuint vertexArrayObject, vertexBuffer, triangleBuffer;
std::vector<GLfloat> vertices;
std::vector<GLuint> indices;
};
#endif // MESH_H
According to this, this should be the correct way to do it (?).
BTW, all this code is a mashup from this and open.gl sites, here are the variables I pass into the constructor.
For the vertices:
std::vector<GLfloat> vertices = {
// Position Color Texcoords
-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // Top-left
0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // Top-right
0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // Bottom-right
-0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f // Bottom-left
};
and the indices:
std::vector<GLuint> elements = {
0, 1, 2,
2, 3, 0
};
Also please note that I changed the code snippets according to Anton's suggestions, that didn't seem to work
You have two problems that immediately come to mind:
this->triangles.size() * sizeof(GLfloat)
This problem will not affect anything because GLfloat has the same size as GLuint, but if that was not a typo, it indicates to me you may be thinking about the index buffer wrong.
Make this triangles.size () * sizeof (GLuint) for consistency.
glDrawElements(GL_TRIANGLES, this->vertices.size(), GL_UNSIGNED_INT, 0);
You have two separate lists in your class, vertices and triangles, and this call only cares about the size of one of those.
triangles is an array of indices that makes a list of triangles (3 indices per-triangle).
When you draw your elements array, you pass the number of elements in that list, not the number of vertices in your mesh.
While not technically a problem, I think you use this-> a little bit too much when you write your code.
It is useful when you have variables at one scope that have the same name as members in your class (which one could argue builds a case for inadequately descriptive names), but completely unnecessary in functions like mesh::~mesh(). It makes your code harder to read (for me anyway) because the lines are much longer.
Following the theme of inadequately descriptive names, by calling your index array "triangles" you have inadvertently limited this mesh class to drawing triangles. That might be a good variable name for a specialized constructor that builds a triangle mesh, but not so much for a class member. If you use indices instead, that gets rid of the name collision and the need to qualify everything using this->.
I write a program to draw a simple triangle and I use VAO、VBO and GLSL shaders. The result is the following:
But if I enable depth test using:
glEnable(GL_DEPTH_TEST)
nothing appears in the window.
Now I post some code of my program:
float positionData[] = {
-0.8f, -0.8f, 0.0f,
0.8f, -0.8f, 0.0f,
0.0f, 0.8f, 0.0f };
float colorData[] = {
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f };
void initVBO()
{
// Create and populate the buffer objects
GLuint vboHandles[2];
glGenBuffers(2, vboHandles);
GLuint positionBufferHandle = vboHandles[0];
GLuint colorBufferHandle = vboHandles[1];
glBindBuffer(GL_ARRAY_BUFFER,positionBufferHandle);
glBufferData(GL_ARRAY_BUFFER,9 * sizeof(float),
positionData,GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER,colorBufferHandle);
glBufferData(GL_ARRAY_BUFFER,9 * sizeof(float),
colorData,GL_STATIC_DRAW);
glGenVertexArrays(1,&vaoHandle);
glBindVertexArray(vaoHandle);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, positionBufferHandle);
glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 0, (GLubyte *)NULL );
glBindBuffer(GL_ARRAY_BUFFER, colorBufferHandle);
glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, 0, (GLubyte *)NULL );
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(vaoHandle);
glDrawArrays(GL_TRIANGLES,0,3);
glBindVertexArray(0);
glutSwapBuffers();
}
My question is : why I cannot draw the triangle after enabling depth test?
There are multiple (types of) buffers used when rendering, typically. One is the color buffer, which contains the pixel data in some pixel format (IE: RGB with 8 bits for each color channel). Another typical buffer used is the depth buffer. Depth testing and writing to the depth buffer are two different things. Depth testing checks the depth value from a pixel against the depth value of the associated pixel(s) in the depth buffer and decides whether to accept or reject the pixel/fragment. Depth writing actually writes that value to a buffer, such as the depth buffer.
Your program probably writes to the depth buffer and test the depth buffer, but you never clear out the depth buffer, so it believes that, even though the color buffer has been cleared, that there are already things written to it that are at/in front (or whatever is configured) of the pixels you're trying to write to, so it rejects them.
Clear your depth buffer each frame, typically. You do this by passing the GL_DEPTH_BUFFER_BIT flag to glClear.
You need to explicitly clear the depth buffer, too:
glClear(GL_COLOR_BUFFER_BIT |
GL_DEPTH_BUFFER_BIT)