Минимальный запускаемый пример
glOrtho: 2D-игры, объекты близко и далеко выглядят одинакового размера:

glFrustrum: более реалистично, как в 3D, идентичные объекты, расположенные дальше, кажутся меньше:

main.c
#include <stdlib.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
static int ortho = 0;
static void display(void) {
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
if (ortho) {
} else {
gluLookAt(0.0, 0.0, -3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}
glColor3f(1.0f, 1.0f, 1.0f);
glutWireCube(2);
glFlush();
}
static void reshape(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (ortho) {
glOrtho(-2.0, 2.0, -2.0, 2.0, -1.5, 1.5);
} else {
glFrustum(-1.0, 1.0, -1.0, 1.0, 1.5, 20.0);
}
glMatrixMode(GL_MODELVIEW);
}
int main(int argc, char** argv) {
glutInit(&argc, argv);
if (argc > 1) {
ortho = 1;
}
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow(argv[0]);
glClearColor(0.0, 0.0, 0.0, 0.0);
glShadeModel(GL_FLAT);
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMainLoop();
return EXIT_SUCCESS;
}
GitHub вверх по течению .
Скомпилировать:
gcc -ggdb3 -O0 -o main -std=c99 -Wall -Wextra -pedantic main.c -lGL -lGLU -lglut
Бежать с glOrtho:
./main 1
Бежать с glFrustrum:
./main
Проверено на Ubuntu 18.10.
Схема
Орто: камера - плоскость, видимый объем - прямоугольник:

Frustrum: камера - точка, видимый объем - срез пирамиды:

Источник изображения .
Параметры
Мы всегда смотрим от + z к -z с + y вверх:
glOrtho(left, right, bottom, top, near, far)
left: минимум xвидим
right: максимум xвидим
bottom: минимум yвидим
top: максимум yвидим
-near: минимум zмы видим. Да , это -1время near. Итак, отрицательный вход означает положительный z.
-far: максимум zмы видим. Тоже отрицательный.
Схема:

Источник изображения .
Как это работает под капотом
В конце концов, OpenGL всегда «использует»:
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
Если мы не используем ни glOrthoили glFrustrum, то вот что мы получим.
glOrthoи glFrustrumпредставляют собой просто линейные преобразования (умножение матриц AKA), такие что:
glOrtho: переносит данный трехмерный прямоугольник в куб по умолчанию
glFrustrum: переносит данную секцию пирамиды в куб по умолчанию
Затем это преобразование применяется ко всем вершинам. Вот что я имею в виду в 2D:

Источник изображения .
Последний шаг после преобразования прост:
- удалить все точки за пределами куба (выбраковки): просто убедитесь , что
x, yи zв[-1, +1]
- игнорировать
zкомпонент и брать только xи y, которые теперь можно поместить на 2D-экран
С glOrtho, zигнорируется, так что вы можете также использовать всегда 0.
Одна из причин, по которой вы можете захотеть использовать, z != 0- заставить спрайты скрывать фон с помощью буфера глубины.
Устаревание
glOrthoустарела с OpenGL 4.5 : профиль совместимости 12.1. «ФИКСИРОВАННЫЕ ПРЕОБРАЗОВАНИЯ ВЕРТЕКСА» выделены красным.
Так что не используйте его для производства. В любом случае понимание этого - хороший способ получить некоторое представление об OpenGL.
Современные программы OpenGL 4 вычисляют матрицу преобразования (которая мала) на ЦП, а затем передают матрицу и все точки для преобразования в OpenGL, который может очень быстро параллельно выполнять тысячи матричных умножений для разных точек.
Написанные вручную вершинные шейдеры затем явно выполняют умножение, обычно с удобными векторными типами данных языка шейдеров OpenGL.
Поскольку вы пишете шейдер явно, это позволяет вам настроить алгоритм в соответствии с вашими потребностями. Такая гибкость - основная особенность более современных графических процессоров, которые, в отличие от старых, которые выполняли фиксированный алгоритм с некоторыми входными параметрами, теперь могут выполнять произвольные вычисления. См. Также: https://stackoverflow.com/a/36211337/895245
В явном виде GLfloat transform[]это будет выглядеть примерно так:
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "common.h"
static const GLuint WIDTH = 800;
static const GLuint HEIGHT = 600;
static const GLchar* vertex_shader_source =
"#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"layout (location = 1) in vec3 color;\n"
"out vec3 ourColor;\n"
"uniform mat4 transform;\n"
"void main() {\n"
" gl_Position = transform * vec4(position, 1.0f);\n"
" ourColor = color;\n"
"}\n";
static const GLchar* fragment_shader_source =
"#version 330 core\n"
"in vec3 ourColor;\n"
"out vec4 color;\n"
"void main() {\n"
" color = vec4(ourColor, 1.0f);\n"
"}\n";
static GLfloat vertices[] = {
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(void) {
GLint shader_program;
GLint transform_location;
GLuint vbo;
GLuint vao;
GLFWwindow* window;
double time;
glfwInit();
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);
shader_program = common_get_shader_program(vertex_shader_source, fragment_shader_source);
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, 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);
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shader_program);
transform_location = glGetUniformLocation(shader_program, "transform");
GLfloat transform[] = {
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
};
time = glfwGetTime();
transform[0] = 2.0f * sin(time);
transform[5] = 2.0f * cos(time);
glUniformMatrix4fv(transform_location, 1, GL_FALSE, transform);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
glfwSwapBuffers(window);
}
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glfwTerminate();
return EXIT_SUCCESS;
}
GitHub вверх по течению .
Выход:

Матрица для glOrthoдействительно проста, состоит только из масштабирования и перевода:
scalex, 0, 0, translatex,
0, scaley, 0, translatey,
0, 0, scalez, translatez,
0, 0, 0, 1
как указано в документации OpenGL 2 .
glFrustumМатрица не слишком сложно , чтобы вычислить вручную либо, но начинает получать раздражает. Обратите внимание, что усеченная пирамида не может быть создана только с помощью масштабирования и переводов, например glOrtho, более подробная информация по адресу: https://gamedev.stackexchange.com/a/118848/25171
Математическая библиотека GLM OpenGL C ++ - популярный выбор для вычисления таких матриц. http://glm.g-truc.net/0.9.2/api/a00245.html документирует операции orthoи frustumоперации.