OpenGL SL barrel distortion: what's wrong with this SDL2 code - c

UPDATE
Ok, I read Learn OpenGL book from https://learnopengl.com/ and changed the OP code in order to support OpenGL 3.3 and later. I don't have the time now for writing the complete solution but if someone is interested ask!
OLD OP
I wrote an emulator using SDL2 and what I want is to add a barrel distortion, like an old CRT, to final rectangular video frame texture.
OpenGL SL vertex and fragment shaders come from here.
This is the code, icon.bmp is here:
#include <stdio.h>
#include "SDL.h"
#include "SDL_opengl.h"
static SDL_bool shaders_supported;
static int current_shader = 0;
enum
{
SHADER_1,
SHADER_2,
NUM_SHADERS
};
typedef struct
{
GLhandleARB program;
GLhandleARB vert_shader;
GLhandleARB frag_shader;
const char *vert_source;
const char *frag_source;
} ShaderData;
static ShaderData shaders[NUM_SHADERS] =
{
// SHADER_1
{0, 0, 0,
/* vertex shader */
"varying vec4 v_color;\n"
"varying vec2 v_texCoord;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
" v_color = gl_Color;\n"
" v_texCoord = vec2(gl_MultiTexCoord0);\n"
"}",
/* fragment shader */
"varying vec4 v_color;\n"
"varying vec2 v_texCoord;\n"
"uniform sampler2D tex0;\n"
"\n"
"void main()\n"
"{\n"
" gl_FragColor = texture2D(tex0, v_texCoord) * v_color;\n"
"}"
},
// SHADER_2
{0, 0, 0,
/* vertex shader */
"varying vec4 Vertex_UV;\n"
"uniform mat4 gxl3d_ModelViewProjectionMatrix;\n"
"void main()\n"
"{\n"
" gl_Position = gxl3d_ModelViewProjectionMatrix * gl_Vertex;\n"
" Vertex_UV = gl_MultiTexCoord0;\n"
"}",
/* fragment shader */
"uniform sampler2D tex0;\n"
"varying vec4 Vertex_UV;\n"
"const float PI = 3.1415926535;\n"
"uniform float BarrelPower;\n"
"\n"
"vec2 Distort(vec2 p)\n"
"{\n"
" float theta = atan(p.y, p.x);\n"
" float radius = length(p);\n"
" radius = pow(radius, BarrelPower);\n"
" p.x = radius * cos(theta);\n"
" p.y = radius * sin(theta);\n"
" return 0.5 * (p + 1.0);\n"
"}\n"
"\n"
"void main()\n"
"{\n"
" vec2 xy = 2.0 * Vertex_UV.xy - 1.0;\n"
" vec2 uv;\n"
" float d = length(xy);\n"
" if (d < 1.0)\n"
" {\n"
" uv = Distort(xy);\n"
" }\n"
" else\n"
" {\n"
" uv = Vertex_UV.xy;\n"
" }\n"
" vec4 c = texture2D(tex0, uv);\n"
" gl_FragColor = c;\n"
"}"
}
};
static PFNGLATTACHOBJECTARBPROC glAttachObjectARB;
static PFNGLCOMPILESHADERARBPROC glCompileShaderARB;
static PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB;
static PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB;
static PFNGLDELETEOBJECTARBPROC glDeleteObjectARB;
static PFNGLGETINFOLOGARBPROC glGetInfoLogARB;
static PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB;
static PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB;
static PFNGLLINKPROGRAMARBPROC glLinkProgramARB;
static PFNGLSHADERSOURCEARBPROC glShaderSourceARB;
static PFNGLUNIFORM1IARBPROC glUniform1iARB;
static PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB;
static SDL_bool CompileShader(GLhandleARB shader, const char *source)
{
GLint status = 0;
glShaderSourceARB(shader, 1, &source, NULL);
glCompileShaderARB(shader);
glGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &status);
if (status == 0)
{
GLint length = 0;
char *info;
glGetObjectParameterivARB(shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
info = (char *)SDL_malloc(length + 1);
if (!info)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!");
}
else
{
glGetInfoLogARB(shader, length, NULL, info);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile shader:\n%s\n%s", source, info);
SDL_free(info);
}
return SDL_FALSE;
}
else
return SDL_TRUE;
}
static SDL_bool LinkProgram(ShaderData *data)
{
GLint status = 0;
glAttachObjectARB(data->program, data->vert_shader);
glAttachObjectARB(data->program, data->frag_shader);
glLinkProgramARB(data->program);
glGetObjectParameterivARB(data->program, GL_OBJECT_LINK_STATUS_ARB, &status);
if (status == 0)
{
GLint length = 0;
char *info;
glGetObjectParameterivARB(data->program, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
info = (char *) SDL_malloc(length + 1);
if (!info)
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!");
else
{
glGetInfoLogARB(data->program, length, NULL, info);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to link program:\n%s", info);
SDL_free(info);
}
return SDL_FALSE;
}
else
return SDL_TRUE;
}
static SDL_bool CompileShaderProgram(ShaderData *data)
{
const int num_tmus_bound = 4;
int i;
GLint location;
glGetError();
/* Create one program object to rule them all */
data->program = glCreateProgramObjectARB();
/* Create the vertex shader */
data->vert_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
if (!CompileShader(data->vert_shader, data->vert_source))
return SDL_FALSE;
/* Create the fragment shader */
data->frag_shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
if (!CompileShader(data->frag_shader, data->frag_source))
return SDL_FALSE;
/* ... and in the darkness bind them */
if (!LinkProgram(data))
return SDL_FALSE;
/* Set up some uniform variables */
glUseProgramObjectARB(data->program);
for (i = 0; i < num_tmus_bound; ++i)
{
char tex_name[5];
SDL_snprintf(tex_name, SDL_arraysize(tex_name), "tex%d", i);
location = glGetUniformLocationARB(data->program, tex_name);
if (location >= 0)
glUniform1iARB(location, i);
}
glUseProgramObjectARB(0);
return (glGetError() == GL_NO_ERROR) ? SDL_TRUE : SDL_FALSE;
}
static void DestroyShaderProgram(ShaderData *data)
{
if (shaders_supported)
{
glDeleteObjectARB(data->vert_shader);
glDeleteObjectARB(data->frag_shader);
glDeleteObjectARB(data->program);
}
}
static SDL_bool InitShaders()
{
int i;
/* Check for shader support */
shaders_supported = SDL_FALSE;
if (SDL_GL_ExtensionSupported("GL_ARB_shader_objects") &&
SDL_GL_ExtensionSupported("GL_ARB_shading_language_100") &&
SDL_GL_ExtensionSupported("GL_ARB_vertex_shader") &&
SDL_GL_ExtensionSupported("GL_ARB_fragment_shader"))
{
glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) SDL_GL_GetProcAddress("glAttachObjectARB");
glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) SDL_GL_GetProcAddress("glCompileShaderARB");
glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glCreateProgramObjectARB");
glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) SDL_GL_GetProcAddress("glCreateShaderObjectARB");
glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) SDL_GL_GetProcAddress("glDeleteObjectARB");
glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) SDL_GL_GetProcAddress("glGetInfoLogARB");
glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) SDL_GL_GetProcAddress("glGetObjectParameterivARB");
glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) SDL_GL_GetProcAddress("glGetUniformLocationARB");
glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) SDL_GL_GetProcAddress("glLinkProgramARB");
glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) SDL_GL_GetProcAddress("glShaderSourceARB");
glUniform1iARB = (PFNGLUNIFORM1IARBPROC) SDL_GL_GetProcAddress("glUniform1iARB");
glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glUseProgramObjectARB");
if (glAttachObjectARB && glCompileShaderARB && glCreateProgramObjectARB && glCreateShaderObjectARB &&
glDeleteObjectARB && glGetInfoLogARB && glGetObjectParameterivARB && glGetUniformLocationARB &&
glLinkProgramARB && glShaderSourceARB && glUniform1iARB && glUseProgramObjectARB)
shaders_supported = SDL_TRUE;
}
if (!shaders_supported)
return SDL_FALSE;
/* Compile all the shaders */
for (i = 0; i < NUM_SHADERS; ++i)
{
if (!CompileShaderProgram(&shaders[i]))
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to compile shader!\n");
return SDL_FALSE;
}
}
/* We're done! */
return SDL_TRUE;
}
static void QuitShaders()
{
int i;
for (i = 0; i < NUM_SHADERS; ++i)
DestroyShaderProgram(&shaders[i]);
}
/* Quick utility function for texture creation */
static int power_of_two(int input)
{
int value = 1;
while (value < input)
value <<= 1;
return value;
}
GLuint
SDL_GL_LoadTexture(SDL_Surface * surface, GLfloat * texcoord)
{
GLuint texture;
int w, h;
SDL_Surface *image;
SDL_Rect area;
SDL_BlendMode saved_mode;
/* Use the surface width and height expanded to powers of 2 */
w = power_of_two(surface->w);
h = power_of_two(surface->h);
texcoord[0] = 0.0f; /* Min X */
texcoord[1] = 0.0f; /* Min Y */
texcoord[2] = (GLfloat) surface->w / w; /* Max X */
texcoord[3] = (GLfloat) surface->h / h; /* Max Y */
image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32,
#if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */
0x000000FF,
0x0000FF00,
0x00FF0000,
0xFF000000
#else
0xFF000000,
0x00FF0000,
0x0000FF00,
0x000000FF
#endif
);
if (image == NULL)
return 1;
/* Save the alpha blending attributes */
SDL_GetSurfaceBlendMode(surface, &saved_mode);
SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE);
/* Copy the surface into the GL texture image */
area.x = 0;
area.y = 0;
area.w = surface->w;
area.h = surface->h;
SDL_BlitSurface(surface, &area, image, &area);
/* Restore the alpha blending attributes */
SDL_SetSurfaceBlendMode(surface, saved_mode);
/* Create an OpenGL texture for the image */
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels);
SDL_FreeSurface(image); /* No longer needed */
return texture;
}
/* A general OpenGL initialization function. Sets all of the initial parameters. */
void InitGL(int Width, int Height) /* We call this right after our OpenGL window is created. */
{
GLdouble aspect;
glViewport(0, 0, Width, Height);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); /* This Will Clear The Background Color To Black */
glClearDepth(1.0); /* Enables Clearing Of The Depth Buffer */
glDepthFunc(GL_LESS); /* The Type Of Depth Test To Do */
glEnable(GL_DEPTH_TEST); /* Enables Depth Testing */
glShadeModel(GL_SMOOTH); /* Enables Smooth Color Shading */
glMatrixMode(GL_PROJECTION);
glLoadIdentity(); /* Reset The Projection Matrix */
aspect = (GLdouble)Width / Height;
glOrtho(-1.0, 1.0, -1.0 / aspect, 1.0 / aspect, 0.0, 1.0);
glMatrixMode(GL_MODELVIEW);
}
/* The main drawing function. */
void DrawGLScene(SDL_Window *window, GLuint texture, GLfloat * texcoord)
{
/* Texture coordinate lookup, to make it simple */
enum
{
MINX,
MINY,
MAXX,
MAXY
};
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* Clear The Screen And The Depth Buffer */
glLoadIdentity(); /* Reset The View */
/* Enable blending */
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
/* draw a textured square (quadrilateral) */
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);
glColor3f(0.5f, 0.5f, 0.5f);
if (shaders_supported)
glUseProgramObjectARB(shaders[current_shader].program);
glBegin(GL_QUADS); /* start drawing a polygon (4 sided) */
glTexCoord2f(texcoord[MINX], texcoord[MINY]);
glVertex3f(-1.0f, 1.0f, 0.0f); /* Top Left */
glTexCoord2f(texcoord[MAXX], texcoord[MINY]);
glVertex3f( 1.0f, 1.0f, 0.0f); /* Top Right */
glTexCoord2f(texcoord[MAXX], texcoord[MAXY]);
glVertex3f( 1.0f,-1.0f, 0.0f); /* Bottom Right */
glTexCoord2f(texcoord[MINX], texcoord[MAXY]);
glVertex3f(-1.0f,-1.0f, 0.0f); /* Bottom Left */
glEnd(); /* done with the polygon */
if (shaders_supported)
glUseProgramObjectARB(0);
glDisable(GL_TEXTURE_2D);
/* swap buffers to display, since we're double buffered. */
SDL_GL_SwapWindow(window);
}
int main(int argc, char **argv)
{
int done;
SDL_Window *window;
SDL_Surface *surface;
GLuint texture;
GLfloat texcoords[4];
/* Enable standard application logging */
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
/* Initialize SDL for video output */
if ( SDL_Init(SDL_INIT_VIDEO) < 0 )
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to initialize SDL: %s\n", SDL_GetError());
exit(1);
}
/* Create a 500x500 OpenGL screen */
window = SDL_CreateWindow( "Shader Demo", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 500, 500, SDL_WINDOW_OPENGL );
if ( !window )
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create OpenGL window: %s\n", SDL_GetError());
SDL_Quit();
exit(2);
}
if ( !SDL_GL_CreateContext(window))
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create OpenGL context: %s\n", SDL_GetError());
SDL_Quit();
exit(3);
}
printf("Vendor graphic card: %s\n", glGetString(GL_VENDOR));
printf("Renderer: %s\n", glGetString(GL_RENDERER));
printf("Version GL: %s\n", glGetString(GL_VERSION));
printf("Version GLSL: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
surface = SDL_LoadBMP("icon.bmp");
if ( ! surface )
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to load icon.bmp: %s\n", SDL_GetError());
SDL_Quit();
exit(4);
}
texture = SDL_GL_LoadTexture(surface, texcoords);
SDL_FreeSurface(surface);
/* Loop, drawing and checking events */
InitGL(500, 500);
if (InitShaders())
SDL_Log("Shaders supported, press SPACE to cycle them.\n");
else
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Shaders not supported!\n");
done = 0;
while ( ! done )
{
DrawGLScene(window, texture, texcoords);
/* This could go in a separate function */
{ SDL_Event event;
while ( SDL_PollEvent(&event) )
{
if ( event.type == SDL_QUIT )
done = 1;
if ( event.type == SDL_KEYDOWN )
{
if ( event.key.keysym.sym == SDLK_SPACE )
current_shader = (current_shader + 1) % NUM_SHADERS;
if ( event.key.keysym.sym == SDLK_ESCAPE )
done = 1;
}
}
}
}
QuitShaders();
SDL_Quit();
return 0;
}
As you can see when you press SPACE on keyboard the second shader shows a black window. I tried many barrel/fish eye shaders from internet but I always got the same behaviour, so something is wrong in this code.
I used to develop with SDL2 so I know only the theory behind OpenGL SL and I don't want to learn all OpenGL.
Really appreciate any suggestion.

