Какво означава „непосредствен режим“ в OpenGL?

What Doesimmediate Modemean Opengl



Решение:

Един пример за „непосредствен режим“ се използва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 ();

Редактиране:
По обща заявка същото в запазен режим би изглеждало донякъде така:



float verts = {...}; плаващи цветове = {...}; static_assert (sizeof (verts) == sizeof (цветове), ''); // не е наистина необходим за този пример, но задължителен в профила на ядрото след GL 3.2 GLuint vao; glGenVertexArrays (1, & vao); glBindVertexArray (vao); GLuint buf [2]; glGenBuffers (2, buf); // приемайки оформление (местоположение = 0) за позиция и // оформление (местоположение = 1) за цвят в вертексния шейдър // позиции на върхове 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); // копиране/поставяне за цвят ... същия код като по -горе. Истинска, нетривиална програма би // нормално използвала един буфер и за двете-обикновено със stride (5-ти параметър) до // glVertexAttribPointer-което предполага преплитане на масивите verts и цветове. // Това е малко по-грозно, но има по-добра производителност на кеша (грозното обаче няма // значение за истинска програма, тъй като данните така или иначе се зареждат от генериран // инструмент за моделиране // двоичен файл). glBindBuffer (GL_ARRAY_BUFFER, buf [1]); glBufferData (GL_ARRAY_BUFFER, sizeof (цветове), цветове, GL_STATIC_DRAW); glEnableVertexAttribArray (1); glVertexAttribPointer (1, 3, GL_FLOAT, GL_FALSE, 0, 0); glDrawArrays (GL_TRIANGLES, 0, 3); 

Изпълним запазен пример

Деймън е предоставил ключовите части, но новобранците като мен ще търсят пълен пример за изпълнение.

въведете описание на изображението тук

main.c

#include #include #define GLEW_STATIC #include #include #define INFOLOG_LEN 512 static const GLuint WIDTH = 512, HEIGHT = 512; / * данните за върха се предават като входни данни към този шейдър * ourColor се предава като вход към към фрагментния шейдър. */ static const GLchar* vertexShaderSource = '#version 330 core  n '' оформление (местоположение = 0) в позиция vec3;  n '' оформление (местоположение = 1) в цвят vec3;  n '' извън vec3 ourColor;  n '' void main () { n '' gl_Position = vec4 (позиция, 1.0f);  n '' ourColor = цвят;  n ''}  n '; static const GLchar* fragmentShaderSource = '#version 330 core  n' 'във vec3 ourColor;  n' 'цвят vec4;  n' 'void main () { n' 'color = vec4 (ourColor, 1.0f);  n ''}  n '; GLfloat върхове [] = { / * Позиции Цветове * / 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 незабавно = (argc> 1) && argv [1] [0] == '1'; /* Използва се само в! Незабавно. */ GLuint vao, vbo; GLint shaderProgram; glfwInit (); GLFWwindow* window = glfwCreateWindow (WIDTH, HEIGHT, __FILE__, NULL, NULL); glfwMakeContextCurrent (прозорец); glewExperimental = GL_TRUE; glewInit (); glClearColor (0.0f, 0.0f, 0.0f, 1.0f); glViewport (0, 0, WIDTH, HEIGHT); if (незабавно) {float ratio; int ширина, височина; glfwGetFramebufferSize (прозорец, & ширина, & височина); съотношение = ширина / (поплавък) височина; glClear (GL_COLOR_BUFFER_BIT); glMatrixMode (GL_PROJECTION); glLoadIdentity (); glOrtho (-отношение, съотношение, -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 { /* Изграждане и компилиране на шейдър програма. *// *Vertex shader */ GLint vertexShader = glCreateShader (GL_VERTEX_SHADER); glShaderSource (vertexShader, 1, & vertexShaderSource, NULL); glCompileShader (vertexShader); Успех на GLint; GLchar infoLog [INFOLOG_LEN]; glGetShaderiv (vertexShader, GL_COMPILE_STATUS и успех); if (! успех) {glGetShaderInfoLog (vertexShader, INFOLOG_LEN, NULL, infoLog); printf ('ГРЕШКА :: 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 и успех); if (! успех) {glGetShaderInfoLog (fragmentShader, INFOLOG_LEN, NULL, infoLog); printf ('ГРЕШКА :: SHADER :: FRAGMENT :: COMPILATION_FAILED  n%s  n', infoLog); } / * Свързване на шейдъри * / shaderProgram = glCreateProgram (); glAttachShader (shaderProgram, vertexShader); glAttachShader (shaderProgram, fragmentShader); glLinkProgram (shaderProgram); glGetProgramiv (shaderProgram, GL_LINK_STATUS и успех); if (! успех) {glGetProgramInfoLog (shaderProgram, INFOLOG_LEN, NULL, infoLog); printf ('ГРЕШКА :: SHADER :: ПРОГРАМА :: 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 (върхове), върхове, GL_STATIC_DRAW); / * Атрибут на позиция */ glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof (GLfloat), (GLvoid *) 0); glEnableVertexAttribArray (0); / * Цветен атрибут */ 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 (прозорец); /* Основен контур. */ while (! glfwWindowShouldClose (window)) {glfwPollEvents (); } if (! незабавно) {glDeleteVertexArrays (1, & vao); glDeleteBuffers (1, & vbo); glDeleteProgram (shaderProgram); } glfwTerminate (); връщане EXIT_SUCCESS; }

