我必须在OpenGL中为不同的对象使用两个不同的着色器程序。
我发现我必须使用glUseProgram()
在不同的着色器程序之间切换,但没有太多信息。
如果我有两个不同的着色器程序用于不同的对象,那么生成和绑定VAO和VBO如何为每个着色器程序(如何和何时)工作?
答案 0 :(得分:6)
在OpenGL中渲染对象时,代码将如下所示:
使用glUseProgram
绑定程序,将制服设置为glUniform4fv
,glUniformMatrix4fv
等
使用glBindVertexArray
绑定顶点数组。
使用glActiveTexture
和glBindTexture
绑定您需要的任何纹理。
更改任何其他州,例如glEnable
,glDisable
,glBlendFunc
。
使用glDrawArrays
或glDrawElements
。
如果需要,请将状态重置为默认值。
这些都是您在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);