So the source you've used is not using modern OpenGL as you've noted. I've taken it as an example and nabbed some of your code to get a basic version working with modern OpenGL which I believe has the effect you're looking for. Note that I've used GLEW to write my version so I can get access to all the OpenGL functions.
Here is what happens when you press the spacebar in my version. I have the BarrelPower set at 2 as a constant in my shader, but you can play around with that value to inspect the results. You could also quite easily pass it in as an attribute for better configurability.
Below is my source code which I have commented up to explain the changes. I figure that's better than listing them here since you can take a local copy and study it. Between this and LearnOpenGL.com it should be enough to get you up and running.
Let me know if this helps!
#include <stdio.h>
#include <stdbool.h>
#include <GL/glew.h>
#include <SDL.h>
#include <SDL_opengl.h>
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
#define NUM_SHADERS 2
// Some globals since this is an example
SDL_Window* window = NULL;
SDL_GLContext glContext;
// Vertices (and texture coords) and indices
// See "SetupDrawing" function for their initialisation
float vertices[20];
int indices[6];
// Handles for out VBO and IBO
int vbo = -1;
int ibo = -1;
// Existing shader structure (unchanged)
typedef struct
{
GLhandleARB program;
GLhandleARB vert_shader;
GLhandleARB frag_shader;
const char* vert_source;
const char* frag_source;
} ShaderData;
// Array of shaders - see comments for updates
static ShaderData shaders[NUM_SHADERS] =
{
// SHADER_1
{0, 0, 0,
/* vertex shader - In our basic vertex shader you can see we take
* position and textureCoord attributes. These are setup from our
* global vertices array later in the code. You can also see that
* we are passing out a textureCoord value which is picked up by
* the fragment shader later on in the pipeline
*/
"#version 330\n"
"attribute vec3 position;\n"
"attribute vec2 texCoord;\n"
"out vec2 textureCoord;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = vec4(position, 1.0);\n"
" textureCoord = texCoord;\n"
"}",
/* fragment shader - Our fragment shader is very basic. It gets texture
coords passed through our vertex shader and a texture. We then use the
glsl texture function to perform our texturing. When you initially run
the app, this is the fragment shader that will be in use
*/
"#version 330\n"
"in vec2 textureCoord;\n"
"uniform sampler2D theTexture;\n"
"void main()\n"
"{\n"
" gl_FragColor = texture(theTexture, textureCoord);\n"
"}"
},
// SHADER_2
{0, 0, 0,
/* vertex shader - Our vertex shader remains unchanged for the second program.
The barrel distortion effect is being applied in the fragment shader only
*/
"#version 330\n"
"attribute vec3 position;\n"
"attribute vec2 texCoord;\n"
"out vec2 textureCoord;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = vec4(position, 1.0);\n"
" textureCoord = texCoord;\n"
"}",
/* fragment shader - Here is the barrel distortion fragment shader. Not
too much has changed here. The updates are simply to the expected inputs
which are the same as our basic fragment shader. Barrel power has been
made constant for simplicity. You can increase and decrease this value to
observe the effects. In the main the code is essentially the same, I've
just changed the Vertex_UV.xy to textureCoord since that's what would've
been in the Vertex_UV.x and .y variables in the old version. */
"#version 330\n"
"in vec2 textureCoord;\n"
"uniform sampler2D theTexture;\n"
"const float PI = 3.1415926535;\n"
"const float BarrelPower = 2.0;\n"
"\n"
"vec2 Distort(vec2 p)\n"
"{\n"
" float theta = atan(p.y, p.x);\n"
" float radius = length(p);\n"
" radius = pow(radius, BarrelPower);\n"
" p.x = radius * cos(theta);\n"
" p.y = radius * sin(theta);\n"
" return 0.5 * (p + 1.0);\n"
"}\n"
"\n"
"void main()\n"
"{\n"
" vec2 xy = 2.0 * textureCoord - 1.0;\n"
" vec2 uv;\n"
" float d = length(xy);\n"
" if (d < 1.0)\n"
" {\n"
" uv = Distort(xy);\n"
" }\n"
" else\n"
" {\n"
" uv = textureCoord;\n"
" }\n"
" vec4 c = texture2D(theTexture, uv);\n"
" gl_FragColor = c;\n"
"}"
}
};
bool Setup()
{
bool setupSuccess = false;
bool sdlInitialised = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) == 0;
if (sdlInitialised)
{
window = SDL_CreateWindow("Barrel Distortion Test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL);
if (window)
{
glContext = SDL_GL_CreateContext(window);
glewInit();
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
glClearColor(0.9f, 0.9f, 0.9f, 0.f);
setupSuccess = true;
}
else
{
printf("Could not initialise GL window\n");
}
}
else
{
printf("Could not initialise SDL\n");
}
return setupSuccess;
}
void Teardown()
{
SDL_DestroyWindow(window);
window = NULL;
glDeleteBuffers(1, &vbo);
glDeleteBuffers(1, &ibo);
}
void SetupDrawing()
{
// Setup vertices and texture coords. We setup a stride later for indexing these. The
// first three values on each line are the vertices, the last two are the texture
// coordinates (U, V)
vertices[0] = 1.0f; vertices[1] = 1.0f; vertices[2] = 0.0f; vertices[3] = 0.f; vertices[4] = 0.f;
vertices[5] = 1.0f; vertices[6] = -1.0f; vertices[7] = 0.0f; vertices[8] = 0.f; vertices[9] = 1.f;
vertices[10] = -1.0f; vertices[11] = -1.0f; vertices[12] = 0.0f; vertices[13] = 1.f; vertices[14] = 1.f;
vertices[15] = -1.0f; vertices[16] = 1.0f; vertices[17] = 0.0f; vertices[18] = 1.f; vertices[19] = 0.f;
// Setup indices
indices[0] = 0; indices[1] = 1; indices[2] = 3;
indices[3] = 1; indices[4] = 2; indices[5] = 3;
// Setup buffers
glGenBuffers(1, &vbo);
glGenBuffers(1, &ibo);
}
static SDL_bool CompileShader(GLhandleARB shader, const char* source)
{
GLint status = 0;
glShaderSource(shader, 1, &source, NULL);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == 0)
{
GLint length = 0;
char* info;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
info = (char*)SDL_malloc(length + 1);
if (!info)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!");
}
else
{
glGetInfoLogARB(shader, length, NULL, info);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile shader:\n%s\n%s", source, info);
//SDL_free(info);
}
return SDL_FALSE;
}
else
return SDL_TRUE;
}
static SDL_bool LinkProgram(ShaderData* data)
{
GLint status = 0;
glAttachShader(data->program, data->vert_shader);
glAttachShader(data->program, data->frag_shader);
glLinkProgram(data->program);
glGetProgramiv(data->program, GL_LINK_STATUS, &status);
if (status == 0)
{
GLint length = 0;
char* info;
glGetProgramiv(data->program, GL_INFO_LOG_LENGTH, &length);
info = (char*)SDL_malloc(length + 1);
if (!info)
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!");
else
{
glGetProgramInfoLog(data->program, length, NULL, info);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to link program:\n%s", info);
SDL_free(info);
}
return SDL_FALSE;
}
else
return SDL_TRUE;
}
static SDL_bool CompileShaderProgram(ShaderData* data)
{
const int num_tmus_bound = 4;
int i;
GLint location;
glGetError();
/* Create one program object to rule them all */
data->program = glCreateProgram();
/* Create the vertex shader */
data->vert_shader = glCreateShader(GL_VERTEX_SHADER);
if (!CompileShader(data->vert_shader, data->vert_source))
return SDL_FALSE;
/* Create the fragment shader */
data->frag_shader = glCreateShader(GL_FRAGMENT_SHADER);
if (!CompileShader(data->frag_shader, data->frag_source))
return SDL_FALSE;
/* ... and in the darkness bind them */
if (!LinkProgram(data))
return SDL_FALSE;
return (glGetError() == GL_NO_ERROR) ? SDL_TRUE : SDL_FALSE;
}
static void DestroyShaderProgram(ShaderData* data)
{
glDeleteShader(data->vert_shader);
glDeleteShader(data->frag_shader);
glDeleteProgram(data->program);
}
/* Quick utility function for texture creation */
static int power_of_two(int input)
{
int value = 1;
while (value < input)
value <<= 1;
return value;
}
GLuint SDL_GL_LoadTexture(SDL_Surface* surface/*, GLfloat* texcoord*/)
{
GLuint texture;
int w, h;
SDL_Surface* image;
SDL_Rect area;
SDL_BlendMode saved_mode;
/* Use the surface width and height expanded to powers of 2 */
w = power_of_two(surface->w);
h = power_of_two(surface->h);
image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32,
#if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */
0x000000FF,
0x0000FF00,
0x00FF0000,
0xFF000000
#else
0xFF000000,
0x00FF0000,
0x0000FF00,
0x000000FF
#endif
);
if (image == NULL)
return 1;
/* Save the alpha blending attributes */
SDL_GetSurfaceBlendMode(surface, &saved_mode);
SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE);
/* Copy the surface into the GL texture image */
area.x = 0;
area.y = 0;
area.w = surface->w;
area.h = surface->h;
SDL_BlitSurface(surface, &area, image, &area);
/* Restore the alpha blending attributes */
SDL_SetSurfaceBlendMode(surface, saved_mode);
/* Create an OpenGL texture for the image */
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels);
SDL_FreeSurface(image); /* No longer needed */
return texture;
}
static SDL_bool InitShaders()
{
int i;
/* Compile all the shaders */
for (i = 0; i < NUM_SHADERS; ++i)
{
if (!CompileShaderProgram(&shaders[i]))
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to compile shader!\n");
return SDL_FALSE;
}
}
/* We're done! */
return SDL_TRUE;
}
static void QuitShaders()
{
int i;
for (i = 0; i < NUM_SHADERS; ++i)
DestroyShaderProgram(&shaders[i]);
}
void Run()
{
bool shadersInitialised = InitShaders();
if (!shadersInitialised)
return;
SetupDrawing();
int texture = -1;
SDL_Surface* surface = SDL_LoadBMP("icon.bmp");
if (surface)
{
texture = SDL_GL_LoadTexture(surface);
}
int shaderIndex = 0;
SDL_Event e;
bool run = true;
while (run)
{
while (SDL_PollEvent(&e))
{
if (e.type == SDL_QUIT)
{
run = false;
}
else if (e.type == SDL_KEYDOWN & e.key.keysym.sym == SDLK_SPACE)
{
if (shaderIndex == 0)
{
shaderIndex = 1;
}
else if (shaderIndex == 1)
{
shaderIndex = 0;
}
}
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(shaders[shaderIndex].program);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
// Bind our vertices to our array
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
// Find our attributes in the current shader program and once found setup the
// attribute pointers
int position = glGetAttribLocation(shaders[shaderIndex].program, "position");
if (position != -1)
{
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0);
}
int textureCoord = glGetAttribLocation(shaders[shaderIndex].program, "texCoord");
if (textureCoord != -1)
{
glVertexAttribPointer(textureCoord, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(3 * sizeof(float)));
}
// Bind our indices to our array
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_DYNAMIC_DRAW);
// Enable vertex attrib array
glEnableVertexAttribArray(position);
glEnableVertexAttribArray(textureCoord);
// Draw
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// Disable vertex attrib array
glDisableVertexAttribArray(position);
glDisableVertexAttribArray(textureCoord);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
SDL_GL_SwapWindow(window);
}
}
int main(int argc, char** argv)
{
bool setupSuccess = Setup();
if (setupSuccess)
{
Run();
}
Teardown();
return 0;
}

