Що означає "негайний режим" у OpenGL?


82

Що таке "негайний режим"? Наведіть приклад коду.

Коли мені потрібно використовувати негайний режим замість збереженого режиму? Які плюси і мінуси використання кожного методу?

Відповіді:


143

Одним із прикладів "негайного режиму" є використання glBeginта glEndперерва glVertexміж ними. Іншим прикладом "безпосереднього режиму" є використання glDrawArraysз клієнтським масивом вершин (тобто не об'єктом буфера вершин).

Зазвичай ви ніколи не захочете використовувати негайний режим (за винятком, можливо, вашої першої програми "hello world"), оскільки вона застаріла і не забезпечує оптимальної продуктивності.

Причина, по якій безпосередній режим не є оптимальним, полягає в тому, що графічна карта пов’язана безпосередньо з потоком вашої програми. Драйвер не може сказати графічному процесору розпочати рендерінг раніше glEnd, оскільки він не знає, коли ви закінчите подачу даних, і йому також потрібно передати ці дані (що він може зробити лише після glEnd).
Подібним чином, з клієнтським масивом вершин драйвер може витягувати копію вашого масиву лише в той момент, коли ви телефонуєте glDrawArrays, і при цьому він повинен блокувати вашу програму. Причина полягає в тому, що в іншому випадку ви можете змінити (або звільнити) пам’ять масиву до того, як драйвер захопить його. Він не може запланувати цю операцію ні раніше, ні пізніше, оскільки знає лише, що дані дійсні точно в один момент часу.

На відміну від цього, якщо ви використовуєте, наприклад, об'єкт буфера вершин, ви заповнюєте буфер даними та передаєте його OpenGL. Ваш процес більше не володіє цими даними і, отже, більше не може їх змінювати. Водій може покладатися на цей факт і може (навіть умоглядно) завантажувати дані, коли автобус вільний.
Будь-які ваші пізніші glDrawArraysабо glDrawElementsдзвінки просто потрапляють у робочу чергу і негайно повертаються (до того, як закінчити!), Тому ваша програма продовжує подавати команди, водночас драйвер працює по черзі. Також їм, швидше за все, не потрібно буде чекати надходження даних, оскільки водій міг це зробити вже набагато раніше.
Таким чином, візуалізація потоку та графічного процесора працює асинхронно, кожен компонент постійно зайнятий, що забезпечує кращу продуктивність.

Перевага безпосереднього режиму дійсно проста у використанні, але знову ж таки використання OpenGL належним чином у застарілому вигляді теж не є точно ракетною наукою - для цього потрібно лише дуже мало додаткової роботи.

Ось типовий код OpenGL "Hello World" у безпосередньому режимі:

Редагувати:
За загальним запитом те саме в збереженому режимі буде виглядати приблизно так:


1
Велике спасибі Деймону, дуже цікаве порівняння. Це дійсно здається мені значно складнішим, але, мабуть, як тільки я правильно зрозумію трубопровід, стане зрозуміліше ...
mallardz

6
@mallardz: Набагато важче взагалі щось робити за допомогою сучасного OpenGL, але насправді стає простіше, коли ви подолаєте початкову перешкоду (і набагато швидше). Негайний режим приємний, оскільки вхідний бар’єр надзвичайно низький. У моєму прикладі все ще відсутні шейдери вершин і фрагментів, які вам також потрібно буде надати (досить основні). Повний запущений приклад того, що насправді компілюється та працює, досить довгий. :-)
Деймон

19

Приклад з підтримкою запуску

Деймон надав ключові частини, але такі новачки, як я, будуть шукати повний приклад, яким можна керувати.

введіть тут опис зображення

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 = 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;
}

Адаптовано з 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
# Immediate
./main.out 1

З цього ми бачимо, як:

При використанні шейдерів:

  • програми шейдерів вершин та фрагментів представлені у вигляді рядків у стилі C, що містять мову GLSL ( vertexShaderSourceі fragmentShaderSource) всередині звичайної програми C, що працює на центральному процесорі

  • ця програма C робить виклики OpenGL, які компілюють ці рядки в код графічного процесора, наприклад:

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

    "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"
        "in vec3 ourColor;\n"
    

    Потім програма C надає масив, що містить положення вершин і кольори від процесора до графічного процесора

        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    

Однак на безпосередньому нешейдерному прикладі ми бачимо, що здійснюються магічні виклики API, які явно дають позиції та кольори:

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

Отже, ми розуміємо, що це представляє набагато більш обмежену модель, оскільки позиції та кольори вже не є довільними визначеними користувачем масивами в пам'яті, а скоріше лише входами до моделі, подібної до Фонгу.

В обох випадках виведений вихід зазвичай надходить прямо на відео, не проходячи назад через центральний процесор, хоча це можливо для читання в центральному процесорі, наприклад, якщо ви хочете зберегти їх у файл: Як використовувати GLUT / OpenGL для візуалізації в файл?

Більшість "сучасних" підручників OpenGL зазвичай зберігають режим і GLFW, багато прикладів ви знайдете тут:


1
У мене був звіт, що якщо ви отримаєте помилку, ERROR::SHADER::VERTEX::COMPILATION_FAILEDви можете виправити це, glfwWindowHintяк показано на: stackoverflow.com/questions/52592309/ ... Однак я не можу відтворити.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
Я щойно вирішив проблему, запустивши команду export MESA_GL_VERSION_OVERRIDE=3.3перед запуском main.out (Debian 8), як вказує одна з відповідей на тому самому дописі, яким ви поділилися.
Ivanzinho
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.