I'm currently trying to learn OpenGL. I've successfully rendered rainbow triangles to the screen, and I've successfully moved them about the screen.
However, I can not seem to properly render a texture to two triangles.
Whenever I run this program, I am greeted with a black screen with nothing rendering. I've checked, and the image file is definately in the correct directory (it wouldn't run otherwise!)
define STB_IMAGE_IMPLEMENTATION
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <stb/stb_image.h>
int main()
{
//Initialize glfw
if (!glfwInit())
{
fprintf(stderr, "ERROR: Could not start GLFW3.\n");
return 1;
}
//Create the rendering context
GLFWwindow* window = glfwCreateWindow(480, 600, "Hello Rendering",
nullptr, nullptr);
if(!window)
{
fprintf(stderr, "ERROR: Could not create a rendering context with "
"GLFW3.\n");
return 1;
}
glfwMakeContextCurrent(window);
//Start GLEW
glewExperimental=GL_TRUE;
GLenum err=glewInit();
if(err!=GLEW_OK)
{
//Problem: glewInit failed, something is seriously wrong.
std::cout<<"glewInit failed, aborting."<<std::endl;
}
////////////////////////
//Loading PNG
////////////////////////
int x = 0;
int y = 0;
int n = 0;
int force_channels = 4;
unsigned char* image_data = stbi_load("spooky.png", &x, &y, &n,
force_channels);
if(!image_data)
{
fprintf(stderr, "ERROR: Could not load spooky.png\n.");
return 1;
}
//NPOT Check
if((x & (x - 1)) != 0 || (y & (y - 1)) != 0)
{
fprintf(stderr, "ERROR: Image is not a power of 2.\n");
fprintf(stderr, "h: %d w: %d", y, x);
return 1;
}
//Flip the image
int width_in_bytes = x * 4;
unsigned char* top = nullptr;
unsigned char* bottom = nullptr;
unsigned char temp = 0;
int half_height = y / 2;
for(int row = 0; row < half_height; row++)
{
top = image_data + row * width_in_bytes;
bottom = image_data + (y - row - 1) * width_in_bytes;
for(int col = 0; col < width_in_bytes; col++)
{
temp = *top;
*top = *bottom;
*bottom = temp;
top++;
bottom++;
}
}
GLuint tex = 0;
glGenTextures(1, &tex);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA,
GL_UNSIGNED_BYTE, image_data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//////////////////////////////////
//Vertex shader
//////////////////////////////////
const char* vertShader =
"#version 330 core"
"layout(location = 0) in vec3 vertexPosition_modelspace;"
"layout(location = 1) in vec2 vt;"
"out vec2 texture_coordinates;"
"void main() {"
" texture_coordinates = vt;"
" gl_Position.xyz = vertexPosition_modelspace;"
" gl_Position.w = 1.0;"
"}";
///////////////////////////////////
//Frag shader
///////////////////////////////////
const char* fragShader =
"#version 330 core"
"uniform sampler2D basic_texture;"
"out vec4 frag_color;"
"void main() {"
" vec4 texel = texture(basic_texture, texture_coordinates);"
" frag_color = texel;"
"}";
////////////////////////////////////
//Create shader program
///////////////////////////////////
GLuint vsp = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vsp, 1, &vertShader, nullptr);
glCompileShader(vsp);
GLuint fsp = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fsp, 1, &fragShader, nullptr);
glCompileShader(fsp);
GLuint shader_program = glCreateProgram();
glAttachShader(shader_program, fsp);
glAttachShader(shader_program, vsp);
glLinkProgram(shader_program);
glUseProgram(shader_program);
////////////////////////////////////
//Texture coordinates
////////////////////////////////////
GLfloat texcoords[] = {
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f
};
GLuint vt_vbo;
glGenBuffers(1, &vt_vbo);
glBindBuffer(GL_ARRAY_BUFFER, vt_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof( texcoords), texcoords,
GL_STATIC_DRAW);
GLuint vao;;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vt_vbo);
int dimensions = 2;
glVertexAttribPointer(1, dimensions, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(1);
while(!glfwWindowShouldClose(window))
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//Use the shader
glUseProgram(shader_program);
//Draw the two triangles (6 points)
glDrawArrays(GL_TRIANGLES, 0, 6);
glfwPollEvents();
glfwSwapBuffers(window);
if(GLFW_PRESS == glfwGetKey(window, GLFW_KEY_ESCAPE))
{
glfwSetWindowShouldClose(window, 1);
}
}
std::cout << "Hello, World!" << std::endl;
return 0;
}
First of all, you should retrieve the error messages when compiling and linking your shader:
GLint status = GL_TRUE;
char error_msg[1024];
GLsizei read;
GLuint vsp = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vsp, 1, &vertShader, nullptr);
glCompileShader(vsp);
glGetShaderiv( vsp, GL_COMPILE_STATUS, &status );
if ( status != GL_TRUE )
{
glGetShaderInfoLog( vsp, 1024, &read, error_msg );
std::cout << "compile error:" << std::endl << error_msg << std::endl;
}
GLuint fsp = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fsp, 1, &fragShader, nullptr);
glCompileShader(fsp);
glGetShaderiv( fsp, GL_COMPILE_STATUS, &status );
if ( status != GL_TRUE )
{
glGetShaderInfoLog( fsp, 1024, &read, error_msg );
std::cout << "compile error:" << std::endl << error_msg << std::endl;
}
GLuint shader_program = glCreateProgram();
glAttachShader(shader_program, fsp);
glAttachShader(shader_program, vsp);
glLinkProgram(shader_program);
glGetProgramiv( shader_program, GL_LINK_STATUS, &status );
if ( status != GL_TRUE )
{
glGetProgramInfoLog( shader_program, 1024, &read, error_msg );
std::cout << "compile error:" << std::endl << error_msg << std::endl;
}
The first error message you will get is somehow like this:
0(1) : error C0205: invalid profile "corelayout"
0(1) : error C0206: invalid token "" in version line
This is because you did not write the line ending "\n" after the first line of the shader code ("#version 330 core").
When you fixe this then you get the next error message:
0(2) : error C1008: undefined variable "texture_coordinates"
You have to add the declaration of the in variable texture_coordinates to the fragment shader.
in vec2 texture_coordinates;
The final code looks like this:
const char* vertShader =
"#version 330 core\n"
"layout(location = 0) in vec3 vertexPosition_modelspace;"
"layout(location = 1) in vec2 vt;"
"out vec2 texture_coordinates;"
"void main() {"
" texture_coordinates = vt;"
" gl_Position.xyz = vertexPosition_modelspace;"
" gl_Position.w = 1.0;"
"}";
const char* fragShader =
"#version 330 core\n"
"uniform sampler2D basic_texture;"
"out vec4 frag_color;"
"in vec2 texture_coordinates;"
"void main() {"
" vec4 texel = texture(basic_texture, texture_coordinates);"
" frag_color = texel;"
"}";
Of course you have to provide vertex data for the vertex attribute vertexPosition_modelspace:
GLfloat texcoords[] = {
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f
};
GLfloat vertex[] = {
-1.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f
};
GLuint vert_vbo;
glGenBuffers(1, &vert_vbo);
glBindBuffer(GL_ARRAY_BUFFER, vert_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof( vertex ), vertex, GL_STATIC_DRAW);
GLuint vt_vbo;
glGenBuffers(1, &vt_vbo);
glBindBuffer(GL_ARRAY_BUFFER, vt_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof( texcoords ), texcoords, GL_STATIC_DRAW);
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vert_vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vt_vbo);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(1);
Finally you should set the index of the texture unit, wher the texture is bound to, to the texture sampler uniform variable basic_texture:
int texture_unit_index = 0;
GLuint tex = 0;
glGenTextures(1, &tex);
glActiveTexture(GL_TEXTURE0 + texture_unit_index );
glBindTexture(GL_TEXTURE_2D, tex);
.....
// to do after glLinkProgram(shader_program);
int tex_sampler_loc = glGetUniformLocation( shader_program, "basic_texture" );
.....
// to do after glUseProgram(shader_program);
glUniform1i( tex_sampler_loc, texture_unit_index );
Related
I'm trying to render JPG image using "stb_image.h" library. Im using GLEW and GLFW, here is the code:
#include "glew.h"
#include "glfw3.h"
#include <iostream>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define WIDTH 800
#define HEIGHT 600
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
int main(){
GLFWwindow *window;
if (!glfwInit())
{
std::cout << "Failed to Init GLFW" << std::endl;
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
window = glfwCreateWindow(WIDTH, HEIGHT, "OpenGL CPP", NULL, NULL);
if (!window)
{
std::cout << "Failed to create GLFW Window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
GLenum err = glewInit();
if (GLEW_OK != err)
{
std::cout << "Failed to Init GLEW" << std::endl;
glfwTerminate();
return -1;
}
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
};
float indices[] = {
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
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"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos, 1.0);\n"
" ourColor = aColor;\n"
" TexCoord = vec2(aTexCoord.x, aTexCoord.y);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"in vec2 TexCoord;\n"
"uniform sampler2D ourTexture;\n"
"void main()\n"
"{\n"
" FragColor = texture(ourTexture, TexCoord);\n"
"}\n\0";
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR COMPILING VERTEX SHADER" << infoLog << std::endl;
}
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR COMPILING FRAGMENT SHADER" << infoLog << std::endl;
}
unsigned int shaderProgram;
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
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);
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
int width, height, nrChannels;
unsigned char *data = stbi_load("C:/Users/user3/Documents/cppopengl/res/container.jpg", &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);
std::cout << "data loaded successfully"<< std::endl;
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
glUseProgram(shaderProgram);
//std::cout << glGetUniformLocation(shaderProgram, "ourTexture") << std::endl;
glUniform1i(glGetUniformLocation(shaderProgram, "ourTexture"), 0);
while (!glfwWindowShouldClose(window))
{
err = glGetError();
glClearColor(0.5, 0.5, 0.5, 1);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glfwPollEvents();
if (((err = glGetError()) != GL_NO_ERROR))
{
std::cout << err << std::endl;
}
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
glDeleteProgram(shaderProgram);
return 0;
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
Code compiles just fine, without errors and warnings. When I start the executable it shows white screen. Both vertex shader and fragment shader compile and link without errors. stbi_load() does not return error and glGetError() does not return error, glGetUniformLocation() returns uniform location just fine. Can you please help me out? Thanks in advance.
EDIT:
indices must be unsigned int instead of float, thanks #Rabbid76
unsigned int indices[] = {
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
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;
Bear in mind that I have openGL 3 and glsl version 1.30, therefore, I don't have dynamic indexing nor glBindTextureUnit().
I have seen people batch render multiple textures in one draw call by simply activating the texture slot, binding the texture then setting the uniform, like so:
glUniform1i(glGetUniformLocation(program, "u_texture0"), 0);
glUniform1i(glGetUniformLocation(program, "u_texture1"), 1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture1);
However, when I do this:
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#define STB_IMAGE_IMPLEMENTATION
#include "stb/stb_image.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char* readFile(const char filePath[])
{
FILE* file;
file = fopen(filePath, "r");
if(file == NULL)
{
perror("unable to load file");
exit(1);
}
fseek(file, 0l, SEEK_END);
char* buffer = (char*)malloc(ftell(file));
rewind(file);
char c;
unsigned int i = 0;
while((c = fgetc(file)) != EOF)
buffer[i++] = c;
fclose(file);
return buffer;
}
GLuint loadShader(const char* src, GLenum type)
{
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &src, NULL);
glCompileShader(shader);
// shader error handling
int result;
glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
if(result == GL_FALSE)
{
int length;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
char* message = (char*)alloca(length * sizeof(char));
glGetShaderInfoLog(shader, length, &length, message);
puts(message);
}
return shader;
}
GLuint loadTexture(const char* filePath)
{
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
unsigned char* buffer;
int width, height;
stbi_set_flip_vertically_on_load(1);
buffer = stbi_load(filePath, &width, &height, NULL, STBI_rgb_alpha);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
if(buffer)
stbi_image_free(buffer);
return texture;
}
void resetViewport(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); }
int main()
{
// initialising GLFW
if(!glfwInit())
{
perror("error with glfw!");
exit(1);
}
// window creation
GLFWwindow* window;
window = glfwCreateWindow(800, 500, "window", NULL, NULL);
glfwSetWindowSizeCallback(window, resetViewport);
// binding renderring context
glfwMakeContextCurrent(window);
// initialising glew
if(glewInit() != GLEW_OK)
perror("error with glee!");
// enabling blend
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// creating program and shaders
GLuint program = glCreateProgram();
char* vertexShaderSource = readFile("vertex.glsl");
char* fragmentShaderSource = readFile("fragment.glsl");
GLuint vertexShader = loadShader(vertexShaderSource, GL_VERTEX_SHADER);
GLuint fragmentShader = loadShader(fragmentShaderSource, GL_FRAGMENT_SHADER);
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
// deleting shaders
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
free(vertexShaderSource);
free(fragmentShaderSource);
// compiling and binding program
glLinkProgram(program);
glUseProgram(program);
// creating vertex array
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// creating vertex buffer
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
// vertex buffer layout
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), NULL);
glBindAttribLocation(program, 0, "a_position");
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (const void*)(2 * sizeof(float)));
glBindAttribLocation(program, 1, "a_textureCoordinate");
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (const void*)(4 * sizeof(float)));
glBindAttribLocation(program, 2, "a_index");
// creating texture
GLuint texture0 = loadTexture("image0.jpeg");
GLuint texture1 = loadTexture("image1.jpeg");
// vertex data
float vertexes[] =
{
// x y u v i (index)
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
0.0f, -1.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.0f, +1.0f, 0.0f, 1.0f, 1.0f,
+1.0f, +1.0f, 1.0f, 1.0f, 1.0f,
+1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
};
// unbinding just incase
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// rebinding
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
// binding texture to texture slot
glUniform1i(glGetUniformLocation(program, "u_texture0"), 0);
glUniform1i(glGetUniformLocation(program, "u_texture1"), 1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture1);
// sending data
glBufferData(GL_ARRAY_BUFFER, 8 * 5 * sizeof(float), vertexes, GL_STATIC_DRAW);
glLinkProgram(program);
while(!glfwWindowShouldClose(window))
{
glfwPollEvents();
// clear screen
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// draw call
glDrawArrays(GL_QUADS, 0, 8);
// updating window's buffer
glfwSwapBuffers(window);
}
// error logging
GLenum error = glGetError();
if(error != GL_NO_ERROR)
printf("error: %u\n", error);
// termination
glDeleteTextures(1, &texture0);
glDeleteTextures(1, &texture1);
glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao);
glDeleteProgram(program);
glfwTerminate();
}
With the fragment shader code being:
#version 130
in vec2 v_textureCoordinate;
flat in uint v_index;
uniform sampler2D u_texture0;
uniform sampler2D u_texture1;
uniform sampler2D u_texture2;
uniform sampler2D u_texture3;
uniform sampler2D u_texture4;
uniform sampler2D u_texture5;
uniform sampler2D u_texture6;
uniform sampler2D u_texture7;
out vec4 o_color;
void main()
{
// dynamic indexing is not suppourted in GLSL version 1.30 therefore the most suitable solution is to switch case between them
switch(v_index)
{
case 0u:
o_color = texture2D(u_texture0, v_textureCoordinate);
return;
case 1u:
o_color = texture2D(u_texture1, v_textureCoordinate);
return;
case 2u:
o_color = texture2D(u_texture2, v_textureCoordinate);
return;
case 3u:
o_color = texture2D(u_texture3, v_textureCoordinate);
return;
case 4u:
o_color = texture2D(u_texture4, v_textureCoordinate);
return;
case 5u:
o_color = texture2D(u_texture5, v_textureCoordinate);
return;
case 6u:
o_color = texture2D(u_texture6, v_textureCoordinate);
return;
case 7u:
o_color = texture2D(u_texture7, v_textureCoordinate);
return;
default:
return;
}
}
and the vertex shader code being:
#version 130
attribute vec4 a_position;
attribute vec2 a_textureCoordinate;
attribute float a_index;
out vec2 v_textureCoordinate;
flat out uint v_index;
void main()
{
gl_Position = a_position;
v_textureCoordinate = a_textureCoordinate;
v_index = uint(a_index);
}
It renders both quads with the texture texture0, Am I doing something wrong? I am getting the impression that binding the second texture is unbinding the first texture. What am I missing?
glBindAttribLocation() has to be called before shader program linking to have any effect:
GLuint prog = glCreateProgram();
AttachShader( prog, GL_VERTEX_SHADER, vert );
AttachShader( prog, GL_FRAGMENT_SHADER, frag );
glBindAttribLocation(prog, 0, "a_position");
glBindAttribLocation(prog, 1, "a_textureCoordinate");
glBindAttribLocation(prog, 2, "a_index");
glLinkProgram( prog );
CheckStatus( prog, false );
glUseProgram( prog );
All together:
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <cstdio>
#include <cstdlib>
#include <iostream>
void CheckStatus( GLuint obj, bool isShader )
{
GLint status = GL_FALSE, log[ 1 << 11 ] = { 0 };
( isShader ? glGetShaderiv : glGetProgramiv )( obj, isShader ? GL_COMPILE_STATUS : GL_LINK_STATUS, &status );
if( status == GL_TRUE ) return;
( isShader ? glGetShaderInfoLog : glGetProgramInfoLog )( obj, sizeof( log ), NULL, (GLchar*)log );
std::cerr << (GLchar*)log << "\n";
std::exit( EXIT_FAILURE );
}
void AttachShader( GLuint program, GLenum type, const char* src )
{
GLuint shader = glCreateShader( type );
glShaderSource( shader, 1, &src, NULL );
glCompileShader( shader );
CheckStatus( shader, true );
glAttachShader( program, shader );
glDeleteShader( shader );
}
const char* const vert = R"GLSL(
#version 130
attribute vec4 a_position;
attribute vec2 a_textureCoordinate;
attribute float a_index;
out vec2 v_textureCoordinate;
flat out uint v_index;
void main()
{
gl_Position = a_position;
v_textureCoordinate = a_textureCoordinate;
v_index = uint(a_index);
}
)GLSL";
const char* const frag = R"GLSL(
#version 130
in vec2 v_textureCoordinate;
flat in uint v_index;
uniform sampler2D u_texture0;
uniform sampler2D u_texture1;
uniform sampler2D u_texture2;
uniform sampler2D u_texture3;
uniform sampler2D u_texture4;
uniform sampler2D u_texture5;
uniform sampler2D u_texture6;
uniform sampler2D u_texture7;
out vec4 o_color;
void main()
{
// dynamic indexing is not suppourted in GLSL version 1.30 therefore the most suitable solution is to switch case between them
switch(v_index)
{
case 0u:
o_color = texture2D(u_texture0, v_textureCoordinate);
return;
case 1u:
o_color = texture2D(u_texture1, v_textureCoordinate);
return;
case 2u:
o_color = texture2D(u_texture2, v_textureCoordinate);
return;
case 3u:
o_color = texture2D(u_texture3, v_textureCoordinate);
return;
case 4u:
o_color = texture2D(u_texture4, v_textureCoordinate);
return;
case 5u:
o_color = texture2D(u_texture5, v_textureCoordinate);
return;
case 6u:
o_color = texture2D(u_texture6, v_textureCoordinate);
return;
case 7u:
o_color = texture2D(u_texture7, v_textureCoordinate);
return;
default:
return;
}
}
)GLSL";
GLuint loadTexture(const unsigned char* image, int w, int h)
{
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
return texture;
}
void resetViewport(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
int main()
{
// initialising GLFW
if(!glfwInit())
{
perror("error with glfw!");
exit(1);
}
// window creation
GLFWwindow* window;
window = glfwCreateWindow(800, 500, "window", NULL, NULL);
glfwSetWindowSizeCallback(window, resetViewport);
// binding renderring context
glfwMakeContextCurrent(window);
// initialising glew
if(glewInit() != GLEW_OK)
perror("error with glee!");
// enabling blend
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// creating program and shaders
GLuint prog = glCreateProgram();
AttachShader( prog, GL_VERTEX_SHADER, vert );
AttachShader( prog, GL_FRAGMENT_SHADER, frag );
glBindAttribLocation(prog, 0, "a_position");
glBindAttribLocation(prog, 1, "a_textureCoordinate");
glBindAttribLocation(prog, 2, "a_index");
glLinkProgram( prog );
CheckStatus( prog, false );
glUseProgram( prog );
// creating vertex array
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// creating vertex buffer
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
// vertex buffer layout
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), NULL);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (const void*)(2 * sizeof(float)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (const void*)(4 * sizeof(float)));
// creating texture
const unsigned char image1[] = { 255, 0, 0, 255 };
GLuint texture0 = loadTexture( image1, 1, 1 );
const unsigned char image2[] = { 0, 255, 0, 255 };
GLuint texture1 = loadTexture( image2, 1, 1 );
// vertex data
float vertexes[] =
{
// x y u v i (index)
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
0.0f, -1.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.0f, +1.0f, 0.0f, 1.0f, 1.0f,
+1.0f, +1.0f, 1.0f, 1.0f, 1.0f,
+1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
};
// unbinding just incase
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// rebinding
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
// binding texture to texture slot
glUniform1i(glGetUniformLocation(prog, "u_texture0"), 0);
glUniform1i(glGetUniformLocation(prog, "u_texture1"), 1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture1);
// sending data
glBufferData(GL_ARRAY_BUFFER, 8 * 5 * sizeof(float), vertexes, GL_STATIC_DRAW);
while(!glfwWindowShouldClose(window))
{
glfwPollEvents();
// clear screen
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// draw call
glDrawArrays(GL_QUADS, 0, 8);
// updating window's buffer
glfwSwapBuffers(window);
}
// error logging
GLenum error = glGetError();
if(error != GL_NO_ERROR)
printf("error: %u\n", error);
// termination
glDeleteTextures(1, &texture0);
glDeleteTextures(1, &texture1);
glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao);
glDeleteProgram(prog);
glfwTerminate();
}
The following code draws a white square to the screen. If I uncomment the line that uses the program, the square disappears.
When I debug the program with GLIntercept, the texture appears in a folder called Images, and the log says that the shaders compiled. However, it also says that the program links, but doesn't validate.
I've been poring over this for hours and I have no idea where to go from here.
// Vertex.vert
#version 150 core
in vec3 in_position;
in vec2 in_texture;
out vec2 Texture;
uniform mat4 in_model;
uniform mat4 in_view;
uniform mat4 in_projection;
void main()
{
gl_Position = in_projection * in_view * in_model * vec4(in_position, 1.0);
Texture = in_texture;
}
// Fragment.frag
#version 150 core
in vec2 Texture;
out vec4 Colour;
uniform sampler2D Sampler2D;
void main()
{
Colour = texture(Sampler2D, Texture);
}
// Source.cpp
#include <cfloat>
#include <iostream>
#include <string>
#include <vector>
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/transform.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include "Archive.h"
using namespace glm;
using namespace sf;
using namespace std;
struct Camera
{
vec3 Position = { 0.0f, 0.0f, 1.0f };
vec3 Target = { 0.0f, 0.0f, 0.0f };
vec3 Up = { 0.0f, 1.0f, 0.0f };
float Fovy = 74.0f;
float Aspect = 16.0f / 9.0f;
float ZNear = FLT_MIN;
float ZFar = FLT_MAX;
mat4 View;
mat4 Projection;
};
struct Actor
{
vec3 Scale = { 1.0f, 1.0f, 1.0f };
vec3 Rotation = { 0.0f, 0.0f, 0.0f };
vec3 Position = { 0.0f, 0.0f, 0.0f };
vector<GLfloat> Vertices;
vector<GLuint> Elements;
GLuint Texture;
Actor(string fileName)
{
Image image;
if (!image.loadFromFile(fileName + ".png"))
{
cerr << "ERROR: Unable to load texture" << endl;
}
glGenTextures(1, &Texture);
glBindTexture(GL_TEXTURE_2D, Texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.getSize().x, image.getSize().y, 0, GL_RGBA, GL_UNSIGNED_BYTE, image.getPixelsPtr());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
// Draw a square instead of the actual model
Vertices.push_back(-1.0f); Vertices.push_back(-1.0f); Vertices.push_back(0.0f); Vertices.push_back(0.0f); Vertices.push_back(0.0f);
Vertices.push_back(1.0f); Vertices.push_back(-1.0f); Vertices.push_back(0.0f); Vertices.push_back(1.0f); Vertices.push_back(0.0f);
Vertices.push_back(1.0f); Vertices.push_back(1.0f); Vertices.push_back(0.0f); Vertices.push_back(1.0f); Vertices.push_back(1.0f);
Vertices.push_back(-1.0f); Vertices.push_back(1.0f); Vertices.push_back(0.0f); Vertices.push_back(0.0f); Vertices.push_back(1.0f);
Elements.push_back(0); Elements.push_back(1); Elements.push_back(2);
Elements.push_back(2); Elements.push_back(3); Elements.push_back(0);
}
};
GLuint CreateShader(GLenum shaderType, string fileName, Archive& archive)
{
string source;
archive.open(fileName);
source.resize(archive.getSize());
archive.read(&source[0], archive.getSize());
GLuint shader = glCreateShader(shaderType);
const char* pointer = source.c_str();
glShaderSource(shader, 1, &pointer, nullptr);
glCompileShader(shader);
GLsizei length;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
if (length > 1)
{
GLchar* infoLog = new GLchar[length];
glGetShaderInfoLog(shader, length, &length, infoLog);
cerr << infoLog << endl;
delete[] infoLog;
}
return shader;
}
GLuint CreateProgram(GLuint vertex, GLuint fragment)
{
GLuint program = glCreateProgram();
glAttachShader(program, vertex);
glAttachShader(program, fragment);
glLinkProgram(program);
GLsizei length;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
if (length > 1)
{
GLchar* infoLog = new GLchar[length];
glGetProgramInfoLog(program, length, &length, infoLog);
cerr << infoLog << endl;
delete[] infoLog;
}
return program;
}
int main(int argc, char* argv[])
{
Window window(VideoMode(1920, 1080), "");
window.setVerticalSyncEnabled(true);
if (!window.setActive(true))
{
cerr << "ERROR: Unable to set the window as the current target for OpenGL rendering" << endl;
return 1;
}
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK)
{
cerr << "ERROR: Unable to initialise GLEW" << endl;
return 1;
}
Archive shaders("Shaders.lea");
Archive models("Models.lea");
Actor actor("tree01");
GLuint vertex = CreateShader(GL_VERTEX_SHADER, "Vertex.vert", shaders);
GLuint fragment = CreateShader(GL_FRAGMENT_SHADER, "Fragment.frag", shaders);
GLuint program = CreateProgram(vertex, fragment);
GLuint vertexArray;
GLuint vertexBuffer;
GLuint elementBuffer;
glGenVertexArrays(1, &vertexArray);
glBindVertexArray(vertexArray);
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glGenBuffers(1, &elementBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer);
// glUseProgram(program);
GLint position = glGetAttribLocation(program, "in_position");
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, 0);
glEnableVertexAttribArray(position);
GLint texture = glGetAttribLocation(program, "in_texture");
glVertexAttribPointer(texture, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (void*)(sizeof(GLfloat) * 3));
glEnableVertexAttribArray(texture);
GLint projection = glGetUniformLocation(program, "in_projection");
GLint view = glGetUniformLocation(program, "in_view");
GLint model = glGetUniformLocation(program, "in_model");
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
Camera camera;
while (window.isOpen())
{
// Input handling code omitted
camera.View = lookAt(camera.Position, camera.Target, camera.Up);
camera.Projection = perspective(radians(camera.Fovy), camera.Aspect, camera.ZNear, camera.ZFar);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUniformMatrix4fv(projection, 1, GL_FALSE, value_ptr(camera.Projection));
glUniformMatrix4fv(view, 1, GL_FALSE, value_ptr(camera.View));
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * actor.Vertices.size(), &actor.Vertices[0], GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * actor.Elements.size(), &actor.Elements[0], GL_STATIC_DRAW);
mat4 transform = translate(mat4(), actor.Position);
transform *= rotate(transform, actor.Rotation.z, vec3(0.0f, 0.0f, 1.0f));
transform *= rotate(transform, actor.Rotation.y, vec3(0.0f, 1.0f, 0.0f));
transform *= rotate(transform, actor.Rotation.x, vec3(1.0f, 0.0f, 0.0f));
transform *= scale(transform, actor.Scale);
glUniformMatrix4fv(model, 1, GL_FALSE, value_ptr(transform));
glBindTexture(GL_TEXTURE_2D, actor.Texture);
glDrawElements(GL_TRIANGLES, actor.Elements.size(), GL_UNSIGNED_INT, 0);
window.display();
}
glUseProgram(0);
glDisableVertexAttribArray(texture);
glDisableVertexAttribArray(position);
glDeleteBuffers(1, &elementBuffer);
glDeleteBuffers(1, &vertexBuffer);
glDeleteVertexArrays(1, &vertexArray);
glDetachShader(program, fragment);
glDetachShader(program, vertex);
glDeleteShader(fragment);
glDeleteShader(vertex);
glDeleteProgram(program);
return 0;
}
It was a combination of two things.
When I last used this code, it was with GLM 0.9.7.6, and mat4() generated an identity matrix. However, at some point between that version of GLM and the one I'm currently using (0.9.9.5), mat4() started generating an empty matrix. Instead, you need mat4(1.0f).
Also, I used bad values for the near and far planes. I did think that the values I had would work, but clearly I don't quite understand what's going on behind the scenes.
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.