Related
I've made a program that displays some textures depending on the given input and can rotate, resize and move the textures depending on the given input. The program works perfectly fine but when I rotate the textures the edges appear to be pixelated, like this:
Is there a way for me to smoothen out these edges?
Here are my shaders and texture classes:
texture.vs:
#version 300 es
precision mediump float;
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
out vec3 ourColor;
out vec2 TexCoord;
uniform mat4 transform;
void main()
{
gl_Position = transform * vec4(aPos, 1.0f);
ourColor = aColor;
TexCoord = vec2(aTexCoord.x, aTexCoord.y);
}
texture.fs
#version 300 es
precision mediump float;
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
// texture sampler
uniform sampler2D texture1;
void main()
{
FragColor = texture(texture1, TexCoord);
}
Here is my texture.cpp:
Texture::Texture(int x, int y, int w, int h, int gw, int gh)
{
pos.TL_x = _VERTICIZE_X(x, gw);
pos.TL_y = -_VERTICIZE_Y(y, gh);
pos.TR_x = _VERTICIZE_X(x + w, gw);
pos.TR_y = -_VERTICIZE_Y(y, gh);
pos.BL_x = _VERTICIZE_X(x, gw);
pos.BL_y = -_VERTICIZE_Y(y + h, gh);
pos.BR_x = _VERTICIZE_X(x + w, gw);
pos.BR_y = -_VERTICIZE_Y(y + h, gh);
}
void set_max_z(int z)
{
max_z = z;
}
int Texture::init(float ang_rad, float z)
{
shader = Shader("./src/opengl/shaders/image.vs", "./src/opengl/shaders/image.fs");
this->angle = ang_rad;
// build and compile our shader zprogram
// ------------------------------------
// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float vertices[32] = {
// positions // colors // texture coords
pos.TR_x, pos.TR_y, _VERTICIZE_Z(z, max_z), 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, // top right
pos.BR_x, pos.BR_y, _VERTICIZE_Z(z, max_z), 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, // bottom right
pos.BL_x, pos.BL_y, _VERTICIZE_Z(z, max_z), 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // bottom left
pos.TL_x, pos.TL_y, _VERTICIZE_Z(z, max_z), 1.0f, 1.0f, 1.0f, 0.0f, 0.0f // top left
};
unsigned int indices[] = {
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
glEnable(GL_DEPTH_TEST);
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
// color attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// texture coord attribute
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
// load and create a texture
// -------------------------
// texture 1
// ----------
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// tell opengl for each sampler to which texture unit it belongs to (only has to be done once)
// -------------------------------------------------------------------------------------------
shader.use(); // don't forget to activate/use the shader before setting uniforms!
// either set it manually like so:
glUniform1i(glGetUniformLocation(shader.ID, "texture"), 0);
return 0;
}
void Texture::render(int w, int h, uint8_t *buffer)
{
// If having trouble doing multiple textures, add "+ index to Active Texture."
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
glGenerateMipmap(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glm::mat4 transform = glm::mat4(1.0f); // make sure to initialize matrix to identity matrix first
transform = glm::translate(transform, glm::vec3(0.0f, 0.0f, 0.0f));
transform = glm::rotate(transform, glm::radians(this->angle), glm::vec3(0.0f, 0.0f, 1.0f));
// get matrix's uniform location and set matrix
shader.use();
unsigned int transformLoc = glGetUniformLocation(shader.ID, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}
Texture::~Texture()
{
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
}
void Texture::framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
}
(_VERTICIZE just calculates points)
I'm using ubuntu, GLAD, GLFW.
This appears to be a classic example of Aliasing. We can fix this by enabling MSAA.
GLFW has an inbuilt way of doing that. Put this before you create the window:
glfwWindowHint(GLFW_SAMPLES, 4); /* if effect is not strong enough, change to 8 */
Edit -
You have to call glfwWindowHint(GLFW_SAMPLES, 4) in between glfwInit and glfwCreateWindow.
Also, in case OpenGL doesn't enable multisampling by default, do
glEnable(GL_MULTISAMPLE);
I tried to use FFmpeg to capture frames rendered by OpenGL. The result is a .mp4 file for playing back purposes. It works since I got the .mp4 I expected, however the quality is quite low compared to the one rendered by OpenGL. Can anyone tell me why? And How can I adjust my code to make the mp4 of the same quality as the original frames generated by OpenGL?
The result I've got:
images OpenGL vs FFmpeg
Here is my simple code:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
// settings
const unsigned int SCR_WIDTH = 500;
const unsigned int SCR_HEIGHT = 500;
// start ffmpeg telling it to expect raw rgba 720p-60hz frames
// -i - tells it to read frames from stdin
const char* cmd = "ffmpeg -f rawvideo -pix_fmt rgba -s 500x500 -i - "
"-threads 0 -preset fast -y -pix_fmt yuv420p -crf 21 -vf vflip output.mp4";
// open pipe to ffmpeg's stdin in binary write mode
FILE* ffmpeg = _popen(cmd, "wb");
int* buffer = new int[SCR_WIDTH*SCR_HEIGHT];
// shaders
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec3 aColor;\n"
"layout (location = 2) in vec2 aTexCoord;\n"
"out vec3 ourColor;\n"
"out vec2 texCoord;\n"
"uniform mat4 transform;\n"
"void main()\n"
"{\n"
" gl_Position = transform*vec4(aPos, 1.0);\n"
" ourColor = aColor;\n"
" texCoord = aTexCoord;\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"in vec2 texCoord;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);\n"
"}\n\0";
int main()
{
// glfw: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// render preparation: data collection and passing
// -----------------------------------------------
// vertex shader: create and compile
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// fragment shader: create and compile
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// shader program
unsigned int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// first shape: square
float vertices[] = {
// positions // colors // texture coords
0.25f, 0.25f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right
0.25f, -0.25f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom right
-0.25f, -0.25f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left
-0.25f, 0.25f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left
};
int indices[] = {
0, 1, 2,
2, 3, 0
};
unsigned int VAO, VBO, EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), 0);
glEnableVertexAttribArray(2);
// second shape: line(from the center of the screen to the center of the square
float vertices2[] = {
0.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.0f
};
unsigned int VAO2, VBO2;
glGenVertexArrays(1, &VAO2);
glGenBuffers(1, &VBO2);
glBindVertexArray(VAO2);
glBindBuffer(GL_ARRAY_BUFFER, VBO2);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2), vertices2, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
glEnableVertexAttribArray(0);
// wireframe mode
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
int frameCounter = 0;
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
if (frameCounter > 900) break;
// input
// -----
processInput(window);
// render
// ------
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// be sure to activate the shader before any calls to glUniform
glUseProgram(shaderProgram);
/**********************draw rotating line*****************************/
glm::mat4 transform = glm::mat4(1.0f);
/// rotate( around the center of the screen )
transform = glm::rotate(transform, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f));
unsigned int transLoc = glGetUniformLocation(shaderProgram, "transform");
glUniformMatrix4fv(transLoc, 1, GL_FALSE, glm::value_ptr(transform));
// draw
glBindVertexArray(VAO2);
glDrawArrays(GL_LINES, 0, 2);
/**********************draw rotating square*****************************/
transform = glm::mat4(1.0f);
/// rotate( around the center of the screen )
transform = glm::rotate(transform, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f));
//// translate
transform = glm::translate(transform, glm::vec3(0.5f, 0.5f, 0.0f));
//// rotate( self rotate)
transform = glm::rotate(transform, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f));
transLoc = glGetUniformLocation(shaderProgram, "transform");
glUniformMatrix4fv(transLoc, 1, GL_FALSE, glm::value_ptr(transform));
// draw
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
/**********************draw center squares*****************************/
transform = glm::mat4(1.0f);
float scaler = sin((float)glfwGetTime())*4;
transform = glm::scale(transform, glm::vec3(scaler, scaler, scaler));
transLoc = glGetUniformLocation(shaderProgram, "transform");
glUniformMatrix4fv(transLoc, 1, GL_FALSE, glm::value_ptr(transform));
// draw
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
/****** ffmpeg *****/
glReadPixels(0, 0, SCR_WIDTH, SCR_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
fwrite(buffer, sizeof(int)*SCR_WIDTH*SCR_HEIGHT, 1, ffmpeg);
frameCounter++;
/****** end: ffmpeg *****/
glfwPollEvents();
}
_pclose(ffmpeg);
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return 0;
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(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 framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
}
Switch to the libx264rgb encoder & crf 0 for lossless capture:
const char* cmd = "ffmpeg -framerate 60 -f rawvideo -pix_fmt rgba -s 500x500 -i - "
"-c:v libx264rgb -threads 0 -preset fast -y -crf 0 -vf vflip output.mp4";
Note that you'll want a re-encode the output before passing it off to other, less general software than ffmpeg since RGB isn't a terribly common color-space for H.264.
Be careful with your player software when checking results, MPV's --profile=gpu-hq on my Linux system introduced ringing artifacts around the lines while VLC didn't.
Can someone show show me how I can modify my code so I can draw more than one triangle?
I'm hoping to accomplish something like the picture shown here:
// Include standard headers
#include <stdio.h>
#include <stdlib.h>
// Include GLEW
#include <GL/glew.h>
// Include GLFW
#include <GLFW/glfw3.h>
GLFWwindow* window;
// Include GLM
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;
#include <common/shader.hpp>
int main(void)
{
// Initialise GLFW
if (!glfwInit())
{
fprintf(stderr, "Failed to initialize GLFW\n");
getchar();
return -1;
}
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// Open a window and create its OpenGL context
window = glfwCreateWindow(1024, 768, "Tutorial 04 - Colored Cube", NULL, NULL);
if (window == NULL) {
fprintf(stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n");
getchar();
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// Initialize GLEW
glewExperimental = true; // Needed for core profile
if (glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to initialize GLEW\n");
getchar();
glfwTerminate();
return -1;
}
// Ensure we can capture the escape key being pressed below
glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
// Dark blue background
glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
// Enable depth test
glEnable(GL_DEPTH_TEST);
// Accept fragment if it closer to the camera than the former one
glDepthFunc(GL_LESS);
GLuint VertexArrayID;
glGenVertexArrays(1, &VertexArrayID);
glBindVertexArray(VertexArrayID);
// Create and compile our GLSL program from the shaders
GLuint programID = LoadShaders("TransformVertexShader.vertexshader", "ColorFragmentShader.fragmentshader");
// Get a handle for our "MVP" uniform
GLuint MatrixID = glGetUniformLocation(programID, "MVP");
int verticeCount = 3;
static const GLfloat g_vertex_buffer_data[] = {
-0.5f, 0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
};
static const GLfloat g_color_buffer_data[] = {
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
};
GLuint vertexbuffer;
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
GLuint colorbuffer;
glGenBuffers(1, &colorbuffer);
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_color_buffer_data), g_color_buffer_data, GL_STATIC_DRAW);
do {
// Clear the screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Use our shader
glUseProgram(programID);
glm::mat4 MVP = glm::mat4(1.0);
// Send our transformation to the currently bound shader,
// in the "MVP" uniform
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glVertexAttribPointer(
0, // attribute. No particular reason for 0, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// 2nd attribute buffer : colors
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glVertexAttribPointer(
1, // attribute. No particular reason for 1, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// Draw the triangle !
glDrawArrays(GL_TRIANGLES, 0, verticeCount * 3);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
// Swap buffers
glfwSwapBuffers(window);
glfwPollEvents();
} // Check if the ESC key was pressed or the window was closed
while (glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS &&
glfwWindowShouldClose(window) == 0);
// Cleanup VBO and shader
glDeleteBuffers(1, &vertexbuffer);
glDeleteBuffers(1, &colorbuffer);
glDeleteProgram(programID);
glDeleteVertexArrays(1, &VertexArrayID);
// Close OpenGL window and terminate GLFW
glfwTerminate();
return 0;
}
It's actually right in front of your nose.
Triangles are defined by 3 vertices.
static const GLfloat g_vertex_buffer_data[] = {
-0.5f, 0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
};
Here, you initialized your vertex data array with 3, 3-dimensional vertices. That's 1 triangle.
Adding more triangles, i.e. 3 sets of 3 vertices, to this array is all you need to do, along with extending the g_color_buffer_data array and changing verticeCount accordingly.
So this:
static const GLfloat g_vertex_buffer_data[] = {
-0.50f, +0.50f, +0.00f,
+0.50f, +0.50f, +0.00f,
+0.50f, -0.50f, +0.00f,
-0.50f, -0.25f, +0.00f,
-0.50f, -0.50f, +0.00f,
-0.25f, -0.50f, +0.00f,
};
static const GLfloat g_color_buffer_data[] = {
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
};
would give you 2 triangles: 1 red, 1 blue.
You could also define verticeCount as:
int verticeCount = sizeof(g_vertex_buffer_data) / (sizeof(g_vertex_buffer_data[0]) * 3);
(Thread on finding the size of a C-style array in C++)
This way you won't manually have to change it.
You can always create a new vertex buffer, but it is usually a good idea to implement vertex and fragment shaders instead. If you choose to use shaders, you can draw the buffer, transform the vertex shader, then draw the buffer again. Fragment shaders also provide a better way of specifying color dynamically. You can read more and see some examples at https://learnopengl.com/Getting-started/Shaders
This question already has an answer here:
Why is the sprite not rendering in OpenGL?
(1 answer)
Closed 2 years ago.
I'm trying to render a square using two triangles, and then applying some transformations, but for whatever reason it isn't showing up. Here is the code:
#include <iostream>
#include <GL/glew.h>
#include <GL/freeglut.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
using namespace std;
#define WINDOW_TITLE "Modern OpenGL 3D Cube"
#ifndef GLSL
#define GLSL(Version, Source) "#version " #Version "\n" #Source
#endif
GLint shaderProgram, windowWidth = 800, windowHeight = 600;
GLuint VBO, VAO, EBO, texture;
void UResizeWindow(int, int);
void URenderGraphics(void);
void UCreateShader(void);
void UCreateBuffers(void);
const GLchar* vertexShaderSource = GLSL(330,
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color;
out vec3 mobileColor;
uniform mat4 shaderTransform;
void main() {
gl_Position = shaderTransform * vec4(position, 1.0f);
mobileColor = color;
});
const GLchar* fragmentShaderSource = GLSL(330,
in vec3 mobileColor;
out vec4 gpuColor;
void main() {
gpuColor = vec4(mobileColor, 1.0);
});
//main program
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowSize(windowWidth, windowHeight);
glutCreateWindow(WINDOW_TITLE);
glutReshapeFunc(UResizeWindow);
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
cout << "Failed to initialize GLEW" << endl;
return -1;
}
UCreateShader();
UCreateBuffers();
glUseProgram(shaderProgram);
glClearColor(0, 0, 0, 1);
glutDisplayFunc(URenderGraphics);
glutMainLoop();
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
return 0;
}
void UResizeWindow(int w, int h) {
windowWidth = w;
windowHeight = h;
glViewport(0, 0, windowWidth, windowHeight);
}
void URenderGraphics(void) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindVertexArray(VAO);
glm::mat4 currentTransform;
currentTransform = glm::translate(currentTransform, glm::vec3(0.0f, 0.5f, 0.0f));
currentTransform = glm::rotate(currentTransform, 45.0f, glm::vec3(0.0f, 0.0f, 1.0f));
currentTransform = glm::scale(currentTransform, glm::vec3(0.5f, 0.5f, 0.5f));
GLuint transformLocation = glGetUniformLocation(shaderProgram, "shaderTransform");
glUniformMatrix4fv(transformLocation, 1, GL_FALSE, glm::value_ptr(currentTransform));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glutSwapBuffers();
}
void UCreateShader() {
GLint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
GLint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
}
void UCreateBuffers() {
GLfloat vertices[] = {
0.5f, 0.5f, 0.0f, 1, 0, 0, //top right vertex 0
0.5f, -0.5f, 0.0f, 0, 1, 0, //bottom right vertex 1
-0.5f, -0.5f, 0.0f, 0, 0, 1, //bottom left vertex 2
-0.5f, 0.5f, 0.0f, 1, 0, 1 //top left vertex 3
};
GLuint indices[] = {
0, 1, 3, //triangle 1
1, 2, 3 //triangle 2
};
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
glBindVertexArray(0);
}
It compiles and runs just fine, but it only opens a blank window. No graphics are shown on that window like it normally would. The output is supposed to look like a slightly rotated square rotated about its z-axis, but nothing appears at all. All help appreciated.
Some common things I check, when I get the infamous blank screen:
forgetting to call glUseProgram(shaderID) before you start passing uniforms (done)
pass the model matrix as an identity matrix.
double checking if the VBO/EBO are laid out in memory correctly (also, simplifying them during this step, ie. removing the color attribute, just pass position, and hard code the color in the fragment shader)
doing some error checking to see if there is a problem with the shader compilation using glGetShaderInfoLog and glGetProgramInfoLog.
The model matrix variable glm::mat4 currentTransform has to be initialized by the Identity matrix.
The OpenGL Mathematics (GLM) API documentation is based on OpenGL Shading Language (GLSL) and refers to The OpenGL Shading Language specification.
5.4.2 Vector and Matrix Constructors
[...] If there is a single scalar parameter to a matrix constructor, it is used to initialize all the components on the matrix's diagonal, with the remaining components initialized to 0.0.
An Identity matrix can be initialized by the single parameter 1.0:
glm::mat4 currentTransform;
glm::mat4 currentTransform(1.0f);
Cube rendering
Hey, i think there may be a mistake in either how i'm sending the data to the vertex or the way my indices are ordered with GL_CCW
struct Vertex
{
glm::vec3 position;
glm::vec3 color;
glm::vec3 normal;
};
Vertices using Vertex struct
Vertex vertices[] =
{
/*FRONT SQUARE*/
///BOTTOM LEFT
glm::vec3(-0.5f, -0.5f, 1.f), glm::vec3(1.f, 0.f, 0.f), glm::vec3(0.f, 0.f, 1.f),
///bottom RIGHT
glm::vec3(0.5f, -0.5f, 1.0f), glm::vec3(1.f, 0.f, 0.f), glm::vec3(0.f, 0.f, 1.f),
////TOP RIGHT
glm::vec3(0.5f, 0.5f, 1.0f), glm::vec3(1.f, 0.f, 0.f), glm::vec3(0.f, 0.f, 1.f),
///TOP LEFT
glm::vec3(-0.5f, 0.5f, 1.0f), glm::vec3(1.f, 0.f, 0.f), glm::vec3(0.f, 0.f, 1.f),
///BOTTOM LEFT
glm::vec3(-0.5f, -0.5f, -1.f), glm::vec3(1.f, 0.f, 0.f), glm::vec3(0.f, 0.f, 1.f),
///bottom RIGHT
glm::vec3(0.5f, -0.5f, -1.0f), glm::vec3(1.f, 0.f, 0.f), glm::vec3(0.f, 0.f, 1.f),
////TOP RIGHT
glm::vec3(0.5f, 0.5f, -1.0f), glm::vec3(1.f, 0.f, 0.f), glm::vec3(0.f, 0.f, 1.f),
///TOP LEFT
glm::vec3(-0.5f, 0.5f, -1.0f), glm::vec3(1.f, 0.f, 0.f), glm::vec3(0.f, 0.f, 1.f)
};
unsigned noOfVertices = sizeof(vertices) / sizeof(Vertex);
This is my indices ordering
GLuint indices[] =
{
// /front face
0, 1, 2,
2, 3, 0,
// right
1, 5, 6,
6, 2, 1,
// back
7, 6, 5,
5, 4, 7,
// left
4, 0, 3,
3, 7, 4,
// bottom
4, 5, 1,
1, 0, 4,
// top
3, 2, 6,
6, 7, 3
};
unsigned noOfIndices = sizeof(indices) / sizeof(GLuint);
Main function and initliase the window
int main()
{
Initiase window
///initialise GLFW for Window
if (glfwInit() == GLFW_FALSE)
{
std::cout << "ERROR::GLFW-INTI::FAILED" << "\n";
glfwTerminate();
}
///Create window with functions
GLFWwindow* window;
const int window_height = 480;
int window_width = 680;
int framebuffer_height = window_height;
int framebuffer_width = window_width;
char* title = "mytutorial";
int GLverMAJ = 3;
int GLverMin = 3;
///sets how the window should be drawn and which hints
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, GLverMAJ);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, GLverMin);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
///create the window with the previously set options
window = glfwCreateWindow(window_width, window_height, title, NULL, NULL);
//checks if window created
if (window == nullptr)
{
std::cout << "ERROR:::GLFWCREATEWINDOWFAILED" << "\n";
}
///sets the frame buffer size
glfwGetFramebufferSize(window, &framebuffer_width, &framebuffer_height);
glfwMakeContextCurrent(window);
//need to read on this part
glewExperimental = GL_TRUE;
//initiliase the glew
if (glewInit() != GLEW_OK)
{
std::cout << "ERROR::GLEWINIT::FAILED" << "\n";
}
initiliase opengl options
///enable functions first
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glEnable(GL_CULL_FACE);
//initialise the enabled functions
glCullFace(GL_BACK);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthFunc(GL_LESS);
///set the way its draw
glFrontFace(GL_CCW);
///set the polygon mode and fill ////Set as GL_LINE TO LOOK AT CUBE MESH
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
This is where i initialise the matrices the view, project and model
///initialise the matrices
///view
///front, position, up then use in lookat
glm::vec3 cameraPos(0.f, 0.f, 1.f);
glm::vec3 cameraUp(0.f, 1.f, 0.f);
glm::vec3 cameraFront(0.f, 0.f, -0.1f);
glm::mat4 ViewMatrix(1.f);
ViewMatrix = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
///set up prespective projection
float fov = 90.f;
float nearPlane = -1.f;
float farPlane = 1000.f;
///projection
glm::mat4 ProjectionMatrix(1.f);
ProjectionMatrix = glm::perspective(glm::radians(fov), static_cast<float>(framebuffer_width / framebuffer_height), nearPlane, farPlane);
//Model Matrix
glm::mat4 ModelMatrix(1.f);
Then i create my shaders
///set up SHADERS
char infolog[512];
GLint success;
GLuint Vertexshader = glCreateShader(GL_VERTEX_SHADER);
///std::string str_src =
std::string temp = "";
std::string src = "";
std::ifstream infile;
infile.open("vert.glsl");
if (infile.is_open())
{
while (std::getline(infile, temp))
src += temp + "\n";
}
infile.close();
const GLchar* source = src.c_str();
///link created shader with shader source
glShaderSource(Vertexshader, 1, &source, NULL);
glCompileShader(Vertexshader);
///error check compilation status
glGetShaderiv(Vertexshader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(Vertexshader, 512, NULL, infolog);
std::cout << infolog;
}
success = 0;
GLuint FragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
///std::string str_src =
temp = "";
src = "";
infile.open("frag.glsl");
if (infile.is_open())
{
while (std::getline(infile, temp))
src += temp + "\n";
}
infile.close();
source = src.c_str();
///link created shader with shader source
glShaderSource(FragmentShader, 1, &source, NULL);
glCompileShader(FragmentShader);
///error check compilation status
glGetShaderiv(FragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(FragmentShader, 512, NULL, infolog);
std::cout << infolog;
}
Link the created shaders with the program
///create and link the program
success = 0;
GLuint programID;
programID = glCreateProgram();
glAttachShader(programID, Vertexshader);
glAttachShader(programID, FragmentShader);
glLinkProgram(programID);
glGetProgramiv(programID, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(programID, 512, NULL, infolog);
std::cout << "ERROR::SHADER::COULD_NOT_LINK_PROGRAM" << "\n";
std::cout << infolog << "\n";
}
after linking unuse and delete buffers
///after linking we unuse the program and delete the shaders
glUseProgram(0);
glDeleteShader(Vertexshader);
glDeleteShader(FragmentShader);
This is where i initiliase VAO VBO AND EBO
///vbo, ebo and vertex array
GLuint VAO;
GLuint VBO;
GLuint EBO;
//gen and bind vao
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
///gen vbo
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, noOfVertices * sizeof(Vertex), vertices, GL_STATIC_DRAW);
///gen EBO
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, noOfIndices * sizeof(GLuint), indices, GL_STATIC_DRAW);
This is the part where i feel like there may be a problem
///tell buffer where data is located
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, position));
glEnableVertexAttribArray(0);
//colour
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, color));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, normal));
glEnableVertexAttribArray(2);
glBindVertexArray(0);
///use program send in uniforms to the shader.
glUseProgram(programID);
///GLuint s = glGetUniformLocation(programID, "ViewMatrix");
GLuint s = glGetUniformLocation(programID, "ModelMatrix");
glUniformMatrix4fv(s, 1, GL_FALSE, glm::value_ptr(ModelMatrix));
s = glGetUniformLocation(programID, "ProjectionMatrix");
glUniformMatrix4fv(s, 1, GL_FALSE, glm::value_ptr(ProjectionMatrix));
s = glGetUniformLocation(programID, "ViewMatrix");
glUniformMatrix4fv(s, 1, GL_FALSE, glm::value_ptr(ViewMatrix));
glUseProgram(0);
///main while loop
while (!glfwWindowShouldClose(window))
{
//clear
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
///draw
glfwPollEvents();
glUseProgram(programID);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, noOfIndices, GL_UNSIGNED_INT, 0);
///flush
glfwSwapBuffers(window);
glFlush();
///unuse
glBindVertexArray(0);
glUseProgram(0);
}
return 0;
}
Both the near plane and the far plane have to be positive values for glm::perspective and static_cast<float>(framebuffer_width / framebuffer_height) would be an integral division:
float fov = 90.f;
float nearPlane = 0.1f; // 0.1 instead of -1.0
float farPlane = 1000.f;
glm::mat4 ProjectionMatrix(1.f);
ProjectionMatrix = glm::perspective(glm::radians(fov),
static_cast<float>(framebuffer_width) / static_cast<float>(framebuffer_height),
nearPlane, farPlane);
See glm::perspective:
GLM_FUNC_DECL mat<4, 4, T, defaultp> glm::perspective(
T fovy,
T aspect,
T near,
T far
)
[...]
near Specifies the distance from the viewer to the near clipping plane (always positive).
far Specifies the distance from the viewer to the far clipping plane (always positive).
The glm library provides matrix operations related to OpenGL and GLSL. The glm API documentation refers to The OpenGL Shading Language specification 4.20.
See The OpenGL Shading Language 4.6, 5.4.2 Vector and Matrix Constructors, page 101:
To initialize a matrix by specifying vectors or scalars, the components are assigned to the matrix elements in column-major order.
mat4(float, float, float, float, // first column
float, float, float, float, // second column
float, float, float, float, // third column
float, float, float, float); // fourth column
This means the transformation of the vertex coordinate in the vertex shader has to be:
gl_Position = ProjectionMatrix * ViewMatrix * ModelMatrix * vec4(vertex_position,1.0);
See also GLSL Programming/Vector and Matrix Operations:
Likely the matrix can be transposed when set to the uniform, by setting the 3rd paramter of glUniformMatrix4fv to GL_TRUE:
GLuint s = glGetUniformLocation(programID, "ModelMatrix");
glUniformMatrix4fv(s, 1, GL_TRUE, glm::value_ptr(ModelMatrix));
s = glGetUniformLocation(programID, "ProjectionMatrix");
glUniformMatrix4fv(s, 1, GL_TRUE, glm::value_ptr(ProjectionMatrix));
s = glGetUniformLocation(programID, "ViewMatrix");
glUniformMatrix4fv(s, 1, GL_TRUE, glm::value_ptr(ViewMatrix));
And compute the vertex coordinate transformation in the reverse order:
gl_Position = vec4(vertex_position,1.0) * ModelMatrix * ViewMatrix * ProjectionMatrix;
Further, the winding order of the faces of the cube a is clockwise, so it has to be:
glFrontFace(GL_CCW);
glFrontFace(GL_CW);
Change the camera position to
glm::vec3 cameraPos(0.f, 0.f, 2.f);
and you'll get the following result:
To me, it would seem that your program is working correctly. If I'm not mistaken, then your camera is located inside the cube, looking straight from the position of one of the faces at the face on the other side. What you're seeing is exactly what one would expect to see in this case. In the center of the image you have the two triangles that make the backside of the face you're looking out of. The "weird lines" you get are simply the edges of the triangles that make the other faces. Since your camera is inside the cube, these faces necessarily reach all the way behind your camera and everything gets cut off at the near plane, which is where you get the outer rectangle from that "encloses" everything.
Everything is a bit squashed because this
static_cast<float>(framebuffer_width / framebuffer_height)
will not have the effect you most likely intend it to have. You are still performing an integer division here.
Also, I'm not sure what exactly you expect the effect of using a negative value for the near plane to be. Most likely, it's also not what you actually want, I suggest you use some positive value, e.g., 0.1f there… 😉