Ответ 1
Один пример "немедленного режима" использует glBegin
и glEnd
с glVertex
между ними. Другим примером "немедленного режима" является использование glDrawArrays
с массивом вершин клиента (т.е. не объект-объект вершин).
Обычно вы никогда не захотите использовать немедленный режим (за исключением, возможно, для вашей первой программы "привет мир" ), потому что это устаревшая функциональность и не обеспечивает оптимальную производительность.
Причина, по которой немедленный режим не является оптимальным, заключается в том, что графическая карта напрямую связана с потоком вашей программы. Драйвер не может сказать, что графический процессор начнет рендеринг перед glEnd
, потому что он не знает, когда вы закончите подавать данные, и ему также необходимо перенести эти данные (что можно делать только после glEnd
).
Аналогично, с массивом вершин клиента, драйвер может вытащить копию вашего массива только в момент вызова glDrawArrays
, и при этом он должен заблокировать ваше приложение. Причина в том, что в противном случае вы могли бы модифицировать (или освобождать) память массива до того, как водитель его захватит. Он не может планировать эту операцию раньше или позже, поскольку он знает только, что данные действительны точно в один момент времени.
В отличие от этого, если вы используете, например, объект буфера вершин, вы заполняете буфер данными и передаете его в OpenGL. Ваш процесс больше не владеет этими данными и поэтому не может его изменять. Драйвер может положиться на этот факт и может (даже спекулятивно) загружать данные всякий раз, когда автобус свободен.
Любой из ваших более поздних вызовов glDrawArrays
или glDrawElements
просто войдет в рабочую очередь и сразу же вернется (до фактического завершения!), Поэтому ваша программа продолжает отправлять команды, в то время как драйвер работает один за другим. Им также, вероятно, не нужно ждать, пока данные прибудут, потому что водитель уже мог сделать это намного раньше.
Таким образом, рендеринг потоков и графических процессоров выполняется асинхронно, каждый компонент всегда занят, что обеспечивает лучшую производительность.
Непосредственный режим имеет преимущество, заключающееся в том, что он мертв, простое в использовании, но с использованием 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();
Edit:
По общему запросу то же самое в сохраненном режиме будет выглядеть примерно так:
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 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);