Пример минимального запуска
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 {
/* This only rotates and translates the world around to look like the camera moved. */
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 upstream .
Компиляция:
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 вверх:
* * 1068
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. «Преобразования с фиксированной функцией VERTEX» выделены красным.
Так что не используйте его для производства. В любом случае, понимание этого - хороший способ получить представление об OpenGL.
Современные программы OpenGL 4 вычисляют матрицу преобразования (которая является небольшой) на ЦП, а затем дают матрицу и все точки, которые нужно преобразовать в OpenGL, который может выполнять тысячи умножений матриц для разных точек очень быстро параллельно.
Вручную написанные вершинные шейдеры затем выполняют умножение в явном виде, обычно с удобными типами векторных данных OpenGL Shading Language.
Поскольку вы пишете шейдер явно, это позволяет вам настроить алгоритм в соответствии с вашими потребностями. Такая гибкость является главной особенностью более современных графических процессоров, которые в отличие от старых, которые использовали фиксированный алгоритм с некоторыми входными параметрами, теперь могут выполнять произвольные вычисления. Смотри также: 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;
/* ourColor is passed on to the fragment shader. */
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[] = {
/* 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(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);
/* 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);
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shader_program);
transform_location = glGetUniformLocation(shader_program, "transform");
/* THIS is just a dummy 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 upstream .
Выход:
Матрица для 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
.