Related

How can I set the "q" texture coordinate in openGL 3? [duplicate]

This question already has answers here:
Perspective correct texturing of trapezoid in OpenGL ES 2.0
(3 answers)
Closed 1 year ago.
I'm trying to apply a square texture to a trapezoid-like shape in OpenGL but I get some distortion. I've been reading a lot on possible solutions and the one that seems most convenient requires modifying the "q" texture coordinates. This is done using GlTexCoord functions in the solution; however, I'm using vertex buffers and I don't know how I can use them to change this coordinate this way. The texture init in GLSL takes a vec2; so I have no idea how I would pass anything but two-dimensional texture coordinates to it.
main.c
//C libs
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
//GL libs (Need to have set up to run this)
#include <glad/glad.h>
#include <GLFW/glfw3.h>
//Libs in folder
#include "shader.h"
#include "image.h"
//Window consts
#define WINDOW_HEIGHT 500
#define WINDOW_WIDTH 500
#define WINDOW_NAME "ForStackOverflow"
//Shader consts
#define VERTEX_SHADER_FILE "vertex_shader.glsl"
#define FRAGMENT_SHADER_FILE "fragment_shader.glsl"
//Vertex constants
#define POSITION_ATTRIBUTE_LOC 0
#define TEXTURE_COORD_ATTRIBUTE_LOC 1
#define POSITION_SIZE 2
#define TEXTURE_COORD_SIZE 2
#define VERTEX_SIZE (POSITION_SIZE + TEXTURE_COORD_SIZE) //Amount of floats per vertex
#define POSITION_OFFSET 0
#define TEXTURE_COORD_OFFSET (POSITION_SIZE * sizeof(float))
#define STRIDE (sizeof(float) * VERTEX_SIZE)
//Functions
static void framebuffer_size_callback(GLFWwindow*, int, int);
static unsigned int load_bmp_texture(const char* name);
int main()
{
printf("Running!\n");
//*GLFW
if (!glfwInit())
{
printf("GLFW init fail\n");
return -1;
}
//3.3 core
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//*Window object
GLFWwindow* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_NAME, NULL, NULL);
if (window == NULL)
{
printf("GLFW window fail\n");
return -1;
}
glfwMakeContextCurrent(window);
//*GLAD
if (!gladLoadGLLoader((GLADloadproc) &glfwGetProcAddress))
{
printf("GLAD init fail");
glfwTerminate();
return -1;
}
//*Window
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
glfwSetFramebufferSizeCallback(window, &framebuffer_size_callback);
//*Shaders
ShaderProgram shader_program;
if (!shader_program_init(&shader_program, VERTEX_SHADER_FILE, FRAGMENT_SHADER_FILE)) {
glfwTerminate();
return -1;
}
//*Triangle rendering
//Vertices
float tri_vertices[4 * VERTEX_SIZE] = { //FORM A TRAPEZOID
//Position //Texture coordinates
-0.5f, 0.5f, 0.0f, 1.0f, //Top-left
-0.5f, -0.5f, 0.0f, 0.0f, //Bottom-left
0.5f, 0.75f, 1.0f, 1.0f, //Top-right
0.5f, -0.75f, 1.0f, 0.0f //Bottom-right
};
//Indices
unsigned int tri_indices[6] = {
2, 0, 1, //Top-right, top-left, bottom-left
2, 3, 1 //Top-right, bottom-right, bottom-left
};
//VAO
unsigned int tri_vao;
glGenVertexArrays(1, &tri_vao);
glBindVertexArray(tri_vao);
//VBO
unsigned int tri_vbo;
glGenBuffers(1, &tri_vbo);
glBindBuffer(GL_ARRAY_BUFFER, tri_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(tri_vertices), tri_vertices, GL_STATIC_DRAW);
//EBO
unsigned int tri_ebo;
glGenBuffers(1, &tri_ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tri_ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(tri_indices), tri_indices, GL_STATIC_DRAW);
//Config
//Position
glVertexAttribPointer(POSITION_ATTRIBUTE_LOC, POSITION_SIZE, GL_FLOAT, GL_FALSE, STRIDE, (void*) POSITION_OFFSET);
glEnableVertexAttribArray(POSITION_ATTRIBUTE_LOC);
//Texture coordinates
glVertexAttribPointer(TEXTURE_COORD_ATTRIBUTE_LOC, TEXTURE_COORD_SIZE, GL_FLOAT, GL_FALSE, STRIDE, (void*) TEXTURE_COORD_OFFSET);
glEnableVertexAttribArray(TEXTURE_COORD_ATTRIBUTE_LOC);
//*Textures
unsigned int brick_tex = load_bmp_texture("purple_bricks.bmp");
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, brick_tex);
//Attaching to uniform
glUseProgram(shader_program);
glUniform1i(glGetUniformLocation(shader_program, "brick"), 0);
//*Rendering setup
//Shader
glUseProgram(shader_program);
//*Main loop
while (!glfwWindowShouldClose(window))
{
//*Blittering
//Background
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
//Triangles
glBindVertexArray(tri_vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tri_ebo);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void*) 0);
//*Buffer, events
glfwSwapBuffers(window);
glfwPollEvents();
}
//*End program
glDeleteVertexArrays(1, &tri_vao);
glDeleteBuffers(1, &tri_vbo);
glDeleteBuffers(1, &tri_ebo);
glfwTerminate();
return 0;
}
//Centers the OpenGL part of the window and keeps the same width and height
static void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
glViewport((width - WINDOW_WIDTH) / 2, (height - WINDOW_HEIGHT) / 2, WINDOW_WIDTH, WINDOW_HEIGHT);
}
//Loads and sets up a BMP texture
static unsigned int load_bmp_texture(const char* name) {
//Loading into array
RGBImage image;
read_bmp(name, &image);
//Generating texture in GL
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
//Setting mapping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); //X
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); //Y
//Setting filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//Setting texture and mipmap
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width, image.height, 0, GL_RGB, GL_UNSIGNED_BYTE, image.data);
glGenerateMipmap(GL_TEXTURE_2D);
//Freeing image array
free_RGBImage(image);
return texture;
}
image.h
//Code for loading a bmp file as an array
//Definitely not part of the problem
//24 bit bmps
//C libs
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
//Prevent struct packing
#pragma pack(1)
typedef struct RGB {
unsigned char R;
unsigned char G;
unsigned char B;
} RGB;
typedef struct RGBImage {
int width;
int height;
RGB* data;
} RGBImage;
typedef struct BMPFileHeader {
char name[2];
uint32_t size;
uint32_t garbage;
uint32_t image_offset;
} BMPFileHeader;
typedef struct BMPInfoHeader {
uint32_t header_size;
uint32_t width;
uint32_t height;
uint16_t color_planes;
uint16_t bits_per_pixel;
uint32_t compression;
uint32_t image_size;
} BMPInfoHeader;
void free_RGBImage(RGBImage image) {
free(image.data);
}
bool read_bmp(const char* file_name, RGBImage* image) {
FILE* fp = fopen(file_name, "rb");
if (fp == NULL) {
printf("Couldn't open %s\n", file_name);
return false;
}
BMPFileHeader file_header;
fread(file_header.name, sizeof(BMPFileHeader), 1, fp);
if ((file_header.name[0] != 'B') || (file_header.name[1] != 'M')) {
fclose(fp);
return false;
}
BMPInfoHeader info_header;
fread(&info_header, sizeof(BMPInfoHeader), 1, fp);
if ((info_header.header_size != 40) || (info_header.compression != 0) || (info_header.bits_per_pixel != 24)) {
fclose(fp);
return false;
}
fseek(fp, file_header.image_offset, SEEK_SET);
image->width = info_header.width;
image->height = info_header.height;
image->data = (RGB*) malloc(image->height * image->width * sizeof(RGB));
for (int i = 0; i < image->height; i++) {
fread(&image->data[i * image->width], sizeof(RGB), image->width, fp);
}
int R;
for (int i = 0; i < image->height * image->width; i++) {
R = image->data[i].R;
image->data[i].R = image->data[i].B;
image->data[i].B = R;
}
fclose(fp);
return true;
}
shader.h
//Code for compiling and linking a shader
//Definitely not part of the problem
//C libs
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
//GL libs
#include <glad/glad.h>
#include <GLFW/glfw3.h>
//Consts
#define INFO_LOG_SIZE 512
static bool _glad_is_init();
static bool _glfw_is_init();
typedef unsigned int ShaderProgram;
bool shader_program_init(ShaderProgram* shader_program, char* vertex_shader_file_name, char* fragment_shader_file_name)
{
if (!_glfw_is_init()) {
printf("Shader: glfw uninitialized\n");
return 0;
}
else if (!_glad_is_init()) {
printf("Shader: glad uninitialized\n");
return 0;
}
long int file_size;
size_t new_source_length;
FILE* fp;
//*Reading vertex shader file
//Open
fp = fopen(vertex_shader_file_name, "r");
if (fp == NULL) {
printf("Couldn't open vertex shader file\n");
return 0;
}
//Find length for buffer
fseek(fp, 0L, SEEK_END);
file_size = ftell(fp);
if (file_size == -1) {
printf("Couldn't seek end of file\n");
return 0;
}
rewind(fp);
char vertex_shader_source[(file_size + 1) * sizeof(char)];
//Read
new_source_length = fread(vertex_shader_source, sizeof(char), file_size, fp);
if (ferror(fp) != 0) {
printf("Error when reading file\n");
return 0;
}
//Add string termination
vertex_shader_source[new_source_length] = '\0';
//Close
fclose(fp);
//*Reading fragment shader
//Open
fp = fopen(fragment_shader_file_name, "r");
if (fp == NULL) {
printf("Couldn't open fragment shader file\n");
return 0;
}
//Find length for buffer
fseek(fp, 0L, SEEK_END);
file_size = ftell(fp);
if (file_size == -1) {
printf("Couldn't seek end of file\n");
return 0;
}
rewind(fp);
char fragment_shader_source[(file_size + 1) * sizeof(char)];
//Read
new_source_length = fread(fragment_shader_source, sizeof(char), file_size, fp);
if (ferror(fp) != 0) {
printf("Error reading file\n");
return 0;
}
//Add string termination
fragment_shader_source[new_source_length] = '\0';
//Close
fclose(fp);
//*Compiling
//For error checking
int success;
char infolog[INFO_LOG_SIZE];
const char* vertex_shader_code = vertex_shader_source; //vertex_shader_source is of type char foo[n], a VLA.
const char* fragment_shader_code = fragment_shader_source;
//Vertex
unsigned int vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, &vertex_shader_code, NULL);
glCompileShader(vertex_shader);
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vertex_shader, INFO_LOG_SIZE, NULL, infolog);
printf("Vertex shader compile fail: \n%s\n", infolog);
return 0;
}
//Fragment
unsigned int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, &fragment_shader_code, NULL);
glCompileShader(fragment_shader);
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fragment_shader, INFO_LOG_SIZE, NULL, infolog);
printf("Fragment shader compile fail: \n%s\n", infolog);
return 0;
}
//Program
*shader_program = glCreateProgram();
glAttachShader(*shader_program, vertex_shader);
glAttachShader(*shader_program, fragment_shader);
glLinkProgram(*shader_program);
glGetProgramiv(*shader_program, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(*shader_program, INFO_LOG_SIZE, NULL, infolog);
printf("Shader program compile fail: \n%s\n", infolog);
return 0;
}
//Deleting
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
return 1;
}
bool _glfw_is_init() {
if (!glfwInit())
{
return false;
}
return true;
}
bool _glad_is_init() {
if (!gladLoadGLLoader((GLADloadproc) &glfwGetProcAddress))
{
return false;
}
return true;
}
vertex_shader.glsl
#version 330 core
layout (location = 0) in vec2 vertex_position;
layout (location = 1) in vec2 vertex_texture_coordinate;
out vec2 texture_coordinate;
void main()
{
gl_Position = vec4(vertex_position, 1.0, 1.0);
texture_coordinate = vertex_texture_coordinate;
};
fragment_shader.glsl
#version 330 core
in vec4 color;
in vec2 texture_coordinate;
out vec4 FragColor;
uniform sampler2D brick;
void main()
{
FragColor = texture(brick, texture_coordinate);
};
Texture used
Result of program
Edit: for those reading in the future, this link helped a lot with implementing the method described in the answer below:
https://www.cs.cmu.edu/~16385/s17/Slides/10.2_2D_Alignment__DLT.pdf
Given the 2d vertex coordinates P[i] and the 2d texture coordinates T[i] you need to find the homography that maps from T[i] to P[i]. The homography H is represented with a 3x3 matrix (up to a scaling factor) and can be calculated, for example, with the direct linear transform method. Beware that it involves solving a system of eight equations with eight/nine unknowns -- so it's best to resort to an existing library, like LAPACK, for that.
The homography satisfies the relations
H * T[i] ~ P[i]
as an equivalence on the projective plane. I.e.
H * (T[i], 1) = a[i] * (P[i], 1)
for some scaling factors a[i].
The trick to get the correct texture mapping is to replace the 2d vertex coordinates with 3d homogeneous ones:
homogeneous P[i] = H * (T[i], 1)
The vertex shader will then receive a vec3 vertex position, and copy the 3rd coordinate to gl_Position.w:
layout (location = 0) in vec3 vertex_position;
...
void main() {
gl_Position = vec4(vertex_position.xy, 0, vertex_position.z);
...
}
Here are the results I get with this method (corrected/uncorrected):

