Как использовать glOrtho () в OpenGL?

87

Я не могу понять, как используется glOrtho. Может кто-нибудь объяснить, для чего он используется?

Используется ли он для установки предела диапазона координат xy и z?

glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);

Это означает, что диапазон x, y и z составляет от -1 до 1?

ufk
источник
1
Это видео мне очень помогло.
ViniciusArruda

Ответы:

153

Взгляните на это изображение: Графические проекции введите описание изображения здесь

Команда 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-тестов.

Mikepote
источник
90
Боже мой, я ЛЮБЛЮ ТЕБЯ. Вы хоть представляете, сколько времени нужно, чтобы найти / разгадать эту единственную строчку кода в Интернете? Спасибо, за это я
назову
2
Примечание: (в Android) даже если модель имеет только отрицательные значения 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.
ToolmakerSteve,
9
Просто смешно, сколько плохих руководств по OpenGl существует.
basickarl
1
@Kari, надеюсь, эта ссылка может помочь. > Learnopengl.com/#!In-Practice/2D-Game/Rendering-Sprites
huahsin68 05
1
@mgouin Диапазон z указывает, где находятся ваша ближняя Z-плоскость и дальняя Z-плоскость. Когда вы рисуете свою геометрию, значения Z должны находиться внутри двух Z-плоскостей. Если они выходят за пределы плоскости Z, ваша геометрия не будет отображаться. Также ваш рендерер имеет только определенное разрешение по глубине. Если у вас дальняя плоскость установлена ​​на расстоянии 1000 единиц, и вы пытаетесь нарисовать крошечную модель с маленькими лицами на расстоянии 0,1 единицы друг от друга, OpenGL не сможет дать вам необходимое разрешение глубины, и вы получите Z-бой (мерцание) между лицами.
Mikepote
54

Минимальный запускаемый пример

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:

./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;
/* 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операции.

Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
1
"что следует использовать вместо этого?" - создавать свои собственные матрицы и назначать их напрямую.
Kromster
4

glOrtho описывает преобразование, которое производит параллельную проекцию. Текущая матрица (см. GlMatrixMode) умножается на эту матрицу, и результат заменяет текущую матрицу, как если бы glMultMatrix был вызван со следующей матрицей в качестве аргумента:

Документация OpenGL (выделено жирным шрифтом)

Цифры определяют расположение плоскостей отсечения (левая, правая, нижняя, верхняя, ближняя и дальняя).

«Нормальная» проекция - это перспективная проекция, обеспечивающая иллюзию глубины. Википедия определяет параллельную проекцию как:

Параллельные проекции имеют линии проекции, параллельные как в реальности, так и в плоскости проекции.

Параллельная проекция соответствует перспективной проекции с гипотетической точкой обзора - например, такой, в которой камера находится на бесконечном расстоянии от объекта и имеет бесконечное фокусное расстояние, или «масштабирование».

ChrisF
источник
Привет, спасибо за информацию. я не мог понять разницу между параллельной и перспективной проекцией. Я немного погуглил
ufk 03
6
К сожалению, информация, которую вы получили с сайта answers.com, бесполезна. Изометрический вид, например, очень трехмерен, но это параллельная проекция без перспективы. Смотрите здесь, а также есть ссылки на множество других примеров проекций: en.wikipedia.org/wiki/Isometric_projection
Бен Фойгт,