C/C++ 中 OpenGL 着色器的简单框架
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2795044/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Easy framework for OpenGL Shaders in C/C++
提问by Nils
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..
我只是想在平面图像上尝试一些着色器。事实证明,编写一个 C 程序,它只是将图片作为纹理并应用,比如说高斯模糊,作为它上面的片段着色器并不是那么容易:你必须初始化 OpenGL,就像 100 行代码,然后了解 GLBuffers 等。还要与窗口系统通信,必须使用 GLUT,这是另一种框架。
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?
事实证明,Nvidia 的 Fx composer 很适合使用着色器。但我仍然想要一个简单的 C 或 C++ 程序,它只是将给定的片段着色器应用于图像并显示结果。有人有例子或有框架吗?
回答by Jerry Coffin
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 canuse 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.
首先,我会避免使用 glut——它有问题,大约有十年没有更新了,而且它的设计不太适合当今大多数人的需求(例如,尽管你可以将它用于动画,它实际上主要是为了产生静态显示)。我在之前的回答中指出了许多替代 glut 的方法。
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.,
如果你要使用,例如,一些在绘制场景的过程中不同的片段着色的,您只需定义每个静态对象,然后执行prog1();
,prog2();
等等,只是之前绘制的对象,您希望与阴影每个着色器。例如,
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).
编辑:正如@rotoglup 非常正确地指出的那样,这种static
变量的使用将破坏延迟到 OpenGL 上下文被破坏之后,因此当析构函数尝试使用glDeleteProgram
/ 时glDeleteShader
,结果是不可预测的(充其量)。
While this may be excusable in a demo program, it's decidedly undesirable in real use. At the same time, you generally do notwant 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.
当然,在少数情况下,这也不是完全可取的。举个例子,考虑一下像 Galaga 或 Centipede 这样的古老的“杀死很多目标”游戏的 3D 版本。对于此类游戏,您需要相对快速地创建和销毁大量基本相同的目标。给定大量本质上相同的目标,您可能希望使用类似 a 的东西shared_ptr<shader_prog>
来创建着色器的单个实例,该实例在特定目标类型的所有实例之间共享。鉴于您多次重复使用相同的目标类型,您可能想要更进一步,因此您在整个游戏中保持相同的着色器,而不仅仅是在显示特定类型的目标时。
In any case, we're getting a bit off-track here. The point is that compiling and linking shaders is a fairlyexpensive 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).
无论如何,我们在这里有点偏离轨道。关键是编译和链接着色器是一个相当昂贵的过程,所以你通常希望管理它们的生命周期以避免创建和销毁它们的频率比真正需要的要多得多(尽管这并不是说在整个过程中创建它们很重要)游戏开始,也只能在最后摧毁它们)。
回答by guesser
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.
大约一年半前,我处于类似的位置。我很快就找到了一个使用 GLSL 的简单教程和源代码......但我确实必须让 GLUT 和 GLEW 工作,我想我最终自己至少编译了其中一个。由于我使用的是 Windows(而 Windows 是一种非标准的特殊情况,开放项目很少对其进行全面处理),因此它也涉及一个荒谬的过程,我需要手动将 DLL 和头文件复制并粘贴到某些常见的地点。这总是很痛苦,我已经失去了做那种事情的大部分时间,但是我按照指示艰难地完成了整个过程,并且最终像一般情况一样解决了问题。
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
无论如何,我现在可以找到的使用 GLSL 的最方便的着色器示例是这个 - 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.
我很遗憾地说它确实使用了 GLUT 和 GLEW。如果您对这个问题有更好的答案,我也会立即成为提供代码的任何站点的粉丝。祝你好运。
回答by timday
This tutorialmight be useful (note that it does contain GLSL material besides the old Cg stuff).
本教程可能很有用(请注意,除了旧的 Cg 内容之外,它确实包含 GLSL 材料)。
Note that I'd consider writing shaders to implement non-graphical, GPGPU-type things to be an obsolete approach these days. OpenCL or CUDA are clearly they way to go in future.
请注意,我会考虑编写着色器来实现非图形的 GPGPU 类型的东西,这些天已经过时了。OpenCL 或 CUDA 显然是他们未来的发展方向。
回答by Matt Curtis
Although it's not your target, you might get something out of Noel Llopis's OpenGL ES 2.0 shader sample: http://www.mobileorchard.com/getting-started-with-opengl-es-20-on-the-iphone-3gs/
虽然它不是您的目标,但您可能会从 Noel Llopis 的 OpenGL ES 2.0 着色器示例中得到一些信息:http: //www.mobileorchard.com/getting-started-with-opengl-es-20-on-the-iphone-3gs/