Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Easy framework for OpenGL Shaders in C/C++

Tags:

c++

c

opengl

I just wanted to try out some shaders on a flat image. Turns out that writing a C program, which just takes a picture as a texture and applies, let's say a gaussian blur, as a fragment shader on it is not that easy: You have to initialize OpenGL which are like 100 lines of code, then understanding the GLBuffers, etc.. Also to communicate with the windowing system one has to use GLUT which is another framework..

Turns out that Nvidia's Fx composer is nice to play with shaders.. But I still would like to have a simple C or C++ program which just applies a given fragment shader to an image and displays the result. Does anybody have an example or is there a framework?

like image 638
Nils Avatar asked May 08 '10 17:05

Nils


People also ask

Is GLSL like C?

Shaders use GLSL (OpenGL Shading Language), a special OpenGL Shading Language with syntax similar to C. GLSL is executed directly by the graphics pipeline. There are several kinds of shaders, but two are commonly used to create graphics on the web: Vertex Shaders and Fragment (Pixel) Shaders.

What language are shaders written?

Shaders are written in the C-like language GLSL. GLSL is tailored for use with graphics and contains useful features specifically targeted at vector and matrix manipulation. Shaders always begin with a version declaration, followed by a list of input and output variables, uniforms and its main function.

Is OpenGL the same as GLSL?

The short version is: OpenGL is an API for rendering graphics, while GLSL (which stands for GL shading language) is a language that gives programmers the ability to modify pipeline shaders. To put it another way, GLSL is a (small) part of the overall OpenGL framework.


2 Answers

First of all, I'd avoid using glut -- it's buggy, hasn't been updated in roughly a decade, and its design doesn't really fit very well with what most people want today (e.g., though you can use it for animations, it's really intended primarily to produce a static display). I pointed out a number of alternatives to glut in a previous answer.

That (mostly) leaves the code to compile, link, and use shaders. I've written a small class I find handy for this purpose:

class shader_prog {     GLuint vertex_shader, fragment_shader, prog;      template <int N>     GLuint compile(GLuint type, char const *(&source)[N]) {         GLuint shader = glCreateShader(type);         glShaderSource(shader, N, source, NULL);         glCompileShader(shader);         GLint compiled;         glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);         if (!compiled) {             GLint length;             glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);             std::string log(length, ' ');             glGetShaderInfoLog(shader, length, &length, &log[0]);             throw std::logic_error(log);             return false;         }         return shader;     } public:     template <int N, int M>     shader_prog(GLchar const *(&v_source)[N], GLchar const *(&f_source)[M]) {         vertex_shader = compile(GL_VERTEX_SHADER, v_source);         fragment_shader = compile(GL_FRAGMENT_SHADER, f_source);         prog = glCreateProgram();         glAttachShader(prog, vertex_shader);         glAttachShader(prog, fragment_shader);         glLinkProgram(prog);     }      operator GLuint() { return prog; }     void operator()() { glUseProgram(prog); }      ~shader_prog() {         glDeleteProgram(prog);         glDeleteShader(vertex_shader);         glDeleteShader(fragment_shader);     } }; 

For a simple demo, a couple of "pass-through" shaders (just imitate the fixed-functionality pipeline):

const GLchar *vertex_shader[] = {     "void main(void) {\n",     "    gl_Position = ftransform();\n",     "    gl_FrontColor = gl_Color;\n",     "}" };  const GLchar *color_shader[] = {     "void main() {\n",     "    gl_FragColor = gl_Color;\n",     "}" }; 

Which you'd use something like:

void draw() {      // compile and link the specified shaders:     static shader_prog prog(vertex_shader, color_shader);      // Use the compiled shaders:         prog();       // Draw something:     glBegin(GL_TRIANGLES);         glColor3f(0.0f, 0.0f, 1.0f);         glVertex3f(-1.0f, 0.0f, -1.0f);         glColor3f(0.0f, 1.0f, 0.0f);         glVertex3f(1.0f, 0.0f, -1.0f);         glColor3f(1.0f, 0.0f, 0.0f);         glVertex3d(0.0, -1.0, -1.0);     glEnd(); } 

If you're going to use, for example, a number of different fragment shaders in the course of drawing your scene, you simply define a static object for each, then execute prog1();, prog2();, etc., just prior drawing the objects you want shaded with each shader. E.g.,

void draw() {      static shader_prog wall_shader("wall_vertex", "wall_frag");     static shader_prog skin_shader("skin_vertex", "skin_frag");      wall_shader();     draw_walls();      skin_shader();     draw_skin(); } 

Edit: As @rotoglup quite correctly points out, this use of static variables delays destruction until after the OpenGL context has been destroyed, so when the destructors attempt to use glDeleteProgram/glDeleteShader, results are unpredictable (at best).

While this may be excusable in a demo program, it's decidedly undesirable in real use. At the same time, you generally do not want to re-compile your shader(s) every time you enter the function(s) that use them.

To avoid both problems, you generally want to create your shader objects as members of a class instance whose lifetime is, in turn, tied to the lifetime of whatever it's going to shade:

class some_character_type {      shader_prog skin_shader; public:     // ... }; 

This will compile/link the shader program once when you create a character of that type, and destroy it when you destroy that character.

Of course, in a few cases, this isn't exactly desirable either. Just for example, consider a 3D version of the ancient "kill lots of targets" games like Galaga or Centipede. For games like this, you're creating and destroying lots of essentially identical targets relatively quickly. Given a large number of essentially identical targets, you probably want to use something like a shared_ptr<shader_prog> to create a single instance of the shader that's shared between all the instances of a particular target type. Given that you re-use the same target types many times, you may want to go a bit further even than that, so you maintain the same shaders through the entire game, not just when a particular type of target is being shown.

In any case, we're getting a bit off-track here. The point is that compiling and linking shaders is a fairly expensive process, so you normally want to manage their lifetime to avoid creating and destroying them a lot more often than truly necessary (though that's not to say that it's critical to create them all at the beginning of the game and only destroy them at the end, either).

like image 148
Jerry Coffin Avatar answered Sep 22 '22 08:09

Jerry Coffin


I was in a similar position about a year and a half ago. I quickly found a simple tutorial and source code for using GLSL.. but I did have to get GLUT and GLEW working, and I think I ended up compiling at least one of those myself. Since I was using Windows (and Windows is somewhat of a non-standard special case which is rarely given full treatment by open projects), it also involved a ridiculous process where I was expected to manually copy and paste DLL's and header files to certain common locations. It's always a pain and I've lost a good chunk of my life doing that sort of thing, but I trudged through the process as directed and it worked out in the end as is generally the case.

Anyway, the most convenient shader example using GLSL that I can find right now is this one - http://www.lighthouse3d.com/opengl/glsl/index.php?minimal

It doesn't specifically modify a texture as you wish.. but in my experience, once you get code like this compiling and running the experience will be more enjoyable and you'll quickly make progress and be able to splice in pieces from other tutorials if necessary. I can say that once I got an example running, I used that same framework to quickly solve many problems at home and work.

I'm sorry to say that it does make use of GLUT and GLEW. If you get a better answer to this question, I too will become an instant fan of whichever site offers the code. Good luck.

like image 29
guesser Avatar answered Sep 25 '22 08:09

guesser