c++ - C/C++ 中 OpenGL 着色器的简单框架

标签 c++ c opengl

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

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

最佳答案

首先,我会避免使用 glut —— 它有缺陷,大约十年没有更新,而且它的设计与当今大多数人想要的不太相符(例如,虽然你 < em>可以将它用于动画,它实际上主要用于产生静态显示)。我在 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<shader_prog> 之类的东西。创建在特定目标类型的所有实例之间共享的着色器的单个实例。鉴于您多次重复使用相同的目标类型,您可能还想走得更远一些,因此您在整个游戏中保持相同的着色器,而不仅仅是在显示特定类型的目标时。

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

关于c++ - C/C++ 中 OpenGL 着色器的简单框架,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2795044/

相关文章:

c++ - 为什么 C 字 rune 字是 int 而不是 char?

c - 在 C 中返回本地指针

c++ - 编译 GLFW 时遇到问题, undefined symbol

c++ - OpenGL以像素为单位定义顶点位置

c++ - 如何实例化C++成员字符串引用

c++ - 使用静态函数变量与类变量来存储一些状态

c++ - 设置精度和裁剪尾随零但从不打印指数

c++ - 将 OpenGL 代码从 C++ 移植到 Python

c++ - 使用 Doxygen 时不要在详细描述中包含简要描述

c - Win32 API - 尝试读取文件时,它被截断了——为什么?