Why is my triangle white in OpenGL ES 3 on Raspberry Pi

I have a very simple example of a OpenGL ES program that I'm trying to get to run on RaspiOS Desktop (a.k.a. Raspbian) on Raspberry Pi 4.
My goal is very simple - to draw a red triangle in the center of the screen. However, the triangle comes out as white instead of red.
I've searched and tried everything and wasn't able to find any help. I'm very frustrated at this point because this was just supposed to be the first tutorial to introduce the world of OpenGL ES and I'm already stuck and can't continue with more complicated examples.
Anyway, here's the full example
#include <GL/glew.h>
#include <GL/freeglut.h>
#include <stdio.h>
#include <stdbool.h>
static struct glData {
GLuint program;
GLuint vbo;
} glData;
const char vert_shader_source[] = "#version 300 es \n"
"precision mediump float; \n"
"layout (location = 0) in vec3 Position; \n"
"void main() \n"
"{ \n"
" gl_Position = vec4(Position, 1.0); \n"
"} \n";
const char frag_shader_source[] = "#version 300 es \n"
"precision mediump float; \n"
"out vec4 fragColor; \n"
"void main() \n"
"{ \n"
" fragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f); \n"
"} \n";
#define POSITION 0
bool initWindow(int* argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_MULTISAMPLE);
glutCreateWindow("Triangle");
GLenum glew_status = glewInit();
if (glew_status != GLEW_OK) {
fprintf(stderr, "Error: %s\n", glewGetErrorString(glew_status));
return false;
}
return true;
}
static GLuint buildShader(const char* shader_source, GLenum type)
{
GLuint shader;
GLint status;
shader = glCreateShader(type);
if (shader == 0) {
return 0;
}
glShaderSource(shader, 1, &shader_source, NULL);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
int length;
char* log;
fprintf(stderr, "failed to compile shader\n");
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
if (length > 1) {
log = calloc(length, sizeof(char));
glGetShaderInfoLog(shader, length, &length, log);
fprintf(stderr, "%s\n", log);
free(log);
}
return false;
}
return true;
}
static GLuint createAndLinkProgram(GLuint v_shader, GLuint f_shader)
{
GLuint program;
GLint linked;
program = glCreateProgram();
if (program == 0) {
fprintf(stderr, "failed to create program\n");
return 0;
}
glAttachShader(program, v_shader);
glAttachShader(program, f_shader);
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &linked);
if (linked != GL_TRUE) {
int length;
char* log;
fprintf(stderr, "failed to link program\n");
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
if (length > 1) {
log = calloc(length, sizeof(char));
glGetProgramInfoLog(program, length, &length, log);
fprintf(stderr, "%s\n", log);
free(log);
}
glDeleteProgram(program);
return 0;
}
return program;
}
static bool initProgram()
{
GLuint v_shader, f_shader;
v_shader = buildShader(vert_shader_source, GL_VERTEX_SHADER);
if (v_shader == 0) {
fprintf(stderr, "failed to build vertex shader\n");
return false;
}
f_shader = buildShader(frag_shader_source, GL_FRAGMENT_SHADER);
if (f_shader == 0) {
fprintf(stderr, "failed to build fragment shader\n");
glDeleteShader(v_shader);
return false;
}
glReleaseShaderCompiler(); // should release resources allocated for the compiler
glData.program = createAndLinkProgram(v_shader, f_shader);
if (glData.program == 0) {
fprintf(stderr, "failed to create and link program\n");
glDeleteShader(v_shader);
glDeleteShader(f_shader);
return false;
}
glUseProgram(glData.program);
// this won't actually delete the shaders until the program is closed but it's a good practice
glDeleteShader(v_shader);
glDeleteShader(f_shader);
return true;
}
bool setupOpenGL()
{
if (!initProgram()) {
fprintf(stderr, "failed to initialize program\n");
return false;
}
GLfloat vVertices[] = {
-0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
};
glClearColor(0, 0, 0, 1);
glGenBuffers(1, &glData.vbo);
glBindBuffer(GL_ARRAY_BUFFER, glData.vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices), vVertices, GL_STATIC_DRAW);
return true;
}
void reshape(int width, int height)
{
glViewport(0, 0, width, height);
}
void drawTriangle()
{
glClear(GL_COLOR_BUFFER_BIT);
glEnableVertexAttribArray(POSITION);
glBindBuffer(GL_ARRAY_BUFFER, glData.vbo);
glVertexAttribPointer(POSITION, 3, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableVertexAttribArray(POSITION);
glutSwapBuffers();
}
int main(int argc, char** argv)
{
printf("initialize window\n");
if (!initWindow(&argc, argv)) {
fprintf(stderr, "failed to initialize window\n");
return EXIT_FAILURE;
}
printf("setup opengl\n");
if (!setupOpenGL()) {
fprintf(stderr, "failed to setup opengl\n");
return EXIT_FAILURE;
}
glutDisplayFunc(drawTriangle);
glutReshapeFunc(reshape);
glutMainLoop();
glDeleteProgram(glData.program);
return EXIT_SUCCESS;
}
Before you run it, you need to:
Run sudo raspi-config
Go to Advanced Options > GL Driver
Enable GL (Fake KMS)
Reboot
Then you can compile and run it like this:
gcc triangle.c -Wall -lm -lglut -lGLEW -lGL -o triangle
./triangle
At first I thought maybe it's some bug in the driver or something. But then I found this example and tried to run it and it draws some graphs with multiple colors and it's fine.
I'd appreaciate any help. I've been trying to debug this for days now.
Nvm, turns out I'm an idiot.
This entire time it was a simple typo in buildShader() function. Here's the fixed version of that function:
static GLuint buildShader(const char* shader_source, GLenum type)
{
GLuint shader;
GLint status;
shader = glCreateShader(type);
if (shader == 0) {
return 0;
}
glShaderSource(shader, 1, &shader_source, NULL);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
int length;
char* log;
fprintf(stderr, "failed to compile shader\n");
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
if (length > 1) {
log = calloc(length, sizeof(char));
glGetShaderInfoLog(shader, length, &length, log);
fprintf(stderr, "%s\n", log);
free(log);
}
return 0;
}
return shader;
}
The problem was that I was accidentally returning true/false instead of the shader. I have absolutely no idea how the program still managed to run without an error and display a white triangle but that's how it is.

