OpenGL中的“即时模式”是什么意思?


80

什么是“即时模式”?给出一个代码示例。

什么时候必须使用立即模式而不是保留模式?使用每种方法的利弊是什么?

Answers:


141

“直接模式”的一个例子是使用glBegin,并glEndglVertex它们之间。“立即模式”的另一个示例是glDrawArrays与客户端顶点数组(即不是顶点缓冲区对象)一起使用。

通常,您永远都不想使用即时模式(也许您的第一个“ hello world”程序除外),因为它已被弃用,并且无法提供最佳性能。

立即模式之所以不理想,是因为图形卡与程序的流程直接关联。驱动程序无法告诉GPU在开始之前开始渲染glEnd,因为它不知道何时完成提交数据,并且它也需要传输该数据(只能在之后执行glEnd)。
同样,对于客户端顶点数组,驱动程序只能在调用时拉出数组的副本glDrawArrays,并且这样做必须阻塞应用程序。原因是否则您可以在驱动程序捕获之前修改(或释放)阵列的内存。它无法计划此操作的早晚,因为它只知道数据恰好在某个时间点有效。

与此相反,如果使用例如顶点缓冲区对象,则用数据填充缓冲区并将其交给OpenGL。您的进程不再拥有此数据,因此无法再对其进行修改。驾驶员可以依靠这一事实,并且只要总线空闲就可以(甚至推测地)上传数据。
您的任何稍后呼叫glDrawArraysglDrawElements调用都将进入工作队列并立即返回(在实际完成之前!),因此您的程序将继续提交命令,而驱动程序则一步一步地工作。他们也可能不需要等待数据到达,因为驱动程序早就可以这样做了。
因此,渲染线程和GPU异步运行,每个组件始终处于繁忙状态,从而产生更好的性能。

即时模式的确具有使用简单的优势,但是再次以不弃用的方式正确使用OpenGL也不完全是火箭科学-它只需要很少的额外工作。

这是即时模式下的典型OpenGL“ Hello World”代码:

glBegin(GL_TRIANGLES);
    glColor3f(1.0f, 0.0f, 0.0f);   glVertex2f(0.0f,   1.0f);
    glColor3f(0.0f, 1.0f, 0.0f);   glVertex2f(0.87f,  -0.5f);
    glColor3f(0.0f, 0.0f, 1.0f);   glVertex2f(-0.87f, -0.5f);
glEnd();

编辑:
通过常见的请求,在保留模式下的同一件事看起来像这样:

float verts = {...};
float colors = {...};
static_assert(sizeof(verts) == sizeof(colors), "");

// not really needed for this example, but mandatory in core profile after GL 3.2
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

GLuint buf[2];
glGenBuffers(2, buf);

// assuming a layout(location = 0) for position and 
// layout(location = 1) for color in the vertex shader

// vertex positions
glBindBuffer(GL_ARRAY_BUFFER, buf[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
glEnableVertexAttribArray(0); 
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

// copy/paste for color... same code as above. A real, non-trivial program would
// normally use a single buffer for both -- usually with stride (5th param) to
// glVertexAttribPointer -- that presumes interleaving the verts and colors arrays.
// It's somewhat uglier but has better cache performance (ugly does however not
// matter for a real program, since data is loaded from a modelling-tool generated
// binary file anyway).
glBindBuffer(GL_ARRAY_BUFFER, buf[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
glEnableVertexAttribArray(1); 
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);

glDrawArrays(GL_TRIANGLES, 0, 3); 

1
非常感谢Damon,非常有趣的比较。对我来说,这的确确实复杂得多,但是我想一旦我正确理解管道就会变得更加清晰……
mallardz 2014年

6
@mallardz:使用现代OpenGL要做任何事情都困难得多,但是实际上,一旦您克服了最初的障碍(并且更快),它就会变得更容易。立即模式非常好,因为进入门槛极低。我的示例仍然缺少您也需要提供的顶点着色器和片段着色器(非常基本的着色器)。一个可以实际编译和运行的东西的完整运行示例很长。:-)
Damon 2014年

18

可运行的保留示例

Damon提供了关键部分,但是像我这样的新手将在寻找一个完整的示例。

main.c

#include <stdio.h>
#include <stdlib.h>

#define GLEW_STATIC
#include <GL/glew.h>

#include <GLFW/glfw3.h>

#define INFOLOG_LEN 512

static const GLuint WIDTH = 800, HEIGHT = 600;
/* vertex data is passed as input to this shader
 * ourColor is passed as input to the to the fragment shader.
*/
static const GLchar* vertexShaderSource =
    "#version 330 core\n"
    "layout (location = 0) in vec3 position;\n"
    "layout (location = 1) in vec3 color;\n"
    "out vec3 ourColor;\n"
    "void main() {\n"
    "    gl_Position = vec4(position, 1.0f);\n"
    "    ourColor = color;\n"
    "}\n";
static const GLchar* fragmentShaderSource =
    "#version 330 core\n"
    "in vec3 ourColor;\n"
    "out vec4 color;\n"
    "void main() {\n"
    "    color = vec4(ourColor, 1.0f);\n"
    "}\n";
GLfloat vertices[] = {
/*   Positions            Colors */
     0.5f, -0.5f, 0.0f,   1.0f, 0.0f, 0.0f,
    -0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,
     0.0f,  0.5f, 0.0f,   0.0f, 0.0f, 1.0f
};

int main(void) {
    glfwInit();
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, __FILE__, NULL, NULL);
    glfwMakeContextCurrent(window);
    glewExperimental = GL_TRUE;
    glewInit();
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glViewport(0, 0, WIDTH, HEIGHT);

    /* Build and compile shader program. */
    /* Vertex shader */
    GLint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);
    GLint success;
    GLchar infoLog[INFOLOG_LEN];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(vertexShader, INFOLOG_LEN, NULL, infoLog);
        printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", infoLog);
    }
    /* Fragment shader */
    GLint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(fragmentShader, INFOLOG_LEN, NULL, infoLog);
        printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", infoLog);
    }
    /* Link shaders */
    GLint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgram, INFOLOG_LEN, NULL, infoLog);
        printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", infoLog);
    }
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    GLuint vbo, vao;
    glGenVertexArrays(1, &vao);
    glGenBuffers(1, &vbo);
    glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    /* Position attribute */
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    /* Color attribute */
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);
    glBindVertexArray(0);

    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();
        glClear(GL_COLOR_BUFFER_BIT);
        glUseProgram(shaderProgram);
        glBindVertexArray(vao);
        glDrawArrays(GL_TRIANGLES, 0, 3);
        glBindVertexArray(0);
        glfwSwapBuffers(window);
    }
    glDeleteVertexArrays(1, &vao);
    glDeleteBuffers(1, &vbo);
    glfwTerminate();
    return EXIT_SUCCESS;
}

在Ubuntu 18.10上:

sudo apt-get install libglew-dev libglfw3-dev
gcc main.c -lGL -lGLEW -lglfw

立即“等效”:

glBegin(GL_TRIANGLES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(0.5f, -0.5.0f, 0.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(-0.5f, -0.5f, 0.0f);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 0.5f, 0.0f);
glEnd();

此示例从此处改编。

大多数“现代” OpenGL教程通常都保留模式和GLFW,您可以在以下位置找到许多示例:


我有一份报告,如果您遇到错误,ERROR::SHADER::VERTEX::COMPILATION_FAILED则可以使用glfwWindowHint如下所示的方法进行修复:stackoverflow.com/questions/52592309 / ...但是我无法重现。
西罗Santilli郝海东冠状病六四事件法轮功
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.