"Chế độ tức thì" là gì? Đưa ra một ví dụ về mã.
Khi nào tôi phải sử dụng chế độ ngay lập tức thay vì chế độ giữ lại? Ưu và nhược điểm của việc sử dụng từng phương pháp là gì?
Câu trả lời:
Một ví dụ về "chế độ ngay lập tức" đang sử dụng glBegin
và glEnd
với glVertex
ở giữa chúng. Một ví dụ khác về "chế độ tức thời" là sử dụng glDrawArrays
với mảng đỉnh máy khách (tức là không phải đối tượng bộ đệm đỉnh).
Thông thường, bạn sẽ không bao giờ muốn sử dụng chế độ tức thì (ngoại trừ có thể cho chương trình "hello world" đầu tiên của bạn) vì nó không dùng chức năng nữa và không mang lại hiệu suất tối ưu.
Lý do tại sao chế độ tức thời không tối ưu là do card đồ họa được liên kết trực tiếp với luồng chương trình của bạn. Trình điều khiển không thể yêu cầu GPU bắt đầu hiển thị trước glEnd
vì nó không biết khi nào bạn sẽ hoàn tất việc gửi dữ liệu và nó cũng cần chuyển dữ liệu đó (điều này chỉ có thể thực hiện sau đó glEnd
).
Tương tự, với mảng đỉnh máy khách, trình điều khiển chỉ có thể kéo một bản sao của mảng của bạn vào thời điểm bạn gọi glDrawArrays
và nó phải chặn ứng dụng của bạn trong khi làm như vậy. Lý do là nếu không, bạn có thể sửa đổi (hoặc giải phóng) bộ nhớ của mảng trước khi trình điều khiển chiếm nó. Nó không thể lên lịch cho hoạt động đó sớm hơn hay muộn hơn, bởi vì nó chỉ biết rằng dữ liệu là hợp lệ chính xác tại một thời điểm.
Ngược lại với điều đó, nếu bạn sử dụng ví dụ như một đối tượng bộ đệm đỉnh, bạn điền vào bộ đệm với dữ liệu và giao nó cho OpenGL. Quy trình của bạn không còn sở hữu dữ liệu này nữa và do đó không thể sửa đổi nó nữa. Người lái xe có thể dựa vào thực tế này và có thể (thậm chí suy đoán) tải dữ liệu lên bất cứ khi nào xe buýt rảnh.
Bất kỳ cuộc gọi sau glDrawArrays
hoặc glDrawElements
cuộc gọi nào của bạn sẽ chỉ đi vào hàng đợi công việc và trả về ngay lập tức (trước khi thực sự kết thúc!), Vì vậy chương trình của bạn tiếp tục gửi lệnh trong khi cùng lúc trình điều khiển hoạt động từng cái một. Họ cũng có thể sẽ không cần đợi dữ liệu đến vì người lái xe đã có thể làm điều đó sớm hơn nhiều.
Do đó, luồng kết xuất và GPU chạy không đồng bộ, mọi thành phần đều bận rộn mọi lúc, điều này mang lại hiệu suất tốt hơn.
Chế độ tức thời có lợi thế là sử dụng đơn giản, nhưng sau đó sử dụng lại OpenGL đúng cách theo cách không bị phản đối cũng không phải là khoa học tên lửa chính xác - nó chỉ mất rất ít công việc bổ sung.
Đây là mã OpenGL "Hello World" điển hình ở chế độ ngay lập tức:
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();
Chỉnh sửa:
Theo yêu cầu chung, điều tương tự ở chế độ được giữ lại sẽ giống như sau:
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);
Ví dụ được giữ lại Runnable
Damon đã cung cấp các phần quan trọng, nhưng những người mới như tôi sẽ tìm kiếm một ví dụ đầy đủ có thể chạy được.
C chính
#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 = 512, HEIGHT = 512;
/* 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(int argc, char **argv) {
int immediate = (argc > 1) && argv[1][0] == '1';
/* Used in !immediate only. */
GLuint vao, vbo;
GLint shaderProgram;
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);
if (immediate) {
float ratio;
int width, height;
glfwGetFramebufferSize(window, &width, &height);
ratio = width / (float) height;
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-ratio, ratio, -1.f, 1.f, 1.f, -1.f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glBegin(GL_TRIANGLES);
glColor3f( 1.0f, 0.0f, 0.0f);
glVertex3f(-0.5f, -0.5f, 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();
} else {
/* 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 */
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);
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);
glUseProgram(shaderProgram);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
}
glfwSwapBuffers(window);
/* Main loop. */
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
}
if (!immediate) {
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glDeleteProgram(shaderProgram);
}
glfwTerminate();
return EXIT_SUCCESS;
}
Phỏng theo Tìm hiểu OpenGL , GitHub ngược dòng của tôi .
Biên dịch và chạy trên Ubuntu 20.04:
sudo apt install libglew-dev libglfw3-dev
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c -lGL -lGLEW -lglfw
# Shader
./main.out
# Immediate
./main.out 1
Từ đó chúng tôi thấy cách:
Khi sử dụng shader:
các chương trình tô bóng đỉnh và phân đoạn đang được biểu diễn dưới dạng chuỗi kiểu C chứa ngôn ngữ GLSL ( vertexShaderSource
và fragmentShaderSource
) bên trong chương trình C thông thường chạy trên CPU
chương trình C này thực hiện các lệnh gọi OpenGL biên dịch các chuỗi đó thành mã GPU, ví dụ:
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
shader xác định các đầu vào dự kiến của chúng và chương trình C cung cấp chúng thông qua một con trỏ tới bộ nhớ cho mã GPU. Ví dụ, trình đổ bóng phân mảnh xác định các đầu vào dự kiến của nó là một mảng các vị trí và màu sắc đỉnh:
"layout (location = 0) in vec3 position;\n"
"layout (location = 1) in vec3 color;\n"
"out vec3 ourColor;\n"
và cũng định nghĩa một trong các đầu ra của nó ourColor
là một mảng màu, sau đó trở thành đầu vào cho bộ đổ bóng phân mảnh:
static const GLchar* fragmentShaderSource =
"#version 330 core\n"
"in vec3 ourColor;\n"
Sau đó, chương trình C cung cấp mảng chứa các vị trí đỉnh và màu sắc từ CPU đến GPU
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
Tuy nhiên, trên ví dụ không đổ bóng ngay lập tức, chúng ta thấy rằng các lệnh gọi API ma thuật được thực hiện để cung cấp các vị trí và màu sắc một cách rõ ràng:
glColor3f( 1.0f, 0.0f, 0.0f);
glVertex3f(-0.5f, -0.5f, 0.0f);
Do đó, chúng tôi hiểu rằng điều này đại diện cho một mô hình bị hạn chế hơn nhiều, vì các vị trí và màu sắc không phải là các mảng do người dùng xác định tùy ý trong bộ nhớ nữa, mà chỉ là đầu vào cho một mô hình giống như Phong.
Trong cả hai trường hợp, đầu ra được kết xuất thường đi thẳng đến video mà không chuyển ngược lại qua CPU, mặc dù có thể đọc cho CPU, ví dụ như nếu bạn muốn lưu chúng vào một tệp: Cách sử dụng GLUT / OpenGL để kết xuất một tập tin?
Hầu hết các hướng dẫn OpenGL "hiện đại" thường được giữ lại ở chế độ và GLFW, bạn sẽ tìm thấy nhiều ví dụ tại:
ERROR::SHADER::VERTEX::COMPILATION_FAILED
thì bạn có thể sửa nó bằng glfwWindowHint
cách được hiển thị tại: stackoverflow.com/questions/52592309/… Tuy nhiên, tôi không thể tạo lại.
export MESA_GL_VERSION_OVERRIDE=3.3
trước khi chạy main.out (Debian 8) như một trong những câu trả lời chỉ ra trên cùng bài đăng mà bạn đã chia sẻ.