“즉시 모드”란 무엇입니까? 코드 예제를 제공하십시오.
유지 모드 대신 즉시 모드를 사용해야하는 경우는 언제입니까? 각 방법의 장단점은 무엇입니까?
답변
“직접 모드”의 한 예는 사용 glBegin
과 glEnd
함께 glVertex
그들 사이입니다. “직접 모드”의 또 다른 예는 사용하는 glDrawArrays
클라이언트 정점 배열 (즉, 함께 하지 정점 버퍼 객체).
일반적으로 즉시 모드 (첫 번째 “hello world”프로그램 제외)를 사용하고 싶지 않을 것입니다. 이는 더 이상 사용되지 않는 기능이고 최적의 성능을 제공하지 않기 때문입니다.
즉시 모드가 최적이 아닌 이유는 그래픽 카드가 프로그램의 흐름과 직접 연결되기 때문입니다. 드라이버는 glEnd
데이터 제출이 언제 완료 될지 알 수없고 해당 데이터도 전송해야하기 때문에 GPU에게 이전 에 렌더링을 시작하도록 지시 할 수 없습니다 ( 이후 에만 수행 할 수 있음glEnd
).
마찬가지로 클라이언트 정점 배열을 사용하는 경우 드라이버는를 호출하는 순간에만 배열의 복사본을 가져올 수 glDrawArrays
있으며 그렇게하는 동안 애플리케이션을 차단해야합니다. 그 이유는 그렇지 않으면 드라이버가 캡처하기 전에 어레이의 메모리를 수정 (또는 해제) 할 수 있기 때문입니다. 데이터가 한 시점에서 정확히 유효하다는 것만 알고 있기 때문에 이전 또는 이후에 해당 작업을 예약 할 수 없습니다.
반대로, 예를 들어 정점 버퍼 객체를 사용하는 경우 버퍼에 데이터를 채우고 OpenGL에 전달합니다. 프로세스는 더 이상이 데이터를 소유하지 않으므로 더 이상 수정할 수 없습니다. 운전자는이 사실에 의존 할 수 있으며 버스가 비어있을 때마다 데이터를 업로드 할 수 있습니다.
나중에 glDrawArrays
또는 glDrawElements
호출은 작업 대기열로 이동하고 즉시 (실제로 완료되기 전에!) 반환되므로 프로그램은 명령을 계속 제출하는 동시에 드라이버가 하나씩 작동합니다. 또한 운전자가 이미 훨씬 일찍 수행 할 수 있기 때문에 데이터가 도착할 때까지 기다릴 필요가 없습니다.
따라서 렌더링 스레드와 GPU가 비동기 적으로 실행되고 모든 구성 요소가 항상 사용 중이므로 더 나은 성능을 제공합니다.
즉시 모드는 사용하기 매우 간단하다는 장점이 있지만, 다시 사용되지 않는 방식으로 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 = {...};
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);
답변
실행 가능 유지 예제
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 언어 (
vertexShaderSource
및fragmentShaderSource
)를 포함하는 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를 유지하며 다음에서 많은 예제를 찾을 수 있습니다.