OpenGL에서 "즉시 모드"는 무엇을 의미합니까?


82

"즉시 모드"란 무엇입니까? 코드 예제를 제공하십시오.

유지 모드 대신 즉시 모드를 사용해야하는 경우는 언제입니까? 각 방법의 장단점은 무엇입니까?

답변:


143

"직접 모드"의 한 예는 사용 glBeginglEnd함께 glVertex그들 사이입니다. "직접 모드"의 또 다른 예는 사용하는 glDrawArrays클라이언트 정점 배열 (즉, 함께 하지 정점 버퍼 객체).

일반적으로 즉시 모드 (첫 번째 "hello world"프로그램 제외)를 사용하고 싶지 않을 것입니다. 이는 더 이상 사용되지 않는 기능이고 최적의 성능을 제공하지 않기 때문입니다.

즉시 모드가 최적이 아닌 이유는 그래픽 카드가 프로그램의 흐름과 직접 연결되기 때문입니다. 드라이버는 glEnd데이터 제출이 언제 완료 될지 알 수없고 해당 데이터도 전송해야하기 때문에 GPU에게 이전 에 렌더링을 시작하도록 지시 할 수 없습니다 ( 이후 에만 수행 할 수 있음glEnd ).
마찬가지로 클라이언트 정점 배열을 사용하는 경우 드라이버는를 호출하는 순간에만 배열의 복사본을 가져올 수 glDrawArrays있으며 그렇게하는 동안 애플리케이션을 차단해야합니다. 그 이유는 그렇지 않으면 드라이버가 캡처하기 전에 어레이의 메모리를 수정 (또는 해제) 할 수 있기 때문입니다. 데이터가 한 시점에서 정확히 유효하다는 것만 알고 있기 때문에 이전 또는 이후에 해당 작업을 예약 할 수 없습니다.

반대로, 예를 들어 정점 버퍼 객체를 사용하는 경우 버퍼에 데이터를 채우고 OpenGL에 전달합니다. 프로세스는 더 이상이 데이터를 소유하지 않으므로 더 이상 수정할 수 없습니다. 운전자는이 사실에 의존 할 수 있으며 버스가 비어있을 때마다 데이터를 업로드 할 수 있습니다.
나중에 glDrawArrays또는 glDrawElements호출은 작업 대기열로 이동하고 즉시 (실제로 완료되기 전에!) 반환되므로 프로그램은 명령을 계속 제출하는 동시에 드라이버가 하나씩 작동합니다. 또한 운전자가 이미 훨씬 일찍 수행 할 수 있기 때문에 데이터가 도착할 때까지 기다릴 필요가 없습니다.
따라서 렌더링 스레드와 GPU가 비동기 적으로 실행되고 모든 구성 요소가 항상 사용 중이므로 더 나은 성능을 제공합니다.

즉시 모드는 사용하기 매우 간단하다는 장점이 있지만, 다시 사용되지 않는 방식으로 OpenGL을 올바르게 사용하는 것은 정확히 로켓 과학이 아닙니다. 추가 작업이 거의 필요하지 않습니다.

다음은 즉시 모드의 일반적인 OpenGL "Hello World"코드입니다.

편집 :
일반적인 요청에 따라 유지 모드의 동일한 내용은 다음과 같습니다.


1
데이먼에게 감사합니다. 매우 흥미로운 비교입니다. 그 모습이 훨씬 더 나에게 복잡하지 않습니다,하지만 난 제대로 명확 될 것입니다 파이프 라인 ... 이해하면 같아요
mallardz

6
@mallardz : 최신 OpenGL로 무엇이든하는 것이 훨씬 더 어렵지만 , 사실 초기 장애물을 넘어 서면 훨씬 더 쉽습니다. 즉시 모드는 진입 장벽이 매우 낮기 때문에 좋습니다. 제 예제에는 여전히 제공해야 할 정점 및 조각 셰이더가 없습니다 (아주 기본적인 것). 실제로 컴파일되고 작동하는 완전한 실행 예제는 꽤 깁니다. :-)
Damon

19

실행 가능 유지 예제

Damon 핵심 부품 을 제공 했지만 저와 같은 newbs는 완전한 실행 가능한 예제를 찾고있을 것입니다.

여기에 이미지 설명 입력

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 , my GitHub upstream 에서 수정되었습니다 .

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

그로부터 우리는 방법을 봅니다.

셰이더를 사용하는 경우 :

  • 정점 및 조각 셰이더 프로그램은 CPU에서 실행되는 일반 C 프로그램 내부에 GLSL 언어 ( vertexShaderSourcefragmentShaderSource)를 포함하는 C 스타일 문자열로 표시됩니다.

  • 이 C 프로그램은 해당 문자열을 GPU 코드로 컴파일하는 OpenGL 호출을 만듭니다. 예 :

    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);
    
  • 셰이더는 예상되는 입력을 정의하고 C 프로그램은 GPU 코드에 대한 메모리 포인터를 통해 입력을 제공합니다. 예를 들어 프래그먼트 셰이더는 예상되는 입력을 정점 위치 및 색상의 배열로 정의합니다.

    "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 프로그램은 CPU에서 GPU 로의 정점 위치와 색상을 포함하는 배열을 제공합니다.

        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);

따라서 위치와 색상이 더 이상 메모리에서 임의의 사용자 정의 배열이 아니라 Phong과 같은 모델에 대한 입력이기 때문에 이것이 훨씬 더 제한된 모델을 나타냄을 이해합니다.

두 경우 모두, 렌더링 된 출력은 일반적으로 CPU를 통해 다시 전달되지 않고 비디오로 곧바로 전달되지만, 예를 들어 파일에 저장하려는 경우 CPU로 읽을 수 있습니다. GLUT / OpenGL을 사용하여 렌더링하는 방법 파일?

대부분의 "최신"OpenGL 자습서는 일반적으로 모드와 GLFW를 유지하며 다음에서 많은 예제를 찾을 수 있습니다.


1
오류가 발생 ERROR::SHADER::VERTEX::COMPILATION_FAILED하면 stackoverflow.com/questions/52592309/…에glfwWindowHint 표시된대로 오류 를 수정할 수 있다는보고를 받았습니다. 그러나 재현 할 수 없습니다.
치로 틸리는郝海东冠状病六四事件法轮功

1
답변 중 하나가 공유 한 동일한 게시물에 표시된 export MESA_GL_VERSION_OVERRIDE=3.3대로 main.out (Debian 8)을 실행하기 전에 명령을 실행하여 문제를 해결 했습니다.
Ivanzinho
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.