I'm trying to render both cone objects to the screen but the screen is kind of flashing. It only show one cone at a time. Below is the code I have so far. I followed some instructions on the Internet where people say put glBindVertexArray(0) and glfwSwapBuffers(gWindow) at the end of the loop instead of below each Render function call.
I tried in Render function and in the loop as well but it didn't work. If I put them in the loop, it shows only the second cone. If I put them in Render(), it's flashing. What did I do wrong here?
#include <iostream> // cout, cerr
#include <cstdlib> // EXIT_FAILURE
#include <GL/glew.h> // GLEW library
#include <GLFW/glfw3.h> // GLFW library
// GLM Math Header inclusions
#include <glm/glm.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "Cone.h" // Include the header file
using namespace std; // Standard namespace
/*Shader program Macro*/
#ifndef GLSL
#define GLSL(Version, Source) "#version " #Version " core \n" #Source
#endif
// Unnamed namespace
namespace
{
const char* const WINDOW_TITLE = "Thi Nguyen - Module Three Milestone"; // Macro for window title
// Variables for window width and height
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;
// Main GLFW window
GLFWwindow* gWindow = nullptr;
// Shader program
GLuint gProgramId;
}
/* User-defined Function prototypes to:
* initialize the program, set the window size,
* redraw graphics on the window when resized,
* and render graphics on the screen
*/
bool UInitialize(int, char* [], GLFWwindow** window);
void UResizeWindow(GLFWwindow* window, int width, int height);
void UProcessInput(GLFWwindow* window);
bool UCreateShaderProgram(const char* vtxShaderSource, const char* fragShaderSource, GLuint& programId);
void UDestroyShaderProgram(GLuint programId);
/* Vertex Shader Source Code*/
const GLchar* vertexShaderSource = GLSL(440,
layout(location = 0) in vec3 position; // Vertex data from Vertex Attrib Pointer 0
layout(location = 1) in vec4 color; // Color data from Vertex Attrib Pointer 1
out vec4 vertexColor; // variable to transfer color data to the fragment shader
//Global variables for the transform matrices
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0f); // transforms vertices to clip coordinates
vertexColor = color; // references incoming color data
}
);
/* Fragment Shader Source Code*/
const GLchar* fragmentShaderSource = GLSL(440,
in vec4 vertexColor; // Variable to hold incoming color data from vertex shader
out vec4 fragmentColor;
void main()
{
fragmentColor = vec4(vertexColor);
}
);
int main(int argc, char* argv[])
{
if (!UInitialize(argc, argv, &gWindow))
return EXIT_FAILURE;
// Create the mesh
//UCreateMesh(gMesh); // Calls the function to create the Vertex Buffer Object
Cone Cone1;
Cone1.CreateMesh();
Cone Cone2;
Cone2.CreateMesh();
// Create the shader program
if (!UCreateShaderProgram(vertexShaderSource, fragmentShaderSource, gProgramId))
return EXIT_FAILURE;
// Sets the background color of the window to black (it will be implicitely used by glClear)
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// render loop
// -----------
while (!glfwWindowShouldClose(gWindow))
{
// input
UProcessInput(gWindow);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glm::mat4 scale = glm::scale(glm::vec3(1.0f, 1.0f, 1.0f));
glm::mat4 rotation = glm::rotate(-90.0f, glm::vec3(1.0, 1.0f, 1.0f));
glm::mat4 translation = glm::translate(glm::vec3(1.0f, 0.0f, 0.0f));
glm::mat4 model = translation * rotation * scale;
glm::mat4 view = glm::translate(glm::vec3(0.0f, 0.0f, -5.0f));
glm::mat4 scale2 = glm::scale(glm::vec3(2.0f, 2.0f, 2.0f));
glm::mat4 rotation2 = glm::rotate(0.0f, glm::vec3(1.0, 1.0f, 1.0f));
glm::mat4 translation2 = glm::translate(glm::vec3(-1.0f, 0.0f, 0.0f));
glm::mat4 model2 = translation2 * rotation2 * scale2;
// Transforms the camera: move the camera back (z axis)
glm::mat4 view2 = glm::translate(glm::vec3(0.0f, 0.0f, -5.0f));
Cone1.Render(gWindow, gProgramId, model, view);
Cone2.Render(gWindow, gProgramId, model2, view2);
// unbind
glBindVertexArray(0);
glUseProgram(0);
glfwSwapBuffers(gWindow);
glfwPollEvents();
}
// Release shader program
UDestroyShaderProgram(gProgramId);
exit(EXIT_SUCCESS); // Terminates the program successfully
}
// Initialize GLFW, GLEW, and create a window
bool UInitialize(int argc, char* argv[], GLFWwindow** window)
{
// GLFW: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// GLFW: window creation
// ---------------------
* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE, NULL, NULL);
if (*window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return false;
}
glfwMakeContextCurrent(*window);
glfwSetFramebufferSizeCallback(*window, UResizeWindow);
// GLEW: initialize
// ----------------
// Note: if using GLEW version 1.13 or earlier
glewExperimental = GL_TRUE;
GLenum GlewInitResult = glewInit();
if (GLEW_OK != GlewInitResult)
{
std::cerr << glewGetErrorString(GlewInitResult) << std::endl;
return false;
}
// Displays GPU OpenGL version
cout << "INFO: OpenGL Version: " << glGetString(GL_VERSION) << endl;
return true;
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
void UProcessInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
void UResizeWindow(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
// Implements the UCreateShaders function
bool UCreateShaderProgram(const char* vtxShaderSource, const char* fragShaderSource, GLuint& programId)
{
// Compilation and linkage error reporting
int success = 0;
char infoLog[512];
// Create a Shader program object.
programId = glCreateProgram();
// Create the vertex and fragment shader objects
GLuint vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
// Retrive the shader source
glShaderSource(vertexShaderId, 1, &vtxShaderSource, NULL);
glShaderSource(fragmentShaderId, 1, &fragShaderSource, NULL);
// Compile the vertex shader, and print compilation errors (if any)
glCompileShader(vertexShaderId); // compile the vertex shader
// check for shader compile errors
glGetShaderiv(vertexShaderId, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShaderId, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
return false;
}
glCompileShader(fragmentShaderId); // compile the fragment shader
// check for shader compile errors
glGetShaderiv(fragmentShaderId, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShaderId, sizeof(infoLog), NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
return false;
}
// Attached compiled shaders to the shader program
glAttachShader(programId, vertexShaderId);
glAttachShader(programId, fragmentShaderId);
glLinkProgram(programId); // links the shader program
// check for linking errors
glGetProgramiv(programId, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(programId, sizeof(infoLog), NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
return false;
}
glUseProgram(programId); // Uses the shader program
return true;
}
void UDestroyShaderProgram(GLuint programId)
{
glDeleteProgram(programId);
}
// UIManager.h
#ifndef UIMANAGER_H
#define UIMANAGER_H
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream> // cout, cerr
#include <cstdlib> // EXIT_FAILURE
#include <GL/glew.h> // GLEW library
#include <GLFW/glfw3.h> // GLFW library
namespace
{
// Stores the GL data relative to a given mesh
struct GLMesh
{
GLuint vao; // Handle for the vertex array object
GLuint vbos[2]; // Handles for the vertex buffer objects
GLuint nIndices; // Number of indices of the mesh
};
}
class Cone {
public:
Cone();
void CreateMesh();
void Render(GLFWwindow* gWindow, GLuint& gProgramId, glm::mat4& modelMatrix, glm::mat4& viewMatrix);
private:
// Member variables, if any
GLMesh gMesh;
};
#endif // CONE_H
#include "Cone.h"
#include <GL/glew.h>
#include <glm/trigonometric.hpp>
// CTOR
Cone::Cone() {
}
void Cone::CreateMesh() {
const int numSlices = 50; // Increase the number of slices for smoother base
const int numVertices = numSlices + 1;
GLfloat verts[numVertices * 7]{};
// For the top vertex
verts[0] = 0.0f; // X position
verts[1] = 1.0f; // Y position
verts[2] = 0.0f; // Z position
verts[3] = 1.0f; // Red color
verts[4] = 0.0f; // Green color
verts[5] = 0.0f; // Blue color
verts[6] = 1.0f; // Alpha (transparency)
// For the other vertices
for (int i = 0; i < numSlices; ++i) {
float theta = (2.0f * 3.14159265359f * float(i)) / float(numSlices);
float x = cos(theta);
float z = sin(theta);
int vertexIndex = (i + 1) * 7;
verts[vertexIndex] = x;
verts[vertexIndex + 1] = 0.0f;
verts[vertexIndex + 2] = z;
verts[vertexIndex + 3] = 0.0f; // Set your desired color here (e.g., 0.0f for red, 1.0f for green, etc.)
verts[vertexIndex + 4] = 1.0f;
verts[vertexIndex + 5] = 0.0f;
verts[vertexIndex + 6] = 1.0f; // Alpha
}
GLushort indices[numSlices * 3];
for (int i = 0; i < numSlices; ++i) {
indices[i * 3] = 0;
indices[i * 3 + 1] = i + 1;
indices[i * 3 + 2] = (i + 1) % numSlices + 1;
}
const GLuint floatsPerVertex = 3;
const GLuint floatsPerColor = 4;
glGenVertexArrays(1, &gMesh.vao); // we can also generate multiple VAOs or buffers at the same time
glBindVertexArray(gMesh.vao);
// Create 2 buffers: first one for the vertex data; second one for the indices
glGenBuffers(2, gMesh.vbos);
glBindBuffer(GL_ARRAY_BUFFER, gMesh.vbos[0]); // Activates the buffer
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); // Sends vertex or coordinate data to the GPU
gMesh.nIndices = sizeof(indices) / sizeof(indices[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gMesh.vbos[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// Strides between vertex coordinates is 6 (x, y, z, r, g, b, a). A tightly packed stride is 0.
GLint stride = sizeof(float) * (floatsPerVertex + floatsPerColor);// The number of floats before each
// Create Vertex Attribute Pointers
glVertexAttribPointer(0, floatsPerVertex, GL_FLOAT, GL_FALSE, stride, 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, floatsPerColor, GL_FLOAT, GL_FALSE, stride, (char*)(sizeof(float) * floatsPerVertex));
glEnableVertexAttribArray(1);
}
void Cone::Render(GLFWwindow* gWindow, GLuint& gProgramId, glm::mat4& modelMatrix, glm::mat4& viewMatrix) {
// Enable z-depth
glEnable(GL_DEPTH_TEST);
// Clear the frame and z buffers
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Creates a perspective projection
glm::mat4 projection = glm::perspective(45.0f, (GLfloat)800 / (GLfloat)600, 0.1f, 100.0f);
// Set the shader to be used
glUseProgram(gProgramId);
// Retrieves and passes transform matrices to the Shader program
GLint modelLoc = glGetUniformLocation(gProgramId, "model");
GLint viewLoc = glGetUniformLocation(gProgramId, "view");
GLint projLoc = glGetUniformLocation(gProgramId, "projection");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(modelMatrix));
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(viewMatrix));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
// Activate the VBOs contained within the mesh's VAO
glBindVertexArray(gMesh.vao);
// Draws the triangles
glDrawElements(GL_TRIANGLES, gMesh.nIndices, GL_UNSIGNED_SHORT, NULL); // Draws the triangle
// Deactivate the Vertex Array Object
glBindVertexArray(0);
glfwSwapBuffers(gWindow); // Flips the the back buffer with the front buffer every frame.
}
The people "on the internet" are correct:
glfwSwapBuffersshould only be called after everything has been drawn. Swapping the buffers makes the current state visible to the user and if you do that inbetween, then half rendered images will be shown which is perceived as flickering.The reason why you then only see the second cube is that
glClearshould only be called at the beginning of the frame, not not inbetween. SinceglClearclears the back buffer, everything that has been drawn would be removed. In your code, the clearing of the screen when rendering the second cone will remove the pixels of the first one.