【问题标题】:Easy framework for OpenGL Shaders in C/C++C/C++ 中 OpenGL 着色器的简单框架
【发布时间】:2011-02-17 05:05:32
【问题描述】:

我只是想在平面图像上尝试一些着色器。事实证明,编写一个仅将图片作为纹理并应用(假设是高斯模糊)作为片段着色器的 C 程序并不容易:您必须初始化 OpenGL,这就像 100 行代码,然后了解 GLBuffers 等。此外,要与窗口系统进行通信,还必须使用另一个框架 GLUT。

事实证明,Nvidia 的 Fx 作曲家很适合使用着色器。但我仍然希望有一个简单的 C 或 C++ 程序,它将给定的片段着色器应用于图像并显示结果。有人有例子还是有框架?

【问题讨论】:

    标签: c++ c opengl


    【解决方案1】:

    首先,我会避免使用 glut——它有缺陷,大约十年没有更新,而且它的设计与当今大多数人的需求不太相符(例如,尽管您 可以将它用于动画,它实际上主要用于产生静态显示)。我在a previous answer 中指出了一些过剩的替代方案。

    这(大部分)将代码留给编译、链接和使用着色器。为此,我编写了一个我觉得很方便的小类:

    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);
        }
    };
    

    对于一个简单的演示,几个“直通”着色器(只是模仿固定功能的管道):

    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",
        "}"
    };
    

    你会使用类似的东西:

    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();
    }
    

    例如,如果您要在绘制场景的过程中使用多个不同的片段着色器,您只需为每个片段定义一个静态对象,然后执行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();
    }
    

    编辑:正如@rotoglup 非常正确地指出的那样,static 变量的这种使用会延迟销毁,直到 OpenGL 上下文被销毁之后,所以当析构函数尝试使用glDeleteProgram/glDeleteShader 时,结果是不可预测的(充其量)。

    虽然在演示程序中这可能是情有可原的,但在实际使用中肯定是不可取的。同时,您通常希望每次输入使用着色器的函数时都重新编译着色器。

    为避免这两个问题,您通常希望将着色器对象创建为类实例的成员,其生命周期反过来又与要着色的任何东西的生命周期相关联:

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

    这将在您创建该类型的角色时编译/链接一次着色器程序,并在您销毁该角色时销毁它。

    当然,在少数情况下,这也不是完全可取的。例如,考虑一个 3D 版本的古代“杀死很多目标”游戏,如 Galaga 或 Centipede。对于这样的游戏,您会相对较快地创建和摧毁许多基本相同的目标。给定大量基本相同的目标,您可能希望使用类似shared_ptr&lt;shader_prog&gt; 的东西来创建在特定目标类型的所有实例之间共享的着色器的单个实例。鉴于您多次重复使用相同的目标类型,您可能还想走得更远一些,因此您在整个游戏中保持相同的着色器,而不仅仅是在显示特定类型的目标时。

    无论如何,我们在这里有点偏离轨道。关键是编译和链接着色器是一个相当昂贵的过程,因此您通常希望管理它们的生命周期以避免创建和销毁它们比真正必要的频率高很多(尽管这并不是说它是在游戏开始时将它们全部创建并仅在结束时销毁它们至关重要)。

    【讨论】:

    • 非常感谢您的代码!有时间我会试试的:)
    • 很抱歉对这个旧答案发表评论,但正如它出现在 opengl 常见问题解答问题中一样,它可能是值得的:我发现您在 shader_prog 的实例中相当大胆地使用了 static,这会将析构函数调用推迟到进程结束时,OpenGL 上下文将早已不复存在。这通常会导致应用程序在退出时崩溃。这可能适用于测试应用,但不适用于生产用途。
    • @rotoglup:根本不是问题——你提出了一个公平的观点。我添加了更多关于更好地管理他们的生命周期的内容。
    • 我不知道,如果你知道,但在 c++ 中你可以这样做:“STRING_PART_1”\n[代码中的新行]“STRING_PART_2”。所以你可以创建字符串,并将其作为程序传递,而不是字符串数组(不同的是,在定义数组时你不必使用逗号)
    • 我发现你为你的程序重载 () 运算符的那部分相当不可读...... - 当你知道源代码中发生了什么时 - 很好,但作为一个 API,我会非常非常,很困惑...
    【解决方案2】:

    大约一年半前,我处于类似的位置。我很快找到了一个使用 GLSL 的简单教程和源代码。但我确实必须让 GLUT 和 GLEW 工作,我想我最终自己至少编译了其中一个。由于我使用的是 Windows(而且 Windows 是一种非标准的特殊情况,开放项目很少对其进行全面处理),它还涉及一个荒谬的过程,我需要手动将 DLL 和头文件复制并粘贴到某些常见的地点。这总是一种痛苦,而且我在做这种事情时已经失去了我生命中的大部分时间,但我按照指示艰难地完成了这个过程,并且最终按照通常的情况解决了。

    无论如何,我现在能找到的最方便的使用 GLSL 的着色器示例就是这个 - http://www.lighthouse3d.com/opengl/glsl/index.php?minimal

    它不会像你希望的那样专门修改纹理。但根据我的经验,一旦你得到这样的代码,编译和运行体验会更愉快,你会很快取得进步并能够拼接成碎片如有必要,请参阅其他教程。我可以说,一旦我运行了一个示例,我就使用相同的框架快速解决了家庭和工作中的许多问题。

    很抱歉,它确实使用了 GLUT 和 GLEW。如果您对这个问题有更好的答案,我也会立即成为提供代码的任何网站的粉丝。祝你好运。

    【讨论】:

    • OSX在这里..也许我今天太累了,明天再试试
    【解决方案3】:

    这个tutorial 可能有用(请注意,除了旧的 Cg 内容之外,它还包含 GLSL 材料)。

    请注意,我会考虑编写着色器来实现非图形的 GPGPU 类型的东西,这些天来是一种过时的方法。 OpenCL 或 CUDA 显然是他们未来的发展方向。

    【讨论】:

      【解决方案4】:

      虽然它不是您的目标,但您可能会从 Noel Llopis 的 OpenGL ES 2.0 着色器示例中得到一些东西: http://www.mobileorchard.com/getting-started-with-opengl-es-20-on-the-iphone-3gs/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-04-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-05
        • 1970-01-01
        • 2010-11-04
        • 1970-01-01
        相关资源
        最近更新 更多