Why is no character being rendered

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.

Freetype Opengl Glyph is not rendering

I am trying to render a single char as a demo in opengl by utilizing the bitmap buffer offered by freetype glyphs. I know my triangle fan is correct because right now i see a black textured triangle_fan against a green background. Ideally i should be seeing a character in my triangle_fan primitive instead of just a full solid black square.
void fontDataNums_init(const char * fname) {
float h = font.h = 16;
/*Dynamically allocated variables, clean before exit */
FT_Face face;
FT_Library library;
GLubyte * expanded_data;
/* Create And Initilize A FreeType Font Library. */
if(FT_Init_FreeType( &library )) {
printf("fontDataNums_init::FT_Init_FreeType failed\n");
exit(1);
}
/* Initialize face, load font from ttf file */
if(FT_New_Face( library, fname, 0, &face )){
printf("fontDataNums_init::FT_New_Face failed\n");
exit(1);
}
if(FT_Set_Char_Size( face, h * 64, h * 64, 96, 96)){
printf("fontDataNums_init::FT_Set_Char_Size failed.\n");
exit(1);
}
font.textures = (GLuint *) malloc(sizeof(GLuint) * 10);
glGenTextures(10, font.textures);
/* CREATE CHARACTER BITMAPS I WANT LOADED */
unsigned char g;
int i, j;
for( g='A'; g < 'J'; g++){
if(FT_Load_Char(face, g, FT_LOAD_RENDER)){
printf("fontDataNums::FT_Load_Char unable to load glyph for character\n");
exit(1);
}
FT_Glyph glyph;
if(FT_Get_Glyph(face->glyph, &glyph) ) { printf("GetGlyph failed.\n");}
if(FT_Glyph_To_Bitmap(&glyph, ft_render_mode_normal, 0, 1)){
printf("fontDataNums::FT_Glyph_To_Bitmap failed to create bitmap.\n");
exit(1);
}
FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph;
int width = next_p2( bitmap_glyph->bitmap.width );
int height = next_p2( bitmap_glyph->bitmap.rows );
printf("WIDTH: %i and HEIGHT: %i \n", width, height);
/* PADDING FOR BITMAP */
expanded_data = (GLubyte *) malloc(sizeof(GLubyte) * 2 * width * height);
for(j=0; j <height;j++) {
for(i=0; i < width; i++){
expanded_data[2*(i+j*width)] = expanded_data[2*(i+j*width)+1] =
(i>=bitmap_glyph->bitmap.width || j>=bitmap_glyph->bitmap.rows) ?
0 :
bitmap_glyph->bitmap.buffer[i + bitmap_glyph->bitmap.width*j];
}
}
/* LOAD TEXTURE INTO OPENGL */
glActiveTexture(GL_TEXTURE0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture( GL_TEXTURE_2D, font.textures[g]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data );
free(expanded_data);
FT_Done_Glyph(glyph);
}
/* Clean Up */
FT_Done_Face(face);
FT_Done_FreeType(library);
}
int next_p2 (int a )
{
int rval=1;
/* rval<<=1 Is A Prettier Way Of Writing rval*=2; */
while(rval<a) rval<<=1;
return rval;
}
void drawGlyph(){
renderGlyph(font.textures[1]);
}
void renderGlyph(GLuint textureName) {
GLuint tbo = 0;
GLuint vbo = 0;
glClear(GL_COLOR_BUFFER_BIT);
/* SETUP VERTICES */
GLfloat verts[8]={ 0.0f, 16.0f,
0.0f, 0.0f,
17.0f , 0.0f,
17.0f , 16.0f};
glEnableVertexAttribArray(GLT_ATTRIBUTE_VERTEX);
if(vbo == 0){glGenBuffers(1, &vbo);}
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 8, verts, GL_DYNAMIC_DRAW);
glVertexAttribPointer(GLT_ATTRIBUTE_VERTEX, 2, GL_FLOAT, GL_FALSE, 0, 0);
/* Setup Texture Buffer */
float x = 17.0f / 32.0f;
float y = 16.0f / 16.0f;
GLfloat vTex[8] = { 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f };
glEnableVertexAttribArray(GLT_ATTRIBUTE_TEXTURE0);
if(tbo == 0) { glGenBuffers(1, &tbo);}
glBindBuffer(GL_ARRAY_BUFFER, tbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 8, vTex, GL_DYNAMIC_DRAW);
glVertexAttribPointer(GLT_ATTRIBUTE_TEXTURE0, 2, GL_FLOAT, GL_FALSE, 0, 0);
/*Create Shaders*/
static const char *szIdentityShaderVP =
"#version 330\n"
"in vec4 vVertex;\n"
"in vec2 TexCoords;\n"
"out vec2 varyingTexCoords;\n"
"uniform mat4 mvp;\n"
"void main(void) \n"
"{"
"varyingTexCoords = TexCoords;\n"
"gl_Position = mvp * vVertex;\n"
"}\n";
static const char *szIdentityShaderFP =
"#version 330\n"
"uniform sampler2D colormap;\n"
"uniform vec4 showFan;"
"in vec2 varyingTexCoords;\n"
"void main(void) \n"
"{"
//"gl_FragColor = showFan;\n"
"gl_FragColor = texture(colormap, varyingTexCoords);\n"
"}\n";
GLuint shaderName = 0;
shaderName = gltLoadShaderPairSrcWithAttributes(szIdentityShaderVP, szIdentityShaderFP, 2, GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_TEXTURE0, "TexCoords");
if(shaderName == 0) { printf("***shader compile failed****\n");}
glUseProgram(shaderName);
vmathM4MakeOrthographic( &pmatrix, -50.0f, 50.0f, -50.0f, 50.0f, -50.0f, 50.0f);
GLint mvp = 0;
mvp = glGetUniformLocation(shaderName, "mvp");
glUniformMatrix4fv(mvp, 1, GL_FALSE, (GLfloat *) &pmatrix);
GLint texUniform = 0;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureName);
texUniform = glGetUniformLocation(shaderName, "colormap");
glUniform1i(texUniform, 0);
GLint showFan= 0;
showFan = glGetUniformLocation(shaderName, "showFan");
glUniform4f(showFan, 1.0f, 0.0f, 0.0f, 1.0f);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
SDL_GL_SwapWindow(gcore.mainwindow);
glDisableVertexAttribArray(GLT_ATTRIBUTE_VERTEX);
glDisableVertexAttribArray(GLT_ATTRIBUTE_TEXTURE0);
glDeleteProgram(shaderName);
glDeleteBuffers(1, &vbo);
glDeleteBuffers(1, &tbo);
glCheckError();
}
void glCheckError(){
GLenum checkError = glGetError();
if(checkError != GL_NO_ERROR)
printf("Error: %i\n", checkError);
}
I use freetype in an IPhone device, it works creating the texture with...
glTexImage2D( GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, data);

