Я не могу понять, как используется glOrtho
. Может кто-нибудь объяснить, для чего он используется?
Используется ли он для установки предела диапазона координат xy и z?
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
Это означает, что диапазон x, y и z составляет от -1 до 1?
Ответы:
Взгляните на это изображение: Графические проекции
Команда
glOrtho
создает "наклонную" проекцию, которую вы видите в нижнем ряду. Независимо от того, насколько далеко вершины находятся в направлении z, они не уйдут вдаль.Я использую glOrtho каждый раз, когда мне нужно создавать 2D-графику в OpenGL (например, полосы состояния, меню и т. Д.), Используя следующий код каждый раз, когда изменяется размер окна:
glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0f, windowWidth, windowHeight, 0.0f, 0.0f, 1.0f);
Это приведет к переназначению координат OpenGL в эквивалентные значения пикселей (X от 0 до windowWidth и Y от 0 до windowHeight). Обратите внимание, что я перевернул значения Y, потому что координаты OpenGL начинаются с нижнего левого угла окна. Таким образом, переворачивая, я получаю более обычное (0,0), начинающееся скорее в верхнем левом углу окна.
Обратите внимание, что значения Z обрезаются от 0 до 1. Так что будьте осторожны, когда вы указываете значение Z для положения вашей вершины, оно будет обрезано, если выйдет за пределы этого диапазона. В противном случае, если он находится внутри этого диапазона, он не повлияет на положение, за исключением Z-тестов.
источник
z= -2
. Треугольник был невидим , если я использовалglOrtho(.., 0.0f, -4.0f);
,..-1.0f, -3.0f)
или..-3.0f, -1.0f)
. Чтобы быть видимым, дальний параметр должен быть POSITIVE 2 или больше; казалось, не имело значения, какой был ближайший параметр. Любой из них работали:..0.0f, 2.0f)
,..-1.0f, 2.0f)
,..-3.0f, 2.0f)
, или..0.0f, 1000.0f
.Минимальный запускаемый пример
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 вверх по течению .
Скомпилировать:
gcc -ggdb3 -O0 -o main -std=c99 -Wall -Wextra -pedantic main.c -lGL -lGLU -lglut
Бежать с
glOrtho
:./main 1
Бежать с
glFrustrum
:Проверено на 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; /* 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 вверх по течению .
Выход:
Матрица для
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
операции.источник
Документация OpenGL (выделено жирным шрифтом)
Цифры определяют расположение плоскостей отсечения (левая, правая, нижняя, верхняя, ближняя и дальняя).
«Нормальная» проекция - это перспективная проекция, обеспечивающая иллюзию глубины. Википедия определяет параллельную проекцию как:
источник