Related
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;
}
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):
I'm having trouble compiling shaders. I'm pretty sure the issue is with my file loading code.
These are the relevant functions:
char* loadShader(char* filename){
char* buffer = 0;
long length;
FILE* f = fopen (filename, "rb");
if(!f){
// error reading file
printf("failed to open file\n");
}
if (f){
fseek (f, 0, SEEK_END);
length = ftell (f);
fseek (f, 0, SEEK_SET);
buffer = malloc (length+1);
if (buffer){
fread (buffer, 1, length, f);
}
fclose (f);
}
return buffer;
}
Renderer* initRenderer(){
Renderer* renderer = malloc(sizeof(Renderer));
//renderer->vertices[0] = -0.5f;
float verts[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
for(int i = 0; i < 9; i++){
renderer->vertices[i] = verts[i];
}
glGenBuffers(1, &renderer->VBO);
glBindBuffer(GL_ARRAY_BUFFER, renderer->VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(renderer->vertices), renderer->vertices, GL_STATIC_DRAW);
GLchar* vst = (GLchar*)loadShader("game_data/assets/shaders/simple_vert.glsl");
GLchar* fst = (GLchar*)loadShader("game_data/assets/shaders/simple_frag.glsl");
const GLchar* vertexShaderSource = vst;
const GLchar* fragmentShaderSource = fst;
if(vst){free(vst);}
if(fst){free(fst);}
int success;
char infoLog[512];
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if(!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n");
printf("%s\n", infoLog);
//std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if(!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n");
printf("%s\n", infoLog);
//std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
return renderer;
}
The vertex shader:
#version 330 core
layout (location = 0) in vec3 aPos;
void main()
{
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
The fragment shader:
#version 330 core
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}
And finally the error message:
ERROR::SHADER::VERTEX::COMPILATION_FAILED
0:1(1): error: syntax error, unexpected $end
ERROR::SHADER::FRAGMENT::COMPILATION_FAILED
0:1(1): error: syntax error, unexpected $end
I'm sure I'm missing something simple with the strings. I looked at this answer with a similar problem but I wasn't able to glean anything from it.
Not an expert in file opening but your opening it in rb (read binary) mode, maybe try just opening it in r (read) mode.
Ok. I'm getting into opengl 2.1(without the fixed function stuff) and I'm having troubles with shaders. I declare my uniform variable on my shader, and in my program I get the uniform location and assign it a value with gluniform but it doesn't seem to work.
These are my shaders
fragment shader:
#version 120
varying float color;
void
main ()
{
gl_FragColor = vec4(color, 0, 0, 1);
}
and my vertex shader:
#version 120
attribute vec2 position;
varying float color;
uniform float pr_color;
void
main ()
{
color = pr_color;
gl_Position = vec4(position, 0.0, 1.0);
}
This is how I'm passing the data to the shader:
void
display ()
{
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(g_program);
pos_loc = glGetAttribLocation(g_program, "position");
col_loc = glGetUniformLocation(g_program, "pr_color");
glUniform1f(col_loc, 1.0);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glEnableVertexAttribArray(pos_loc);
glVertexAttribPointer(pos_loc, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glDrawArrays(GL_TRIANGLES, 0, 3);
glutPostRedisplay();
glutSwapBuffers();
}
and i don't think this is needed, but in any case, my initgl function
void
init ()
{
// Set clear color to black
glClearColor(0.0,0.0,0.0,0.0);
vshader = createShader(GL_VERTEX_SHADER, "vertex.glsl");
fshader = createShader(GL_FRAGMENT_SHADER, "fragment.glsl");
g_program = createProgram(vshader, fshader);
// Create vbo and send it the vertex data
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
}
the thing is, my shaders are working, if I hardcode the color into the fragment sahder, then the triangle is drawn, but if I pass the color to the vertex shader, it doesn't work, the triangle is drawn to black, which is strange, because my position attribute works, but my color uniform is set to 0. As you can see, I'm setting useprogram() before passing the uniform value.
EDIT: I changed the gluniform1i(col_loc, 1) to glUniformif(col_loc, 1.0). Still doesn't work
EDIT: I'll add my shader loading function to make sure the problem is not in the shader
GLuint
createShader (GLenum type, char* filename)
{
GLuint shader;
// Load file
FILE* file;
file = fopen(filename, "r");
if (file == NULL)
{
printf("Error reading file \n");
}
// Get Length
fseek(file, 0, SEEK_END);
long length = ftell(file);
fseek(file, 0, SEEK_SET);
// Get source
char* source;
source = malloc( (size_t) length + 1);
if (source == NULL)
{
printf("Error alocating space for shader\n");
}
// Read file
fread(source, 1, length, file);
// Close file
fclose(file);
source[length] = '\n';
// Create shader, attach it's source and compile it
shader = glCreateShader(type);
glShaderSource(shader, 1, (const GLchar*)&source, &length);
free(source); // Free shader source, once it's attached
glCompileShader(shader);
// Check for errors
GLint shader_status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_status);
if (!shader_status) {
fprintf(stderr, "Failed to compile %s:\n", filename);
GLint log_length;
char *log;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
log = malloc(log_length);
glGetShaderInfoLog(shader, log_length, NULL, log);
fprintf(stderr, "%s", log);
free(log);
glDeleteShader(shader);
return 0;
}
return shader;
}
When there's an error in the shader, the program actually prints the error log, so I don't think the error is in here, but anyway, here it is.
EDIT: program linker code
GLuint
createProgram (GLuint vertexs, GLuint fragments)
{
GLint program_ok;
// Create program and attach to shaders
GLuint program = glCreateProgram();
glAttachShader(program, vertexs);
glAttachShader(program, fragments);
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &program_ok);
if (!program_ok) {
fprintf(stderr, "Failed to link shader program:\n");
GLint log_length;
char *log;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
log = malloc(log_length);
glGetProgramInfoLog(program, log_length, NULL, log);
fprintf(stderr, "%s", log);
free(log);
glDeleteProgram(program);
return 0;
}
return program;
}
Please tell me if there's anything wrong with my shaders/program, I was just starting to get the basics of programmable pipeline and now I can't even render a triangle
You are using
glUniform1i(col_loc, 1);
To set the value of
uniform float pr_color;
1i stands for 1 int. Use 1f for 1 float.
glUniform1f(col_loc, 1.0f);
OK, I got it working with glUnifrom4fv(). I am now passing a vec4 to the shader's color variable and it works!
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);