Getting an error using a uniform 1D texture parameter with CG and openGL

I am trying to write a basic volume renderer which uses opengl, and cg for writing shaders. I'm putting my transfer function in a one dimensional texture, and using that in a dependent texture lookup in the fragment shader. My problem is that I'm getting an openGL error when I try to enable the parameter corresponding to that 1D texture.
My code is pretty messy; right now it's basically a mashup of code taken from "Real-Time Volume Rendering" and "The CG User's Manual".
c file:
#include <stdio.h>
#include <stdlib.h>
#include <GL/glut.h>
#include <Cg/cg.h>
#include <Cg/cgGL.h>
static CGcontext myCgContext;
static CGprofile myCgVertexProfile,
myCgFragmentProfile;
static CGprogram myCgVertexProgram,
myCgFragmentProgram;
static const char *myProgramName = "first_volumetric_renderer",
*myVertexProgramFileName = "fvr_vertex.cg",
*myVertexProgramName = "fvr_vertex",
*myFragmentProgramFileName = "fvr_fragment.cg",
*myFragmentProgramName = "fvr_fragment";
static CGparameter first_texture, second_texture, transfer_function;
#define XDIM 256
#define YDIM 256
#define ZDIM 256
#define TRANSFER_RESOLUTION 256
static GLubyte raw_data[XDIM][YDIM][ZDIM];
static GLubyte transfer[TRANSFER_RESOLUTION][4];
static GLuint transfer_name;
static GLuint x_textures[XDIM], y_textures[YDIM], z_textures[ZDIM];
static void checkForCgError(const char *situation);
/* print any errors if we get them */
void check_gl_error(const char * where){
GLenum error = glGetError();
if(error != GL_NO_ERROR){
printf("openGL Error : %s : %s\n", where, gluErrorString(error));
exit(1);
}
}
long int file_length(FILE *f){
long int pos = ftell(f);
fseek(f, 0, SEEK_END);
long int result = ftell(f);
fseek(f, pos, SEEK_SET);
return result;
}
void get_volume_data(const char *filename){
FILE *in = fopen(filename, "r");
if(in == NULL) {
printf("opening '%s' to get volume data failed, exiting...\n", filename);
exit(1);
}
long int length = file_length(in);
if(length != XDIM*YDIM*ZDIM){
printf("the file does not contain a volume of the correct dimensions\n");
exit(1);
}
size_t res = fread((char *)raw_data, 1, length, in);
if(res < length) printf("error reading in file\n");
fclose(in);
}
void create_textures(){
glEnable(GL_TEXTURE_2D);
// reserve texture identifiers
glGenTextures(XDIM, x_textures);
glGenTextures(YDIM, y_textures);
glGenTextures(ZDIM, z_textures);
// set texture properties
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// generate slices in X
{
int x,y,z;
GLubyte x_slice[ZDIM][YDIM];
for(x=0;x < XDIM; x++){
for(y=0;y < YDIM; y++){
for(z=0;z < ZDIM; z++){
x_slice[z][y] = raw_data[x][y][z];
}
}
GLuint texname = x_textures[x];
glBindTexture(GL_TEXTURE_2D, texname);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, ZDIM, YDIM, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, x_slice);
}
}
// generate slices in Y
{
int x,y,z;
GLubyte y_slice[XDIM][ZDIM];
for(y=0;y < YDIM; y++){
for(x=0;x < XDIM; x++){
for(z=0;z < ZDIM; z++){
y_slice[x][z] = raw_data[x][y][z];
}
}
GLuint texname = y_textures[y];
glBindTexture(GL_TEXTURE_2D, texname);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, XDIM, ZDIM, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, y_slice);
}
}
// generate slices in Z
{
int x,y,z;
GLubyte z_slice[XDIM][YDIM];
for(z=0;z < ZDIM; z++){
for(y=0;y < YDIM; y++){
for(x=0;x < XDIM; x++){
z_slice[x][y] = raw_data[x][y][z];
}
}
GLuint texname = z_textures[z];
glBindTexture(GL_TEXTURE_2D, texname);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, XDIM, YDIM, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, z_slice);
}
}
}
void DrawSliceStack_NegativeZ(int numSlices){
double dZPos = -1.0;
double dZStep = 2.0/((double)numSlices);
int slice;
for(slice = 0;slice < numSlices;slice++){
double dZPosTex = (ZDIM * (dZPos + 1.0)/2.0);
int nTexIdx = (int)dZPosTex;
double dAlpha = dZPosTex - (double)nTexIdx;
check_gl_error("in slice-drawing function, before cg-stuff");
cgGLSetTextureParameter(first_texture, z_textures[nTexIdx]);
checkForCgError("setting first texture");
check_gl_error("1");
cgGLEnableTextureParameter(first_texture);
checkForCgError("enabling first texture");
check_gl_error("2");
cgGLSetTextureParameter(second_texture, z_textures[nTexIdx + 1]);
checkForCgError("setting second texture");
check_gl_error("3");
cgGLEnableTextureParameter(second_texture);
checkForCgError("enabling second texture");
check_gl_error("4");
cgGLSetTextureParameter(transfer_function, transfer_name);
checkForCgError("setting transfer function");
check_gl_error("5");
cgGLEnableTextureParameter(transfer_function);
checkForCgError("enabling transfer function");
check_gl_error("before updating parameters");
cgUpdateProgramParameters(myCgFragmentProgram);
checkForCgError("updating parameters");
check_gl_error("before drawing a slice");
glBegin(GL_QUADS);
glTexCoord3d(0.0, 0.0, dAlpha);
glVertex3d(-1.0, -1.0, dZPos);
glTexCoord3d(0.0, 1.0, dAlpha);
glVertex3d(-1.0, 1.0, dZPos);
glTexCoord3d(1.0, 1.0, dAlpha);
glVertex3d(1.0, 1.0, dZPos);
glTexCoord3d(1.0, 0.0, dAlpha);
glVertex3d(1.0, -1.0, dZPos);
glEnd();
check_gl_error("after drawing a slice");
dZPos += dZStep;
cgGLDisableTextureParameter(first_texture);
checkForCgError("disabling first texture");
cgGLDisableTextureParameter(second_texture);
checkForCgError("disabling second texture");
cgGLDisableTextureParameter(transfer_function);
checkForCgError("disabling transfer function");
}
}
void create_transfer_texture(){
glEnable(GL_TEXTURE_1D);
// create the raw data
int i;
for(i = 0; i < TRANSFER_RESOLUTION; i++){
if(i < 50) {
transfer[i][0] = (GLubyte)0;
transfer[i][1] = (GLubyte)0;
transfer[i][2] = (GLubyte)0;
transfer[i][3] = (GLubyte)0;
}
else {
transfer[i][0] = (GLubyte)255;
transfer[i][1] = (GLubyte)255;
transfer[i][2] = (GLubyte)255;
transfer[i][3] = (GLubyte)i;
}
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(1, &transfer_name);
glBindTexture(GL_TEXTURE_3D, transfer_name);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, TRANSFER_RESOLUTION, 0, GL_RGBA, GL_UNSIGNED_BYTE, transfer);
check_gl_error("creating transfer texture");
}
static void checkForCgError(const char *situation)
{
CGerror error;
const char *string = cgGetLastErrorString(&error);
if (error != CG_NO_ERROR) {
printf("%s: %s: %s\n",
myProgramName, situation, string);
if (error == CG_COMPILER_ERROR) {
printf("%s\n", cgGetLastListing(myCgContext));
}
exit(1);
}
}
void init_CG(){
// copy-pasted straight from one of the CG examples
myCgContext = cgCreateContext();
checkForCgError("creating context");
cgGLSetDebugMode(CG_FALSE);
cgSetParameterSettingMode(myCgContext, CG_DEFERRED_PARAMETER_SETTING);
myCgFragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
cgGLSetOptimalOptions(myCgFragmentProfile);
checkForCgError("selecting fragment profile");
myCgFragmentProgram =
cgCreateProgramFromFile(
myCgContext, /* Cg runtime context */
CG_SOURCE, /* Program in human-readable form */
myFragmentProgramFileName, /* Name of file containing program */
myCgFragmentProfile, /* Profile: OpenGL ARB vertex program */
myFragmentProgramName, /* Entry function name */
NULL); /* No extra compiler options */
checkForCgError("creating fragment program from file");
cgGLLoadProgram(myCgFragmentProgram);
checkForCgError("loading fragment program");
first_texture = cgGetNamedParameter(myCgFragmentProgram, "text0");
checkForCgError("could not get 'texture0'");
second_texture = cgGetNamedParameter(myCgFragmentProgram, "text1");
checkForCgError("could not get 'texture1'");
transfer_function = cgGetNamedParameter(myCgFragmentProgram, "transfer_function");
checkForCgError("could not get 'transfer_function'");
check_gl_error("initializing CG");
}
void reshape(int w, int h)
{
if (h == 0) h = 1;
glViewport(0, 0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-2, 2, -2, 2, -2, 2); // use orthographic projection
glMatrixMode(GL_MODELVIEW);
}
void display(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* cgGLBindProgram(myCgVertexProgram);
checkForCgError("binding vertex program");
cgGLEnableProfile(myCgVertexProfile);
checkForCgError("enabling vertex profile");*/
cgGLBindProgram(myCgFragmentProgram);
checkForCgError("binding fragment program");
cgGLEnableProfile(myCgFragmentProfile);
checkForCgError("enabling fragment profile");
check_gl_error("before entering slice-drawing function");
DrawSliceStack_NegativeZ(ZDIM * 2);
/*cgGLDisableProfile(myCgVertexProfile);
checkForCgError("disabling vertex profile");*/
cgGLDisableProfile(myCgFragmentProfile);
checkForCgError("disabling fragment profile");
glutSwapBuffers();
check_gl_error("Finishing 'display()'");
}
void keyboard(unsigned char c, int x, int y){
}
void init_glut(int argc, char** argv){
glutInitWindowSize(400, 400);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInit(&argc, argv);
glutCreateWindow(myProgramName);
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
glutReshapeFunc(reshape);
glClearColor(1.0, 0.0, 0.0, 0.0); /* Black background */
}
int main(int argc, char **argv){
init_glut(argc, argv);
init_CG();
get_volume_data("aneurism.raw");
create_textures();
create_transfer_texture();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glutMainLoop();
return 0;
}
fragment shader (fvr_fragment.cg):
float4 fvr_fragment(half3 texUV : TEXCOORD0,
uniform sampler2D text0,
uniform sampler2D text1,
uniform sampler1D transfer_function) : COLOR
{
half tex0 = tex2D(text0, texUV.xy);
half tex1 = tex2D(text1, texUV.xy);
half interp = lerp(tex0, tex1, texUV.z);
float4 result = tex1D(transfer_function, interp);
return result;
}
the volume data
When run, the program outputs:
openGL Error : before updating parameters : invalid operation
the line which causes the error, which I found thanks to my ad-hoc print debugging, is:
cgGLEnableTextureParameter(transfer_function);
Any ideas?
Your error is here
glBindTexture(GL_TEXTURE_3D, transfer_name);
It should read
glBindTexture(GL_TEXTURE_1D, transfer_name);

Resources