Мінімальний приклад для запуску
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.
Схема
Орто: камера - це площина, видимий обсяг - прямокутник:

Фруструм: камера - це точка, видимий об’єм зріз піраміди:

Джерело зображення .
Параметри
Ми завжди шукаємо від + 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є просто лінійними перетвореннями (множення матриці АКА) такими, що:
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 вгору за течією .
Вихід:

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