Адаптирано от Learn OpenGL, моят GitHub нагоре по веригата.

Компилирайте и стартирайте на 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 # Незабавен ./main .изход 1

От това виждаме как:

Когато използвате шейдъри:

  • вершинните и фрагментните шейдър програми се представят като низове в стил C, съдържащи език GLSL (vertexShaderSource иfragmentShaderSource) в обикновена C програма, която работи на процесора

  • тази C програма прави OpenGL повиквания, които компилират тези низове в GPU код, например:

    glShaderSource (fragmentShader, 1, & fragmentShaderSource, NULL); glCompileShader (fragmentShader);
  • шейдърът определя техните очаквани входове, а програмата C ги предоставя чрез указател към паметта към кода на графичния процесор. Например, фрагментният шейдър определя своите очаквани входове като масив от позиции и цветове на върховете:

    'layout (location = 0) in vec3 position;  n' 'layout (location = 1) in vec3 color;  n' 'out vec3 ourColor;  n'

    и също така определя един от неговите изходиourColor като масив от цветове, който след това става вход за фрагментарен шейдър:

    static const GLchar* fragmentShaderSource = '#version 330 core  n '' във vec3 ourColor;  n'

    След това програмата C предоставя масива, съдържащ позициите и цветовете на върховете от процесора до графичния процесор

    glBufferData (GL_ARRAY_BUFFER, sizeof (върхове), върхове, GL_STATIC_DRAW);

В непосредствения пример без шейдър обаче виждаме, че се правят магически API извиквания, които изрично дават позиции и цветове:

glColor3f (1.0f, 0.0f, 0.0f); glVertex3f (-0.5f, -0.5f, 0.0f);

Разбираме следователно, че това представлява много по-ограничен модел, тъй като позициите и цветовете вече не са произволни дефинирани от потребителя масиви в паметта, а по-скоро само входове към модел, подобен на Phong.

И в двата случая визуализираният изход обикновено отива направо към видеото, без да преминава обратно през процесора, въпреки че е възможно да се чете в процесора, напр. ако искате да ги запишете във файл: Как да използвате GLUT/OpenGL за изобразяване на файл?

Повечето „съвременни“ уроци по OpenGL, нормално запазен режим и GLFW, ще намерите много примери на адрес:

  • https://github.com/JoeyDeVries/LearnOpenGL
  • https://github.com/opengl-tutorials/ogl
  • https://github.com/capnramses/antons_opengl_tutorials_book
  • https://github.com/Overv/Open.GL/tree/master/content/code
  • https://github.com/tomdalling/opengl-series