Related
So I am trying to render a character in OpenGL using freetype2. If I replace the variable vertex_location in my code with 0 I can see some kind of pixelated thing being rendered but it seems wrong because every time I restart the application the pixelated thing is different... So I am guessing it's just some random bytes or something.
Note: I am using GLEW, freetype2, glfw3, cglm
Anyways, here is my code:
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <cglm/cglm.h>
#include <cglm/call.h>
#include <math.h>
#include <ft2build.h>
#include FT_FREETYPE_H
/**
* Capture errors from glfw.
*/
static void error_callback(int error, const char* description)
{
fprintf(stderr, "Error: %s\n", description);
}
/**
* Capture key callbacks from glfw
*/
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GLFW_TRUE);
}
typedef struct CHARACTER_STRUCT
{
GLuint texture; // ID handle of the glyph texture
vec2 size; // Size of glyph
float width;
float height;
float bearing_left;
float bearing_top;
GLuint advance; // Horizontal offset to advance to next glyph
} character_T;
character_T* get_character(char c)
{
// FreeType
FT_Library ft;
// All functions return a value different than 0 whenever an error occurred
if (FT_Init_FreeType(&ft))
perror("ERROR::FREETYPE: Could not init FreeType Library");
// Load font as face
FT_Face face;
if (FT_New_Face(ft, "/usr/share/fonts/truetype/lato/Lato-Medium.ttf", 0, &face))
perror("ERROR::FREETYPE: Failed to load font");
// Set size to load glyphs as
FT_Set_Pixel_Sizes(face, 0, 32);
// Disable byte-alignment restriction
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Load character glyph
if (FT_Load_Char(face, c, FT_LOAD_RENDER))
perror("ERROR::FREETYTPE: Failed to load Glyph");
// Generate texture
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RED,
face->glyph->bitmap.width,
face->glyph->bitmap.rows,
0,
GL_RED,
GL_UNSIGNED_BYTE,
face->glyph->bitmap.buffer
);
// Set texture options
/*glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
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_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Now store character for later use
character_T* character = calloc(1, sizeof(struct CHARACTER_STRUCT));
character->texture = texture;
character->width = face->glyph->bitmap.width;
character->height = face->glyph->bitmap.rows;
character->bearing_left = face->glyph->bitmap_left;
character->bearing_top = face->glyph->bitmap_top;
character->advance = face->glyph->advance.x;
glBindTexture(GL_TEXTURE_2D, 0);
// Destroy FreeType once we're finished
FT_Done_Face(face);
FT_Done_FreeType(ft);
return character;
}
int main(int argc, char* argv[])
{
glfwSetErrorCallback(error_callback);
/**
* Initialize glfw to be able to use it.
*/
if (!glfwInit())
perror("Failed to initialize glfw.\n");
/**
* Setting some parameters to the window,
* using OpenGL 3.3
*/
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_FLOATING, GL_TRUE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
/**
* Creating our window
*/
GLFWwindow* window = glfwCreateWindow(640, 480, "My Title", NULL, NULL);
if (!window)
perror("Failed to create window.\n");
glfwSetKeyCallback(window, key_callback);
/**
* Enable OpenGL as current context
*/
glfwMakeContextCurrent(window);
/**
* Initialize glew and check for errors
*/
GLenum err = glewInit();
if (GLEW_OK != err)
fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
fprintf(stdout, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
unsigned int VAO;
glGenVertexArrays(1, &VAO);
unsigned int VBO;
glGenBuffers(1, &VBO);
GLuint vertex_shader, fragment_shader, program;
GLint mvp_location, vertex_location;
/**
* Vertex Shader
*/
static const char* vertex_shader_text =
"#version 330 core\n"
"uniform mat4 MVP;\n"
"attribute vec4 thevertex;\n"
"out vec2 TexCoord;\n"
"void main()\n"
"{\n"
" gl_Position = MVP * vec4(thevertex.xy, 0.0, 1.0);\n"
" TexCoord = thevertex.zw;"
"}\n";
/**
* Fragment Shader
*/
static const char* fragment_shader_text =
"#version 330 core\n"
"varying vec3 color;\n"
"in vec2 TexCoord;\n"
"uniform sampler2D ourTexture;\n"
"void main()\n"
"{\n"
" vec4 sampled = vec4(1.0, 1.0, 1.0, texture(ourTexture, TexCoord).r);\n"
" gl_FragColor = vec4(vec3(1, 1, 1), 1.0) * sampled;\n"
"}\n";
int success;
char infoLog[512];
/**
* Compile vertex shader and check for errors
*/
vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL);
glCompileShader(vertex_shader);
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
if(!success)
{
printf("Vertex Shader Error\n");
glGetShaderInfoLog(vertex_shader, 512, NULL, infoLog);
perror(infoLog);
}
/**
* Compile fragment shader and check for errors
*/
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL);
glCompileShader(fragment_shader);
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
if(!success)
{
printf("Fragment Shader Error\n");
glGetShaderInfoLog(fragment_shader, 512, NULL, infoLog);
perror(infoLog);
}
/**
* Create shader program and check for errors
*/
program = glCreateProgram();
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &success);
if(!success)
{
glGetProgramInfoLog(program, 512, NULL, infoLog);
perror(infoLog);
}
/**
* Grab locations from shader
*/
vertex_location = glGetUniformLocation(program, "thevertex");
mvp_location = glGetUniformLocation(program, "MVP");
glBindVertexArray(VAO);
/**
* Create and bind texture
*/
character_T* character = get_character('s');
unsigned int texture = character->texture;
float scale = 1.0f;
GLfloat xpos = 0;
GLfloat ypos = 0;
GLfloat w = character->width * scale;
GLfloat h = character->height * scale;
GLfloat vertices[6][4] = {
{ xpos, ypos + h, 0.0, 0.0 },
{ xpos, ypos, 0.0, 1.0 },
{ xpos + w, ypos, 1.0, 1.0 },
{ xpos, ypos + h, 0.0, 0.0 },
{ xpos + w, ypos, 1.0, 1.0 },
{ xpos + w, ypos + h, 1.0, 0.0 }
};
/**
* Main loop
*/
while (!glfwWindowShouldClose(window))
{
int width, height;
mat4 p, mvp;
double t = glfwGetTime();
glfwGetFramebufferSize(window, &width, &height);
glViewport(0, 0, width, height);
glClear(GL_COLOR_BUFFER_BIT);
mat4 m = GLM_MAT4_IDENTITY_INIT;
glm_translate(m, (vec3){ -sin(t), -cos(t), 0 });
glm_ortho_default(width / (float) height, p);
glm_mat4_mul(p, m, mvp);
glUseProgram(program);
glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp);
/**
* Draw texture
*/
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(VAO);
glBindTexture(GL_TEXTURE_2D, texture);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(vertex_location);
glVertexAttribPointer(vertex_location, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}
thevertex is a vertex attribute.
attribute vec4 thevertex;
thus you have to get the resource index of the attribute by glGetAttribLocation rather than glGetUniformLocation.
vertex_location = glGetUniformLocation(program, "thevertex");
vertex_location = glGetAttribLocation(program, "thevertex");
glGetUniformLocation is meant to retrieve the the active resource index (location) of an uniform variable.
I'm trying to render a textured triangle. I'm using STB image to load a png. The triangle is displayed as a single color for a host of different images that I've tried. The color seems to be an average of the total colors displayed in the image.
I've looked into many solutions for similar questions and have ensured (or so I think) that I've not fallen into the common 'gotchas': glEnable(GL_TEXTURE_2D) is called. My calls to glVertexAttribPoitners() are set up correctly. I've tried to tell OpenGl that I don't want to use mipmapping with glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR), and I've enabled the relevant vertex arrays via glEnableVertexAttribArray(). My shaders compile with no errors.
#include <practice/practice.h>
#include <test_dep/stb_image.h>
void
initRenderer_practice()
{
const char* vertex_shader = GLSL(450 core,
layout (location = 0) in vec2 vCoords;
layout (location = 1) in vec2 tCoords;
out vec2 ftCoords;
void main()
{
gl_Position = vec4(vCoords.x, vCoords.y, 0.0, 1.0);
ftCoords = tCoords;
}
);
const char* fragment_shader = GLSL(450 core,
uniform sampler2D texture_data;
in vec2 ftCoords;
out vec4 fragColor;
void main()
{
fragColor = texture(texture_data, ftCoords.st);
}
);
GLfloat vCoordsLocal[] =
{
//
// POSITION COORDS
//
// bottom left
-0.5, -0.5,
// bottom right
0.5, -0.5,
// top center
0.0, 0.5,
//
// TEXTURE COORDS
//
// left bottom
0.0, 0.0,
// right bottom
1.0, 0.0,
// center top
0.5, 1.0,
};
if (!practice_target)
{
practice_target = (uGLRenderTarget*)malloc(sizeof(uGLRenderTarget));
practice_target->shader_program = 0;
practice_target->vertex_array_object = (GLuint) -1;
practice_target->vertex_buffer_object = (GLuint) -1;
}
practice_target->shader_program = uGLCreateShaderProgram_vf(&vertex_shader,
&fragment_shader);
assert(practice_target->shader_program);
glUseProgram(practice_target->shader_program);
glError;
glGenVertexArrays(1, &practice_target->vertex_array_object);
glBindVertexArray(practice_target->vertex_array_object);
glError;
glGenBuffers(1, &practice_target->vertex_buffer_object);
glBindBuffer(GL_ARRAY_BUFFER, practice_target->vertex_buffer_object);
glBufferData(GL_ARRAY_BUFFER,
sizeof(vCoordsLocal),
vCoordsLocal,
GL_STATIC_DRAW);
glError;
glGenTextures(1, &practice_target->texture_id);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, practice_target->texture_id);
practice_target->shdr_texture_2d_location = glGetUniformLocation(practice_target->shader_program, "texture_data");
glUniform1i(practice_target->shdr_texture_2d_location, 0);
assert(practice_target->shdr_texture_2d_location != -1);
glError;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glError;
stbi_set_flip_vertically_on_load(true);
u8* texture_data = NULL;
GLint width, height, channels = 0;
texture_data = stbi_load("./assets/index.png", &width, &height, &channels, 0);
if (texture_data)
{
/*
void glTexImage2D(GLenum target,
GLint level,
GLint internalFormat,
GLsizei width,
GLsizei height,
GLint border,
GLenum format,
GLenum type,
const GLvoid * data);
*/
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGB,
width,
height,
0,
GL_RGB,
GL_UNSIGNED_BYTE,
texture_data);
glGenerateMipmap(GL_TEXTURE_2D);
glError;
printf("\n\n numchannels: %d\n\n", channels);
}
else
{
puts("[ debug ] Load texture failed\n");
assert(0);
}
stbi_image_free(texture_data);
/*
void glVertexAttribPointer(GLuint index,
GLint size,
GLenum type,
GLboolean normalized,
GLsizei stride,
const GLvoid * pointer);
*/
// Vertex coordinate attribute
glVertexAttribPointer(0,
2,
GL_FLOAT,
GL_FALSE,
0,
(void*) 0);
glEnableVertexAttribArray(0);
// Texture coordinate attribute
glVertexAttribPointer(1,
2,
GL_FLOAT,
GL_FALSE,
0,
(void*) 6);
glEnableVertexAttribArray(1);
glError;
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram(0);
glError;
}
void
render_practice()
{
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(practice_target->shader_program);
glEnable(GL_TEXTURE_2D);
glBindVertexArray(practice_target->vertex_array_object);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, practice_target->texture_id);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram(0);
glError;
}
The expected result is to have the texture mapped onto the triangle, rather than a single color.
The vertex attribute specification for the texture coordinates is misaligned.
The memory layout of the attributes is
x0 y0, x1, y1, x2, y2, u0 v0, u1, v1, u2, v2
So the first texture coordinate is 6th element in the buffer.
But, the last parameter of glVertexAttribPointer is a byte offset into the buffer object's data store.
This mean the offset has to be 6*sizeof(GLfloat) rather than 6
glVertexAttribPointer(1,
2,
GL_FLOAT,
GL_FALSE,
0,
(void*)(6*sizeof(GLfloat)); // <-----
I recommend to change the vertex attribute arrangement, to an extendable layout:
x0 y0 u0 v0 x1 y1 u1 v1 x2 y2 u2 v2 ...
and to use the corresponding vertex attribute specification:
GLsizei stride = (GLsizei)(4*sizeof(GLfloat)); // 4 because of (x, y, u ,v)
void* vertOffset = (void*)(0); // 0 because the vertex coords are 1st
void* texOffset = (void*)(2*sizeof(GLfloat)); // 2 because u, v are after x, y
// Vertex coordinate attribute
glVertexAttribPointer(0,
2,
GL_FLOAT,
GL_FALSE,
stride,
vertOffset);
glEnableVertexAttribArray(0);
// Texture coordinate attribute
glVertexAttribPointer(1,
2,
GL_FLOAT,
GL_FALSE,
stride,
texOffset;
glEnableVertexAttribArray(1);
It's been a while since I've wrote some graphics programming and I've been staring at this for quite some time now and can't figure out what i'm doing wrong.
This is how I am creating my texture.
GLuint create_texture(const char* filename) {
SDL_Surface* surface;
GLenum tex_format;
GLint num_colors;
GLuint tex_id;
char* file_path;
file_path = get_resource(filename);
surface = IMG_Load(file_path);
if (!surface) {
SDL_Log("failed to create surface\n");
SDL_Quit();
return -1;
} else {
if ((surface->w & (surface->w - 1)) != 0) {
SDL_Log("image { %s } width is not power of 2\n", filename);
}
if ((surface->h & (surface->h - 1)) != 0) {
SDL_Log("image { %s } height is not power of 2\n", filename);
}
num_colors = surface->format->BytesPerPixel;
if (num_colors == 4) {
if (surface->format->Rmask == 0x000000ff)
tex_format = GL_RGBA;
else
tex_format = GL_BGRA;
}
if (num_colors == 3) {
if (surface->format->Rmask == 0x000000ff)
tex_format = GL_RGB;
else
tex_format = GL_BGR;
} else {
SDL_Log("pixel image format shouldn't get here! Quitting\n");
SDL_Quit();
};
glGenTextures(1, &tex_id);
glBindTexture(GL_TEXTURE_2D, tex_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, tex_format, surface->w, surface->h, 0,
tex_format, GL_UNSIGNED_BYTE, surface->pixels);
}
if (surface) SDL_FreeSurface(surface);
free(file_path);
return tex_id;
}
this code above works correctly for loading the image data.
vertex shader
#version 330 core
layout (location = 0) in vec3 a_pos;
layout (location = 1) in vec4 a_col;
layout (location = 2) in vec2 a_tex;
uniform mat4 u_mvp_mat;
uniform mat4 u_mod_mat;
uniform mat4 u_view_mat;
uniform mat4 u_proj_mat;
out vec4 f_color;
out vec2 f_tex;
void main()
{
gl_Position = u_mvp_mat * vec4(a_pos, 1.0);
f_tex = a_tex;
f_color = a_col;
}
fragment shader.
#version 330 core
in vec4 f_color;
out vec4 o_color;
in vec2 f_tex;
uniform sampler2D u_sprite_tex;
void main (void)
{
o_color = f_color;
o_color = texture(u_sprite_tex, f_tex);
}
this is how i setup my VBO
quad = ren2d_new(); //just a wrapper around a quad or two tri.
ren2d_set_tint(quad, 0, 1, 0, 1); //setting the color to green
pos_loc = get_attrib_location(ce_get_default_shader(), "a_pos");
col_loc = get_attrib_location(ce_get_default_shader(), "a_col");
mvp_matrix_loc = get_uniform_location(ce_get_default_shader(), "u_mvp_mat");
model_mat_loc = get_uniform_location(ce_get_default_shader(), "u_mod_mat");
view_mat_loc = get_uniform_location(ce_get_default_shader(), "u_view_mat");
proj_matrix_loc =
get_uniform_location(ce_get_default_shader(), "u_proj_mat");
tex_loc = get_uniform_location(ce_get_default_shader(), "u_sprite_tex");
camera = cam_2d_new(ce_get_width(), ce_get_height());
model_mat = mat4_identity();
mat4_scale(model_mat, 128, 128, 1);
tex_id = create_texture("test.png");
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vert_buff);
glBindBuffer(GL_ARRAY_BUFFER, vert_buff);
glBufferData(GL_ARRAY_BUFFER, sizeof(quad->vertices), quad->vertices,
GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &col_buff);
glBindBuffer(GL_ARRAY_BUFFER, col_buff);
glBufferData(GL_ARRAY_BUFFER, sizeof(quad->colors), quad->colors,
GL_STATIC_DRAW);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(quad->tex_coords),
quad->tex_coords);
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &ind_buff);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ind_buff);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quad->indices), quad->indices,
GL_STATIC_DRAW);
glBindVertexArray(0);
this is my render function:
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//could my texturing problems be here?
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_id);
glUniform1i(tex_loc, 0);
glUseProgram(ce_get_default_shader()->shader_program);
glBindVertexArray(vao);
//excuse the silly names
ce_get_view_matrices(&vview_mat, &pproj_mat, &mmvp_mat);
mat4_multi(&mmvp_mat, &vview_mat, model_mat);
mat4_multi(&mmvp_mat, &pproj_mat, &mmvp_mat);
glUniformMatrix4fv(mvp_matrix_loc, 1, GL_FALSE, mat4_get_data(&mmvp_mat));
glUniformMatrix4fv(model_mat_loc, 1, GL_FALSE, mat4_get_data(model_mat));
glUniformMatrix4fv(view_mat_loc, 1, GL_FALSE, mat4_get_data(&vview_mat));
glUniformMatrix4fv(proj_matrix_loc, 1, GL_FALSE, mat4_get_data(&pproj_mat));
glDrawElements(GL_TRIANGLES, quad->vertex_count, GL_UNSIGNED_SHORT, 0);
glBindVertexArray(0);
I am just getting a black quad. I can't track it down and i've been staring at this for quite a few hours now.
doing some very basic testing in the fragment shader like this.
#version 330 core
in vec4 f_color;
out vec4 o_color;
in vec2 f_tex;
uniform sampler2D u_sprite_tex;
void main (void)
{
//o_color = f_color;
vec4 c;
if(f_tex.y == 1.0) { c = vec4(1.0, 0.0, 0.0, 1.0); }
if(f_tex.y == 0.0) { c = vec4(0.0, 0.0, 1.0, 1.0); }
o_color = c;
o_color = texture(u_sprite_tex, f_tex);
}
my quads turn out to be blue both along the x and the y axis. That seems to be that the only value being passed in in 0.0 which doesn't make sense.
okay this was definitely a problem of my own creation. The reason that I was getting segmentation fault was because of this code.
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vert_buff);
glBindBuffer(GL_ARRAY_BUFFER, vert_buff);
glBufferData(GL_ARRAY_BUFFER, sizeof(quad->vertices), quad->vertices,
GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &col_buff);
glBindBuffer(GL_ARRAY_BUFFER, col_buff);
glBufferData(GL_ARRAY_BUFFER, sizeof(quad->colors), quad->colors,
GL_STATIC_DRAW);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0); //right here
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(quad->tex_coords),
quad->tex_coords);
glEnableVertexAttribArray(2);
glGenBuffers(1, &ind_buff);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ind_buff);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quad->indices), quad->indices,
GL_STATIC_DRAW);
glBindVertexArray(0);
after you bind your vbo buffer to zero attempting to access it will cause a segmentation fault...
After that just making sure that I sent the proper data to the gpu and all other code worked as expected.
GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &col_buff);
glBindBuffer(GL_ARRAY_BUFFER, col_buff);
glBufferData(GL_ARRAY_BUFFER, sizeof(quad->colors), quad->colors,
GL_STATIC_DRAW);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
//glBindBuffer(GL_ARRAY_BUFFER, 0); //right here remove this
//properly setup the local data to gpu data
glBufferData(GL_ARRAY_BUFFER, rend2d_get_sizeof_tex_coord(quad),
rend2d_get_tex_coord_data(quad), GL_STATIC_DRAW);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, 0); //put it here after finished with vbo
after those two changes everything worked as expected. Hopefully this can save someone some time.
There are at least two things that might be wrong:
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(quad->tex_coords),
quad->tex_coords);
Are you sure that quad->tex_coords contains 4-dimensional vectors? The fragment shader expects vec2. If you only supply two floats per vertex, you have to change the second parameter to two. When OpenGL tries to read from a too small ARRAY_BUFFER this can also cause segmentation faults. Note, that attributes are set to inactive if they do not contribute to the final color, thus the texture coordinates might only be read when textures are used.
Second: You are calling glGenerateMipmap before uploading data to the texture.
I'm having issues loading a texture onto my triangle strips. I'm following Anton Gerdelan's tutorial, and after failing with my main program, I went back to the basics and just tried to make a plain square and put his texture on it (the skull and crossbones).
I completely copy and pasted code from his "Hello Triangle" page, which worked, but once trying to fit in code from his texture tutorial above (and changing the triangle to a square), all I'm getting is a big white square with no texture.
I've checked the status of my shaders with glGetShaderiv() and they returned positive, I checked the image I loaded to see if the pixel data was sensible, so I believe my error is in declaring my VBOs, or the order/parameters in which I'm using them.
Here's the complete code which I copied, which compiles fine in Visual Studio 2013, except the output isn't what is expected.
I am using the static libraries of GLEW and GLFW, along with the STBI Image header
#include <GL/glew.h> // include GLEW and new version of GL on Windows
#include <GL/glfw3.h> // GLFW helper library
#include <stdio.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb/stb_image.h>
const char* vertex_shader =
"#version 400\n"
"in vec3 vp;"
"layout (location=1) in vec2 vt; // per-vertex texture co-ords"
"out vec2 texture_coordinates; "
"void main () {"
" gl_Position = vec4 (vp, 1.0);"
" texture_coordinates = vt; "
"}";
const char* fragment_shader =
"#version 400\n"
"in vec2 texture_coordinates;"
"uniform sampler2D basic_texture;"
"out vec4 frag_colour;"
"void main () {"
"vec4 texel = texture(basic_texture, texture_coordinates);"
"frag_colour = texel; "
"}";
float points[] = {
-0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.5f, 0.5f, 0.0f
};
float texcoords[] = {
0.0f, 1.0f,
0.0f, 0.0f,
1.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0
};
GLFWwindow* window;
unsigned int vt_vbo;
unsigned int tex = 0;
GLuint vao = 0;
GLuint vbo = 0;
GLuint shader_programme;
void initializeGL(){
// start GL context and O/S window using the GLFW helper library
if (!glfwInit()) {
printf("ERROR: could not start GLFW3\n");
return;
}
window = glfwCreateWindow(640, 480, "Texture Test", NULL, NULL);
if (!window) {
printf("ERROR: could not open window with GLFW3\n");
glfwTerminate();
return;
}
glfwMakeContextCurrent(window);
// start GLEW extension handler
glewExperimental = GL_TRUE;
glewInit();
// get version info
const GLubyte* renderer = glGetString(GL_RENDERER); // get renderer string
const GLubyte* version = glGetString(GL_VERSION); // version as a string
printf("Renderer: %s\n", renderer);
printf("OpenGL version supported %s\n", version);
// tell GL to only draw onto a pixel if the shape is closer to the viewer
glEnable(GL_DEPTH_TEST); // enable depth-testing
glDepthFunc(GL_LESS); // depth-testing interprets a smaller value as "closer"
}
void startShaders(){
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, &vertex_shader, NULL);
glCompileShader(vs);
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, &fragment_shader, NULL);
glCompileShader(fs);
shader_programme = glCreateProgram();
glAttachShader(shader_programme, fs);
glAttachShader(shader_programme, vs);
glLinkProgram(shader_programme);
GLint vsstat;
glGetShaderiv(vs, GL_COMPILE_STATUS, &vsstat);
GLint fsstat;
glGetShaderiv(fs, GL_COMPILE_STATUS, &fsstat);
printf("%i\n%i\n", vsstat, fsstat);
}
void loadImage(){
int x, y, n;
int force_channels = 4;
unsigned char* image_data = stbi_load("skulluvmap.png", &x, &y, &n, force_channels);
if (!image_data) {
printf("ERROR: could not load %s\n", "skulluvmap.png");
}
int width_in_bytes = x * 4;
unsigned char *top = NULL;
unsigned char *bottom = NULL;
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++;
}
}
printf("first 4 bytes are: %i %i %i %i\n",
image_data[0], image_data[1], image_data[2], image_data[3]
);
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);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
void generateBuffers(){
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), points, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(0); // don't forget this!
glGenBuffers(1, &vt_vbo);
glBindBuffer(GL_ARRAY_BUFFER, vt_vbo);
glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), texcoords, GL_STATIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(1); // don't forget this!
}
void mainLoop(){
while (!glfwWindowShouldClose(window)) {
// wipe the drawing surface clear
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
int tex_loc = glGetUniformLocation(shader_programme, "basic_texture");
glUseProgram(shader_programme);
glUniform1i(tex_loc, 0); // use active texture 0
// draw points 0-4 from the currently bound VAO with current in-use shader
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// update other events like input handling
glfwPollEvents();
// put the stuff we've been drawing onto the display
glfwSwapBuffers(window);
}
}
int main() {
initializeGL();
startShaders();
loadImage();
generateBuffers();
mainLoop();
// close GL context and any other GLFW resources
glfwTerminate();
return 0;
}
You're misusing your second buffer which is supposed to be the buffer with texcoords. So what you really want to achieve is having a pair of texture coordinates for every vertex. It means that you texcoords array should in fact store 4 pairs because you have 4 triples in the points array. So that's the first fix. You probably want it to look like:
float texcoords[] = {
0.0f, 1.0f,
0.0f, 0.0f,
1.0, 0.0,
1.0, 1.0,
};
Then in the generateBuffers, your vt_vbo is wrong. The data should be passed this way:
glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(float), texcoords, GL_STATIC_DRAW);
because you only want to pass 8 values there. 2 texcoords for each vertex.
Edit:
This however, doesn't fully explain why your texture doesn't appear at all. I primarily thought that there might be a problem with your texcoords pointer but it doesn't seem to be the case.
I am having some trouble in OpenGL, making a render to texture example work. At initialization, i generate a texture 'randtex' with random values of green and black. If i render this texture directly to the window (mapped into a quad) it works all well.
like this:
But if i render 'randtex' into another texture 'tex' which is attached to a framebuffer object, then rendering 'tex' on the screen just gives me a black image on the fbo's blue background and from what i know it should give me the original texture over the blue background. In other words, this is what i am getting
vertex shader for display only (display_shaderp).
#version 420
in vec4 pos;
in vec2 tex_coord;
out vec2 vtex_coord;
uniform mat4 projection;
uniform mat4 modelview;
void main(){
gl_Position = projection * modelview * pos;
vtex_coord = tex_coord;
}
fragment shader for display only (display_shaderp)
#version 420
in vec2 vtex_coord;
uniform sampler2D tex;
out vec4 color;
void main(){
color = texture2D(tex, vtex_coord);
//color = vec4(1.0f, 1.0f, 1.0f, 1.0f);
}
shader program compiles and links ok, i get no gl errors and framebuffer is complete without errors too.
This is the rendering code:
glClearColor(0.5, 0.5, 0.5, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, win_width, win_height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0f, (GLfloat)win_width / (GLfloat) win_height, 0.1f, 50.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslate3f(0.0f, 0.0f, -3.0f)
// render to texture
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glViewport(0,0, win_width, win_height);
glClearColor(0.0, 0.0, 0.5, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(fbo_shaderp);
GLfloat m_matrix[16], p_matrix[16];
glGetFloatv(GL_MODELVIEW_MATRIX, m_matrix);
glGetFloatv(GL_PROJECTION_MATRIX, p_matrix);
glUniformMatrix4fv(glGetUniformLocation(fbo_shaderp, "modelview"),1,GL_FALSE,m_matrix);
glUniformMatrix4fv(glGetUniformLocation(fbo_shaderp, "projection"),1,GL_FALSE,p_matrix);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, randtex);
glUniform1i(glGetUniformLocation(fbo_shaderp, "tex"), randtex);
GLuint _p = glGetAttribLocation(fbo_shaderp, "pos");
GLuint _t = glGetAttribLocation(fbo_shaderp, "tex_coord");
glVertexAttribPointer(_p, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), 0);
glVertexAttribPointer(_t, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(sizeof(float)*3));
glEnableVertexAttribArray(_p);
glEnableVertexAttribArray(_t);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, 0);
glUseProgram(0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// render to the window
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0,0, win_width, win_height);
glClearColor(0.5, 0.5, 0.5, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(display_shaderp);
glUniformMatrix4fv(glGetUniformLocation(display_shaderp,"modelview"),1,GL_FALSE,m_matrix);
glUniformMatrix4fv(glGetUniformLocation(display_shaderp,"projection"),1,GL_FALSE,p_matrix);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
glUniform1i(glGetUniformLocation(display_shaderp, "tex"), 0);
GLuint _p = glGetAttribLocation(display_shaderp, "pos");
GLuint _t = glGetAttribLocation(display_shaderp, "tex_coord");
glVertexAttribPointer ( _p, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), 0 );
glVertexAttribPointer ( _t, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*) (sizeof(float) * 3) );
glEnableVertexAttribArray(_p);
glEnableVertexAttribArray(_t);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, 0);
glUseProgram(0);
And the code to create textures and framebuffer
int i = 0;
// create a random texture 'randtex'
glGenTextures(1, &randtex);
glBindTexture(GL_TEXTURE_2D, randtex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// random data
GLubyte* data = (GLubyte *) malloc(width*height*4*sizeof(GLubyte));
GLubyte val;
for (i = 0; i < width * height * 4; i+=4){
if ((double)rand()/(double)RAND_MAX > 0.8)
val = 255;
else
val = 0;
data[i] = 0;
data[i+1] = val;
data[i+2] = 0;
data[i+3] = 255;
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,GL_UNSIGNED_BYTE, data);
// create an empty texture 'tex'
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,GL_UNSIGNED_BYTE, 0);
// create framebuffer and attach 'tex'
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D, tex, 0);
GLenum status;
if ((status = glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE)
fprintf(stderr, "glCheckFramebufferStatus: error %p", status);
shaders for render to texture (fbo_shaderp)
render to texture vertex shader
in vec4 pos;
in vec2 tex_coord;
out vec2 vtex_coord;
uniform mat4 projection;
uniform mat4 modelview;
void main(){
gl_Position = projection * modelview * pos;
vtex_coord = tex_coord;
}
render to texture fragment shader
#version 420
in vec2 vtex_coord;
layout(location = 0) out vec4 color;
uniform sampler2D tex;
void main(){
color = texture2D(tex, vtex_coord);
//color = vec4(1.0f, 1.0f, 1.0f, 1.0f);
}
In this last shader, if i use the commented line to paint all white and comment out the
texture one, i do get a white image but also opengl error right after rendering to texture "OpenGL Error: invalid value", so this actually confuses me more.
glUniform1i(glGetUniformLocation(fbo_shaderp, "tex"), randtex);
You must not give the ID of the texture, but the slot you bind the texture to. So in your case, that should be
glUniform1i(glGetUniformLocation(fbo_shaderp, "tex"), 0);