在OpenGL中使用不同的着色器程序?

时间:2016-10-07 18:19:17

标签: c++ opengl glsl

我必须在OpenGL中为不同的对象使用两个不同的着色器程序。

我发现我必须使用glUseProgram()在不同的着色器程序之间切换,但没有太多信息。

如果我有两个不同的着色器程序用于不同的对象,那么生成和绑定VAO和VBO如何为每个着色器程序(如何和何时)工作?

2 个答案:

答案 0 :(得分:6)

在OpenGL中渲染对象时,代码将如下所示:

  • 使用glUseProgram绑定程序,将制服设置为glUniform4fvglUniformMatrix4fv

  • 使用glBindVertexArray绑定顶点数组。

  • 使用glActiveTextureglBindTexture绑定您需要的任何纹理。

  • 更改任何其他州,例如glEnableglDisableglBlendFunc

  • 使用glDrawArraysglDrawElements

  • 进行绘制
  • 如果需要,请将状态重置为默认值。

这些都是您在vanilla OpenGL 3代码中所做的所有事情。你应该已经有了这个部分。

如果需要使用不同的着色器程序编写多个对象,则只需多次执行上述步骤即可。如果要对多个程序使用相同的状态(除了为每个程序单独保存的制服外),可以省略状态更改。例如,您可以使用相同的VAO,相同的纹理,相同的混合函数等等。

如果您正在寻找更详细的示例,有许多关于OpenGL 3绘图命令如何工作的教程。

答案 1 :(得分:0)

可以切换程序对象而不必再次指定它们的参数。但是,为了使其工作,必须在使用glBindAttribLocation进行编译和链接之前预先指定顶点属性数组索引的值,并确保每个程序都使用单独的索引。如果您不这样做,则同一个VBO可能会同时进入这两个程序。两个程序使用的VBO必须都在同一个VAO中,并且必须在程序处于活动状态并且执行Draw命令时将其绑定。

这是一个例子:

// HELPER FUNCTIONS
// ================
// Read a file to a string.
char *load(const char *fn)
{
    int fd = open(fn, O_RDONLY);
    assert(fd != -1);

    off_t size = lseek(fd, 0, SEEK_END);
    assert(size != -1);
    off_t res = lseek(fd, 0, SEEK_SET);
    assert(res != -1);

    size++;  // null terminator
    char *buf = (char *)malloc(size);

    char *p = buf;
    for (;;) {
        // File has gotten bigger since we started? Fuck that.
        assert(p - buf < size);
        ssize_t nread = read(fd, (char *)p, 0x10000);
        assert(nread != -1);
        if (nread == 0) {
            *p = '\0';
            break;
        }

#ifndef NDEBUG
        // Null character? Fuck that shit.
        void *nullbyte = memchr((char *)p, '\0', nread);
        assert(nullbyte == NULL);
#endif

        p += nread;
    }
    int cres = close(fd);
    assert(cres == 0);

    return buf;
}

// Compile the "type" shader named "filename" and attach it to
// "shader_program".
static void compile_shader(GLenum type, const char *filename,
        GLuint shader_program)
{
    GLuint shader = glCreateShader(type);
    char *source = load(filename);
    glShaderSource(shader, 1, &source, 0);
    glCompileShader(shader);
#ifndef NDEBUG
    GLint success;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
    if (!success) {
        GLint log_length;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
        char *log = (char *)malloc(log_length);
        glGetShaderInfoLog(shader, log_length, NULL, log);
        fprintf(stderr, "Failed to compile %s:\n%s", filename, log);
        abort();
    }
#endif
    glAttachShader(shader_program, shader);
}
// Return a shader program with vertex shader "vertfile" and the fragment
// shader "fragfile". The program is not linked, in case stuff still needs to
// be added.
GLuint compile_shader_program(const char *vertfile, const char *fragfile)
{
    GLuint p = glCreateProgram();
    compile_shader(GL_VERTEX_SHADER, vertfile, p);
    compile_shader(GL_FRAGMENT_SHADER, fragfile, p);
    return p;
}


// . . . 


// INIT
// ====

// Make and bind the VAO, shared between both programs.
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

// Init one_program.
float square[] = {0.f, 0.f,  1.f, 0.f,  0.f, 1.f,  1.f, 1.f};
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(square), square, GL_STATIC_DRAW);
// Make sure these indices are unique among all VBOs you are using to render.
GLuint attr = 0;
glEnableVertexAttribArray(attr);
glVertexAttribPointer(attr, 2, GL_FLOAT, GL_FALSE, 0, NULL);

GLuint one_program = compile_shader_program("glsl/2d.vert", "glsl/white.frag");
glBindAttribLocation(one_program, attr, "vertex_pos");
glLinkProgram(one_program);

// Set uniforms.
// . . .

// Do the same thing for the_other_program.
float triangle[] = {0.f, 0.f,  1.f, 0.f,  0.f, 1.f};
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(square), square, GL_STATIC_DRAW);
attr++;
glEnableVertexAttribArray(attr);
glVertexAttribPointer(attr, 2, GL_FLOAT, GL_FALSE, 0, NULL);

GLuint the_other_program =
    compile_shader_program("glsl/2d.vert", "glsl/chrome.frag");
glBindAttribLocation(the_other_program, attr, "vertex_pos");
glLinkProgram(the_other_program);

// Set uniforms.
// . . .



// RENDER ONE FRAME
// ================
glUseProgram(one_program);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

glUseProgram(the_other_program);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);