I am following this tutorial: https://learnopengl.com/Getting-started/Textures to create a function where I can call it in my glfw loop in the main function to display multiple squares with different textures by calling this function per square to create.
Draw.h
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <stb_image.h>
#include <shader_s.h>
#include <iostream>
int width = 860;
int height = 640;
class Draw {
public:
int drawErr = 0;
void Square(int x, int y, int z, int w, int h, const char* file, bool usingTexture, bool flipImage){
Shader ourShader("vertex.vs", "fragment.fs");
if (!usingTexture) {
//draw polygon with no texture
}
else {
//shouldnt be in loop to draw
float vertices[] = {
//position //color //texCoords
x+w,y,z, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, //top right
x+w,y+h,z, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,//bottom right
x,y+h,z, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, //bottom left
x,y,z, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f//top left
};
unsigned int indices[] = {
0, 1, 3,
1, 2, 3
};
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);
//position
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
//color
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
//texture
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
//load Texture
stbi_set_flip_vertically_on_load(flipImage);
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
int width, height, nrChannels;
unsigned char* data = stbi_load(file, &width, &height, &nrChannels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cout << "Failed to load texture!" << std::endl;
glfwTerminate();
drawErr = -1;
}
stbi_image_free(data);
//should be in loop to draw
ourShader.use();
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}
}
//other drawing functions here
};
Shader.h
#ifndef SHADER_H
#define SHADER_H
#include <gl/glew.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
class Shader
{
public:
unsigned int ID;
// constructor generates the shader on the fly
// ------------------------------------------------------------------------
Shader(const char* vertexPath, const char* fragmentPath)
{
// 1. retrieve the vertex/fragment source code from filePath
std::string vertexCode;
std::string fragmentCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
// ensure ifstream objects can throw exceptions:
vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try
{
// open files
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
// read file's buffer contents into streams
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
// close file handlers
vShaderFile.close();
fShaderFile.close();
// convert stream into string
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
}
catch (std::ifstream::failure e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
const char* vShaderCode = vertexCode.c_str();
const char* fShaderCode = fragmentCode.c_str();
// 2. compile shaders
unsigned int vertex, fragment;
// vertex shader
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
// fragment Shader
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
// shader Program
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
// delete the shaders as they're linked into our program now and no longer necessary
glDeleteShader(vertex);
glDeleteShader(fragment);
}
// activate the shader
// ------------------------------------------------------------------------
void use()
{
glUseProgram(ID);
}
// utility uniform functions
// ------------------------------------------------------------------------
void setBool(const std::string& name, bool value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
// ------------------------------------------------------------------------
void setInt(const std::string& name, int value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}
// ------------------------------------------------------------------------
void setFloat(const std::string& name, float value) const
{
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}
private:
// utility function for checking shader compilation/linking errors.
// ------------------------------------------------------------------------
void checkCompileErrors(unsigned int shader, std::string type)
{
int success;
char infoLog[1024];
if (type != "PROGRAM")
{
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
else
{
glGetProgramiv(shader, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
}
};
#endif
Main function:
int main() {
Draw draw;
if (!glfwInit()) {
std::cout << "failed to load GLFW" << std::endl;
return -1;
}
GLFWwindow* window = glfwCreateWindow(width, height, "Electrocraft", NULL, NULL);
glfwMakeContextCurrent(window);
GLenum err = glewInit();
if (GLEW_OK != err) {
std::cerr << "Error: " << glewGetErrorString(err) << std::endl;
}
std::cerr << "Status: Using GLEW " << glewGetString(GLEW_VERSION) << std::endl;
while (!glfwWindowShouldClose(window)) {
glClearColor(68.0f / 255.0f, 85.0f / 255.0f, 255.0f / 255.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
draw.Polygon(-1.0f, 1.0f, 0.0f, 2.0f, 2.0f, "Library\\Textures\\blocks\\dirt.bmp", true, false);
glfwSwapBuffers(window);
glfwPollEvents();
}
}
Vertex.vs
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
out vec3 ourColor;
out vec2 TexCoord;
void main(){
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
TexCoord = aTexCoord;
}
Fragment.vs
#version 330 core
out vec4 fragColor;
in vec3 ourColor;
in vec2 TexCoord;
uniform sampler2D ourTexture;
void main(){
fragColor = texture(ourTexture, TexCoord);
}
The issue with my program is that it constantly reeloads the texture and rebinds it using a lot of memory to do so. I have tried to do it this way so I can call the square function and draw a multiple squares with different textures at the same time. I cannot figure out a way to reduce the memory and using the function to have multiple squares.
A simple improvement is to split your draw::Polygon method in two parts:
one generic function that loads a texture from disk and returns a Texture object that wraps the OpenGL texture ID
rewrite Polygon to accept such a Texture object.
See for example the sf::Texture class from SFML and how it combines with the sf::Shape class.
You want to create the shader once (not every time you draw a square!) and you want to load the texture once (not every time you draw a square!)
One very simple way to do this would be to make the OpenGL texture a global variable:
// put this outside of any function, and delete it from Square
unsigned int texture;
// put this in main and delete it from Square
stbi_set_flip_vertically_on_load(flipImage);
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
int width, height, nrChannels;
unsigned char* data = stbi_load(file, &width, &height, &nrChannels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cout << "Failed to load texture!" << std::endl;
glfwTerminate();
drawErr = -1;
}
stbi_image_free(data);
// put this in Square
glBindTexture(GL_TEXTURE_2D, texture);
This is the simplest way to load the texture once.
But then how can you have more than one texture?
Well, you could have more than one global variable (or put them in the Draw class if you want. It doesn't really matter for this program). dirtTexture, snowTexture, grassTexture. If you do this, you should take the code that I told you to put in main, and delete it from main, and create a loadTexture function which returns the texture ID. Then you can use dirtTexture = loadTexture("dirt.jpg"); snowTexture = loadTexture("snow.jpg"); and so on.
You have the same mistake for your shader, vertex array and buffer. For the shader, you can't make it a global variable, because then it will try to create the shader when the program starts up, before main runs, before glfwInitialize is called and then it will crash because you didn't initialize OpenGL yet. If you make it so the constructor doesn't load the shader, and add a function like LoadShader which loads the shader, then you can make it a global variable.
For the vertex array, same thing. Create it in main. Store the ID in a global variable. Bind the same vertex array over and over.
For the vertex buffer, same thing. But note that you do need to set new data every Square is called, because the squares are different. You can't really get around that. But you don't need to create a new vertex buffer every time Square is called, you only need to put new data into the same buffer. It shouldn't be very slow.
Related
I'm having issues rendering a 2D object in the 3D space in OpenGL. Whenever I compile my code I get a purple background, which is what I set it to. However, I do not see the object that I should be able to see. I think it's something wrong with the vertex shader, however, I am not completely sure.
Here is my code:
#pragma once
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <vector>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#ifndef STBI_INCLUDE_STB_IMAGE_H
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#endif
#include "Shader.h"
class Render3D {
private:
GLFWwindow* window;
const int windowWidth = 1920;
const int windowHeigth = 1080;
Render3D(Render3D& render){}
Render3D operator=(Render3D& render) {}
void processInput(GLFWwindow* window) {
}
void privateInit() {
if (!glfwInit()) {
throw(-1);
std::cout << "Error: GLFW Init Failed" << std::endl;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
window = glfwCreateWindow(windowWidth, windowHeigth, "HAHAH BRRR", NULL, NULL);
if (window == NULL) {
std::cout << "Failed to create GLFW Window \n";
glfwTerminate();
throw (-1);
}
glfwMakeContextCurrent(window);
glewExperimental = true;
if (glewInit() != GLEW_OK) {
std::cout << "GLEW INIT FAILED\n";
exit(1);
}
//The first two parameters set the position of the lower left corner
glViewport(0, 0, windowWidth, windowHeigth);
}
void render() {
float positions[] = {
// positions // colors // texture
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left
};
unsigned int indices[] = {
0, 1, 3,
1, 2, 3
};
const char* vertexShader =
"#version 330 core\n"
"\n"
"layout (location = 0) vec3 aPos;\n"
"layout (location = 1) vec3 aColors;\n"
"layout (location = 2) vec2 aTex;\n"
"\n"
"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"
"\n"
"out vec3 Colors;\n"
"out vec2 TextureCoords;\n"
"\n"
"void main(){\n"
"\n"
"gl_Position = projection * view * model * vec4(aPos, 1.0f);\n"
"Colors = aColors;\n"
"TextureCoords = aTex;\n"
"\n"
"}\0";
const char* fragmentShader =
"#version 330 core\n"
"\n"
"out vec4 ourFrag;\n"
"\n"
"in vec3 Colors;\n"
"in vec2 TextureCoords;\n"
"\n"
"uniform sampler2D texture1;\n"
"uniform sampler2D texture2;\n"
"\n"
"void main(){\n"
"\n"
"ourFrag = mix(texture(texture1, TextureCoords), texture(texture2, TextureCoords), 0.2) * vec4(Colors, 1.0f);;\n"
"\n"
"\n"
"}\0";
unsigned int VAO, VBO, EBO;
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, 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), (void*)(0));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
glBindVertexArray(0);
unsigned int Texture1, Texture2;
glGenTextures(1, &Texture1);
glGenTextures(1, &Texture2);
glBindTexture(GL_TEXTURE_2D, Texture1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
int width, height, nrChannels;
unsigned char* data = stbi_load("src/Images/woodblock.jpg", &width, &height, &nrChannels, 4);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cout << "TEXTURE FAILED:";
std::cout << stbi_failure_reason() << std::endl;
}
glBindTexture(GL_TEXTURE_2D, Texture2);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
stbi_set_flip_vertically_on_load(1);
data = stbi_load("src/Images/awesomeface.png", &width, &height, &nrChannels, 4);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cout << "TEXTURE FAILED:";
std::cout << stbi_failure_reason() << std::endl;
}
Shader render3D_Program(vertexShader, fragmentShader, 1);
render3D_Program.use();
glUniform1i(glGetUniformLocation(render3D_Program.ID, "texture1"), 0);
glUniform1i(glGetUniformLocation(render3D_Program.ID, "texture2"), 1);
while (!glfwWindowShouldClose(window)) {
processInput(window);
glClearColor(0.5f, 0.2f, 0.6f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, Texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, Texture2);
render3D_Program.use();
glm::mat4 model = glm::mat4(1.0f);
glm::mat4 view1 = glm::mat4(1.0f);
glm::mat4 projection = glm::mat4(1.0f);
model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f));
view1 = glm::translate(view1, glm::vec3(0.0f, 0.0f, -3.0f));
projection = glm::perspective(glm::radians(45.0f), (float)windowWidth / (float)windowHeigth, 0.1f, 100.0f);
glUniformMatrix4fv(glGetUniformLocation(render3D_Program.ID, "model"), 1, GL_FALSE, value_ptr(model));
glUniformMatrix4fv(glGetUniformLocation(render3D_Program.ID, "view"), 1, GL_FALSE, value_ptr(view1));
glUniformMatrix4fv(glGetUniformLocation(render3D_Program.ID, "projection"), 1, GL_FALSE, value_ptr(projection));
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &VBO);
glfwTerminate();
}
public:
Render3D(){}
void Init() {
privateInit();
}
void Run() {
render();
}
};
Additionally, here is the Shader class:
#ifndef SHADER_H
#define SHADER_H
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
class Shader
{
public:
unsigned int ID;
// constructor generates the shader on the fly
// ------------------------------------------------------------------------
Shader(const char* vertexPath, const char* fragmentPath)
{
// 1. retrieve the vertex/fragment source code from filePath
std::string vertexCode;
std::string fragmentCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
// ensure ifstream objects can throw exceptions:
vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try
{
// open files
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
// read file's buffer contents into streams
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
// close file handlers
vShaderFile.close();
fShaderFile.close();
// convert stream into string
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
}
catch (std::ifstream::failure& e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
const char* vShaderCode = vertexCode.c_str();
const char* fShaderCode = fragmentCode.c_str();
// 2. compile shaders
unsigned int vertex, fragment;
// vertex shader
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
// fragment Shader
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
// shader Program
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
// delete the shaders as they're linked into our program now and no longer necessary
glDeleteShader(vertex);
glDeleteShader(fragment);
}
Shader(const char* VertexShader, const char* FragmentShader, bool Specifier) {
unsigned int vertex = glCreateShader(GL_VERTEX_SHADER);
unsigned int fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vertex, 1, &VertexShader, NULL);
glShaderSource(fragment, 1, &FragmentShader, NULL);
glCompileShader(vertex);
int successVertex;
char infoLogVertex[512];
glGetShaderiv(vertex, GL_COMPILE_STATUS, &successVertex);
if (!successVertex) {
glGetShaderInfoLog(vertex, 512, NULL, infoLogVertex);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLogVertex << std::endl;
}
glCompileShader(fragment);
int successFragment;
glGetShaderiv(fragment, GL_COMPILE_STATUS, &successFragment);
if (!successFragment){
glGetShaderInfoLog(fragment, 512, NULL, infoLogVertex);
std::cout << "ERROR::SHADER:FRAGMENT::COMPILATION_FAILED\n" << infoLogVertex << std::endl;
}
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
glDeleteShader(vertex);
glDeleteShader(fragment);
}
// activate the shader
// ------------------------------------------------------------------------
void use()
{
glUseProgram(ID);
}
// utility uniform functions
// ------------------------------------------------------------------------
void setBool(const std::string& name, bool value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
// ------------------------------------------------------------------------
void setInt(const std::string& name, int value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}
// ------------------------------------------------------------------------
void setFloat(const std::string& name, float value) const
{
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}
private:
// utility function for checking shader compilation/linking errors.
// ------------------------------------------------------------------------
void checkCompileErrors(unsigned int shader, std::string type)
{
int success;
char infoLog[1024];
if (type != "PROGRAM")
{
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
else
{
glGetProgramiv(shader, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
}
};
#endif
Your vertex shader fails to compiled because you missed the in type qualifier for the vertex shader inputs (attributes):
layout (location = 0) vec3 aPos;
layout (location = 1) vec3 aColors;
layout (location = 2) vec2 aTex;
It has to be:
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColors;
layout (location = 2) in vec2 aTex;
I am trying to implement polygon selection through clicking by first drawing triangle IDs to an off-screen framebuffer and then reading back pixel values at clicked positions via glReadPixels. I am passing ID as unsigned integer to each vertex (and I confirmed that the buffer is correct from apitrace) and outputting it as uvec4 in fragment shader. I set up the framebuffer as RGBA8UI texture (also confirmed units to be correct from apitrace). There is no opengl error and also checked that framebuffer is complete.
The problem is that the output image where the IDs should be always has a value of 255. The area covered by the triangles are modified from the glClear value but they are not (id, 0, 0, 0) but always (255, 0, 0, 0). The exception is those with ID of 0. It seems like somewhere in the shader, the ID is converted to 255 if the ID is not 0. Is this the expected behavior from the code below? Am I doing something wrong?
Vertex buffer:
x (float), y (float), z (float), tx (float), ty (float), id (unsigned int)
Vertex shader:
#version 330 core
// Input
layout(location = 0) in vec3 position;
layout(location = 1) in vec2 texCoord;
layout(location = 2) in uint id;
// Output (Varying)
out vec2 v_texCoord;
flat out uint v_id;
// Uniform
uniform mat4 u_model;
uniform mat4 u_view;
uniform mat4 u_projection;
void main()
{
v_texCoord = texCoord;
v_id = id;
gl_Position = u_projection * u_view * u_model * vec4(position, 1.0);
}
Fragment shader:
#version 330 core
// Input (Varying)
in vec2 v_texCoord;
flat in uint v_id;
// Output
layout(location = 0) out uvec4 color;
void main()
{
color = uvec4(v_id, 0, 0, 0);
}
GL_VERSION is 3.3.0 NVIDIA 419.35 and I have updated the driver yesterday.
-- Edit --
I was down-voted for lack of information so I created a separate project that just shows my point above with the rest of the code below:
#include <glad/glad.h> // Must be included before GLFW header
#include <GLFW/glfw3.h>
#include <iostream>
#include <vector>
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 800;
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);
// glfw window creation
GLFWwindow* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// glad: load all OpenGL function pointers
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
std::cout << glGetString(GL_VERSION) << std::endl;
// Vertex and fragment shaders
GLuint shader = glCreateProgram();
{
GLint isSuccess = false;
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
// Vertex shader
{
const GLchar* vertexShaderSource =
"#version 330 core\n"
"layout(location = 0) in vec2 position;\n"
"layout(location = 1) in uint id;\n"
"flat out uint v_id;\n"
"void main() {v_id = id; gl_Position = vec4(position.x, position.y, 0.0, 1.0);}\n";
glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
glCompileShader(vertexShader);
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &isSuccess);
std::cout << "Vertex shader compile status: " << isSuccess << std::endl;
}
// Fragment shader
{
const GLchar* fragmentShaderSource =
"#version 330 core\n"
"layout(location = 0) out uvec4 color;\n"
"flat in uint v_id;\n"
"void main() {color = uvec4(v_id, 0, 0, 0);}\n";
glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &isSuccess);
std::cout << "Fragment shader compile status: " << isSuccess << std::endl;
}
glAttachShader(shader, vertexShader);
glAttachShader(shader, fragmentShader);
glLinkProgram(shader);
glGetProgramiv(shader, GL_LINK_STATUS, &isSuccess);
std::cout << "Shader link status: " << isSuccess << std::endl;
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
}
// Vertex Buffer
GLuint vertexBuffer;
{
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
GLfloat data[] = {
// x y id
-1.0f, 0.0f, 0.0f,
-1.0f, -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
};
GLuint* data2 = ((GLuint *)data);
data2[2] = 0;
data2[5] = 0;
data2[8] = 0;
data2[11] = 1;
data2[14] = 1;
data2[17] = 1;
std::cout << "Size of GLuint: " << sizeof(GLuint) << std::endl;
std::cout << "Size of GLfloat: " << sizeof(GLfloat) << std::endl;
std::cout << "Size of vertex buffer: " << sizeof(data) << std::endl;
glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
// Vertex Array
GLuint vertexArray;
{
glGenVertexArrays(1, &vertexArray);
glBindVertexArray(vertexArray);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 1, GL_UNSIGNED_INT, GL_FALSE, 3 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
// Texture for framebuffer
GLuint texture;
glGenTextures(1, &texture);
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, WINDOW_WIDTH, WINDOW_HEIGHT, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, nullptr);
glBindTexture(GL_TEXTURE_2D, 0);
}
// Framebuffer
GLuint framebuffer;
{
GLenum completenessStatus;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
std::cout << "Framebuffer status: " << (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) << std::endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
// Clear
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
GLenum error = glGetError();
std::cout << "No error: " << (error == GL_NO_ERROR) << std::endl;
// Draw
while (!glfwWindowShouldClose(window))
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
glClear(GL_COLOR_BUFFER_BIT);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
{
glDisable(GL_DITHER);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shader);
glBindVertexArray(vertexArray);
glActiveTexture(GL_TEXTURE0);
glDrawArrays(GL_TRIANGLES, 0, 6);
glEnable(GL_DITHER);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteProgram(shader);
glDeleteBuffers(1, &vertexBuffer);
glDeleteVertexArrays(1, &vertexArray);
glDeleteFramebuffers(1, &framebuffer);
glDeleteTextures(1, &texture);
return 0;
}
Output:
3.3.0 NVIDIA 419.35
Vertex shader compile status: 1
Fragment shader compile status: 1
Shader link status: 1
Size of GLuint: 4
Size of GLfloat: 4
Size of vertex buffer: 72
Framebuffer status: 1
No error: 1
Framebuffer is RGBA8UI:
Vertices are correct:
Triangle with ID of 0 is colored as (0, 0, 0, 0) as expected:
Area outside triangle is (255, 255, 255, 255) as expected (glClearColor is white):
Triangle with ID of 1 is colored as (255, 0, 0, 0). It should be (1, 0, 0, 0):
The same issue occurs for ID > 1. Why is this the case? How can I make it so that the color is (ID, 0, 0, 0) as shown in the fragment shader?
You have to use glVertexAttribIPointer (focus on I), when defining the array of generic vertex attribute data, for the vertex attribute in uint id;.
When vertex attribute data are defined by glVertexAttribPointer, then they will be converted to floating point values.
See OpenGL 4.6 API Core Profile Specification; 10.2. CURRENT VERTEX ATTRIBUTE VALUES; page 344
The VertexAttribI* commands specify signed or unsigned fixed-point values
that are stored as signed or unsigned integers, respectively. Such values are referred to as pure integers.
...
All other VertexAttrib* commands specify values that are converted directly to the internal floating-point representation.
I'm new to openGL so I'm testing some things out based on the tutorials from learnopengl.com
I was learning about textures so I decided to try some things out. This is my code.
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include "Shader.h"
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 400;
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); // uncomment this statement to fix compilation on OS X
#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;
}
// build and compile our shader zprogram
// ------------------------------------
Shader ourShader("DVDVertexShader.txt", "DVDFragmentShader.txt");
// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float vertices[] = {
// positions // colors // texture coords
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left
};
unsigned int indices[] = {
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
unsigned int VBO, VAO, 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);
// 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
// -------------------------
unsigned int texture;
// texture
// ---------
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// load image, create texture and generate mipmaps
int width, height, nrChannels;
stbi_set_flip_vertically_on_load(true);
unsigned char *data = stbi_load("dvd_logo2.png", &width, &height, &nrChannels, 0);
std::cout << "width and height is " << width << ", " << height << std::endl;
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
glBindTexture(GL_TEXTURE_2D, 0);
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
// -----
processInput(window);
// render
// ------
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// render container
ourShader.use();
glBindTexture(GL_TEXTURE_2D, texture);
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);
glfwPollEvents();
}
// optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
// 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)
{
glViewport(0, 0, width, height);
}
Note that stb_image.h is a header file that allows us to load images from any format. It's an open source code that's available in github.
https://github.com/nothings/stb/blob/master/stb_image.h
This is the Shader.h code just in case.
#ifndef SHADER_H
#define SHADER_H
#include <glad/glad.h>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include "Shader.h"
class Shader
{
public:
unsigned int ID;
// constructor generates the shader on the fly
// ------------------------------------------------------------------------
Shader(const char* vertexPath, const char* fragmentPath)
{
// 1. retrieve the vertex/fragment source code from filePath
std::string vertexCode;
std::string fragmentCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
// ensure ifstream objects can throw exceptions:
vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try
{
// open files
std::cout << "vertexPath is " << vertexPath<<std::endl;
std::cout << "fragmentPath is " << fragmentPath<<std::endl;
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
// read file's buffer contents into streams
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
// close file handlers
vShaderFile.close();
fShaderFile.close();
// convert stream into string
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
}
catch (std::ifstream::failure e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
const char* vShaderCode = vertexCode.c_str();
const char * fShaderCode = fragmentCode.c_str();
// 2. compile shaders
unsigned int vertex, fragment;
int success;
char infoLog[512];
// vertex shader
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
// fragment Shader
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
// shader Program
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
// delete the shaders as they're linked into our program now and no longer necessary
glDeleteShader(vertex);
glDeleteShader(fragment);
}
// activate the shader
// ------------------------------------------------------------------------
void use()
{
glUseProgram(ID);
}
// utility uniform functions
// ------------------------------------------------------------------------
void setBool(const std::string &name, bool value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
// ------------------------------------------------------------------------
void setInt(const std::string &name, int value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}
// ------------------------------------------------------------------------
void setFloat(const std::string &name, float value) const
{
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}
private:
// utility function for checking shader compilation/linking errors.
// ------------------------------------------------------------------------
void checkCompileErrors(unsigned int shader, std::string type)
{
int success;
char infoLog[1024];
if (type != "PROGRAM")
{
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
else
{
glGetProgramiv(shader, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
}
};
#endif
My vertex and fragment shader are very basic, so I don't know if these are the reasons for my weird output, but here it is.
DVDVertexShader.txt :
#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aColor;
layout(location = 2) in vec2 aTexCoord;
out vec3 ourColor;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
TexCoord = aTexCoord;
}
DVDFragmentShader.txt :
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
uniform sampler2D ourTexture;
void main()
{
FragColor = texture(ourTexture, TexCoord);
}
Anyway, I really think the shader isn't really the problem. The image "dvd_logo2.png" is this one.
dvd_logo2.png
However, the output I get from my code is this.
weird_dvd_output
(My original intention was to make the output have the same look as the input so I could do sth to it afterwards like make it move around per frame.)
I don't understand why this is happening... The funny thing is, other images with all kinds of colors work perfectly fine. Maybe the problem is because the photo's black and white..?
Any help and advice would be greatly appreciated. Thanks in advance!
Since it's *.png you need change at last format to GL_RGBA and by format I don't mean internalFormat, so something like that should work glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);. For what I mean by format and internalFormat check reference: glTexImage2D
If you still can't understand why it is then let me make quote from that reference:
format
Specifies the format of the pixel data.
So your pixel data have GL_RGBA value: red, green, blue, and alpha since it's *.png, problem is that now you pass only 3 of them.
EDIT:
I don't note it first time but you need also change in stbi_load number of components to 4:
stbi_load("dvd_logo2.png", &width, &height, &nrChannels, 4);
I get a black screen after trying to get a simple triangle on OpenGL with the SDL2...
I actually initialize the window with OpenGL and start GLEW with an object name as "EngineOpenGL" : http://pastebin.com/S4YDgY45
Then I start a scene which supposed to rendering the triangle :
"SceneOpenGL" : http://pastebin.com/GD4f5UDj
The main file :
#include <SDL.h>
#include <iostream>
#include <GL\glew.h>
#include "EngineOpenGl.h"
// DEBUG_MemoryLeaks
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
// _DEBUG
#include "SDLInitVideoException.h"
int main(int argc, char **argv) {
_CrtDumpMemoryLeaks();
EngineOpenGL * scene = new EngineOpenGL("OpenGL", 720, 1280);
try {
scene->init();
} catch (const SDLInitVideoException &e) {
delete scene;
std::cout << "Error while initialising SDL" << std::endl;
} catch (const SDLCreateWindowException &e) {
delete scene;
std::cout << "Error while initialising the window" << std::endl;
} catch (const SDLCreateContextGLException &e) {
delete scene;
std::cout << "Error while creating the contextGL" << std::endl;
} catch (const GLEWInitException &e) {
delete scene;
std::cout << "Error while initialising " << std::endl;
} catch (const SceneOpenGLException &e) {
delete scene;
std::cout << "Error while starting the scene" << std::endl;
}
scene->start();
delete scene;
// DEBUG_MemoryLeaks
_CrtDumpMemoryLeaks();
// _DEBUG
return 0;
}
FPS show the fps in the console and Input gets the user input.
Edit : I actually just tried to copy/past a code for rendering triangle and it keep doesn't work...
#include "SceneOpenGl.h"
// Shader temporaire :
#include "Shader.h"
SceneOpenGL::SceneOpenGL(SDL_Window* window) {
if (window == NULL) {
throw new SceneOpenGLException();
}
// Engine
this->window = window;
// Tools
this->fps = new FPSCount();
this->input = new Input();
// Scene
this->run = false;
int w, h;
SDL_GetWindowSize(window, &w, &h);
this->projection = glm::perspective(70.0, (double)(w/h), 1.0, 100.0);
this->modelview = glm::mat4(1.0);
}
SceneOpenGL::~SceneOpenGL(){
delete fps;
delete input;
}
void SceneOpenGL::start() {
run = true;
// Test en attendant ObjectGL.show() //
GLfloat vertices[] = {
0.5f, 0.5f, 0.0f, // Top Right
0.5f, -0.5f, 0.0f, // Bottom Right
-0.5f, -0.5f, 0.0f, // Bottom Left
-0.5f, 0.5f, 0.0f // Top Left
};
GLuint indices[] = { // Note that we start from 0!
0, 1, 3, // First Triangle
1, 2, 3 // Second Triangle
};
// Build and compile our shader program
// Vertex shader
const GLchar* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
"}\0";
const GLchar* fragmentShaderSource = "#version 330 core\n"
"out vec4 color;\n"
"void main()\n"
"{\n"
"color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// Check for compile time errors
GLint success;
GLchar infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// Fragment shader
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// Check for compile time errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// Link shaders
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// Check for linking errors
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
GLuint VBO, VAO, EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
// Bind the Vertex Array Object first, then bind and set vertex buffer(s) and attribute pointer(s).
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, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // Note that this is allowed, the call to glVertexAttribPointer registered VBO as the currently bound vertex buffer object so afterwards we can safely unbind
glBindVertexArray(0); // Unbind VAO (it's always a good thing to unbind any buffer/array to prevent strange bugs), remember: do NOT unbind the EBO, keep it bound to this VAO
// /Test //
while (run) {
// FPS flag
if (fps->flag()) {
std::cout << "FPS : " << fps->getFps() << std::endl;
}
// Input check/actions
input->updateEvents();
if (input->isKeyPush(SDL_SCANCODE_ESCAPE)) {
run = false;
}
// Cleaning last frame
glClear(GL_COLOR_BUFFER_BIT);
// Test //
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Draw our first triangle
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
//glDrawArrays(GL_TRIANGLES, 0, 6);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
// FPS add frame
fps->addFrame();
// Rendering
SDL_GL_SwapWindow(window);
}
}
At no point are you loading your vertex data into video memory. You have a line which looks like an attempt to load the vertices into video memory:
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices);
.... But that's not what that function does.
You need to create and bind a Vertex Array and a Vertex Buffer:
GLuint vertex_array_id;
glGenVertexArrays(1, &vertex_array_id);
glBindVertexArray(vertex_array_id);
GLuint vertex_buffer_id;
glGenBuffers(1, &vertex_buffer_id);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id);
Then, while this Vertex Array and Vertex Buffer is bound, you need to explicitly provide data to the buffer:
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
Then you use glVertexAttribPointer to specify how the data is laid out.
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr);
glEnableVertexAttribArray(0);
All of that code, by the way, should probably be outside the loop. Then inside the draw loop, you simply have to call glDrawArrays.
I've been following this tutorial in a bid to learn OpenGL. I have something that works, but only if I use global variables:
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
// float vertices[] = {
// -0.5f, -0.5f, 0.0f,
// 0.5f, -0.5f, 0.0f,
// 0.0f, 0.5f, 0.0f
// };
float rectVertices[] = {
0.5f, 0.5f, 0.0f, // Top Right
0.5f, -0.5f, 0.0f, // Bottom Right
-0.5f, -0.5f, 0.0f, // Bottom Left
-0.5f, 0.5f, 0.0f // Top Left
};
uint rectIndices[] = {
0, 1, 3, // First Triangle
1, 2, 3 // Second Triangle
};
const GLchar* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"void main() {\n"
"gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
"}\n";
const GLchar* fragmentShaderSource = "#version 330 core\n"
"out vec4 color;\n"
"void main() {\n"
"color = vec4(1.0, 0.5, 0.2, 1.0);\n"
"}\n";
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) {
if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
}
void finalize() {
glfwTerminate();
}
void programExit(int code) {
finalize();
exit(code);
}
void enforce(int success, const char* msg, const char* info) {
if (!success) {
std::cerr << msg << info << std::endl;
exit(-1);
}
}
// Initialise
// GLFW and OpenGL
void initialiseGLFW(int vMajor, int vMinor) {
// Initialising GLFW
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, vMajor);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, vMinor);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
}
// Window Creation
GLFWwindow* createWindow(int width, int height, const char* title) {
GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", nullptr, nullptr);
enforce(window != nullptr, "Failed to create GLFW window", nullptr);
return window;
}
void initialiseGLEW(bool experimental) {
glewExperimental = experimental;
enforce(glewInit() == GLEW_OK, "Failed to initialise GLEW", nullptr);
}
GLFWwindow* initialise(int width, int height, const char* title) {
initialiseGLFW(3, 3);
// Creating GLFW Window
GLFWwindow* window = createWindow(width, height, title);
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, key_callback);
// Initialising GLEW
initialiseGLEW(true);
glViewport(0, 0, 800, 600);
return window;
}
// Creating Shaders
void checkCompilationError(uint shader) {
int success;
char infoLog[512];
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
glGetShaderInfoLog(shader, 512, nullptr, infoLog);
enforce(success, "Shader compilation error: ", infoLog);
}
void checkLinkingError(uint program) {
int success;
char infoLog[512];
glGetProgramiv(program, GL_LINK_STATUS, &success);
glGetProgramInfoLog(program, 512, nullptr, infoLog);
enforce(success, "Program linking error: ", infoLog);
}
uint compileShader(GLenum type, const char* source, ushort count, int* lengths) {
uint shader = glCreateShader(type);
glShaderSource(shader, count, &source, lengths);
glCompileShader(shader);
checkCompilationError(shader);
return shader;
}
uint createProgram(uint vShader, uint fShader) {
uint program = glCreateProgram();
glAttachShader(program, vShader);
glAttachShader(program, fShader);
glLinkProgram(program);
checkLinkingError(program);
return program;
}
// Subprocedure specific to this program
uint initialiseShaders(const char* vsSource, const char* fsSource) {
// Initialising shaders
uint vShader = compileShader(GL_VERTEX_SHADER, vsSource, 1, nullptr);
uint fShader = compileShader(GL_FRAGMENT_SHADER, fsSource, 1, nullptr);
// Link program
GLuint shaderProgram = createProgram(vShader, fShader);
// clean up
glDeleteShader(vShader);
glDeleteShader(fShader);
return shaderProgram;
}
void configureVBO(float* vertices) {
for (int i = 0; i < 12; i++) std::cout << vertices[i] << std::endl;
glBufferData(
GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW
);
// Tell OpenGL how to interpret the vertices
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*) 0);
glEnableVertexAttribArray(0);
}
void configureEBO(uint* indices) {
for (int i = 0; i < 6; i++) std::cout << indices[i] << std::endl;
glBufferData(
GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW
);
}
uint intialiseVAO(float* vertices, uint* indices) {
uint VAO, VBO, EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
configureVBO(vertices);
if (indices != nullptr) {
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
configureEBO(indices);
}
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
return VAO;
}
void execGameLoop(GLFWwindow* window, uint shaderProgram, uint VAO) {
while(!glfwWindowShouldClose(window)) {
glfwPollEvents();
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Set the program to be used
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glfwSwapBuffers(window);
}
}
int main() {
// Config
int width = 800, height = 800;
const char* title = "Learn OpenGL";
// Initialise GLFW and GLEW
GLFWwindow* window = initialise(width, height, title);
// Initialise Shader program
uint shaderProgram = initialiseShaders(vertexShaderSource, fragmentShaderSource);
// Configuring VAO, VBO and EBO
uint VAO = intialiseVAO(rectVertices, rectIndices);
execGameLoop(window, shaderProgram, VAO);
finalize();
return 0;
}
My problem is specifically to do with:
void configureVBO(float* vertices) {
for (int i = 0; i < 12; i++) std::cout << vertices[i] << std::endl;
glBufferData(
GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW
);
// Tell OpenGL how to interpret the vertices
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*) 0);
glEnableVertexAttribArray(0);
}
void configureEBO(uint* indices) {
for (int i = 0; i < 6; i++) std::cout << indices[i] << std::endl;
glBufferData(
GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW
);
}
which works as expected when using the rectVertices/Indices global variables, but don't work when they're passed as parameters. The for-loops in each method prints the array, and they contain the values I expect them to. Why does OpenGL draw the rectangle when I use the global variables, but not when I use local parameters?
void configureVBO(float* vertices) {
for (int i = 0; i < 12; i++) std::cout << vertices[i] << std::endl;
glBufferData(
GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW
);
// Tell OpenGL how to interpret the vertices
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*) 0);
glEnableVertexAttribArray(0);
}
The problem here is that sizeof(vertices) is not doing what you think it does. It doesn't tell you how many vertices are in the array -- rather, it's telling you how many bytes a variable of type vertices occupies. Since vertices is a pointer, that number is likely to be either 4 or 8 depending on your system -- but importantly, it has nothing to do with how many vertices you're actually intending to upload.
Instead, what you need to do is tell OpenGL how big you want the buffer to be, in bytes. You calculate that by number of vertices * sizeof(vertex_type). So in your case that would be 12 * sizeof(float).
In general, you'll either need to include a second parameter to the function which includes the number of vertices, or use a std::vector<float> to hold your vertex data instead. For example, with a vector this would become:
void configureVBO(const std::vector<float>& vertices) {
for (auto v : vertices) std::cout << v << "\n";
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float),
vertices.data(), GL_STATIC_DRAW);
// etc...
}
The reason the code worked when you used a global variable is to do with the way C++ passes arrays to functions (which in turn, it inherited from C). When you pass an array, what the function receives is just a pointer to the start of the array -- the information about the length of the array has been lost, so sizeof(vertices) can't tell you how long the array actually is. However, if you use a global variable, then the compiler can see the definition float vertices[12] even within the configureVBO function, so in that case sizeof() will do what you expect.
This is a very common mix-up for people getting started with C and C++. I'd really recommend using std::vector instead (for many reasons, of which this is just one), but it's also worth reading up on how arrays and pointers work in C and C++.