什么是“即时模式”?给出一个代码示例。
什么时候必须使用立即模式而不是保留模式?使用每种方法的利弊是什么?
Answers:
“直接模式”的一个例子是使用glBegin
,并glEnd
与glVertex
它们之间。“立即模式”的另一个示例是glDrawArrays
与客户端顶点数组(即不是顶点缓冲区对象)一起使用。
通常,您永远都不想使用即时模式(也许您的第一个“ hello world”程序除外),因为它已被弃用,并且无法提供最佳性能。
立即模式之所以不理想,是因为图形卡与程序的流程直接关联。驱动程序无法告诉GPU在开始之前开始渲染glEnd
,因为它不知道何时完成提交数据,并且它也需要传输该数据(只能在之后执行glEnd
)。
同样,对于客户端顶点数组,驱动程序只能在调用时拉出数组的副本glDrawArrays
,并且这样做必须阻塞应用程序。原因是否则您可以在驱动程序捕获之前修改(或释放)阵列的内存。它无法计划此操作的早晚,因为它只知道数据恰好在某个时间点有效。
与此相反,如果使用例如顶点缓冲区对象,则用数据填充缓冲区并将其交给OpenGL。您的进程不再拥有此数据,因此无法再对其进行修改。驾驶员可以依靠这一事实,并且只要总线空闲就可以(甚至推测地)上传数据。
您的任何稍后呼叫glDrawArrays
或glDrawElements
调用都将进入工作队列并立即返回(在实际完成之前!),因此您的程序将继续提交命令,而驱动程序则一步一步地工作。他们也可能不需要等待数据到达,因为驱动程序早就可以这样做了。
因此,渲染线程和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);
可运行的保留示例
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 / ...但是我无法重现。