Як використовувати glOrtho () у OpenGL?


87

Я не можу зрозуміти використання glOrtho. Хтось може пояснити, для чого він використовується?

Чи використовується він для встановлення діапазону границь координат xy та z?

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

Це означає, що діапазон x, y та z становить від -1 до 1?


1
Це відео мені дуже допомогло.
ViniciusArruda

Відповіді:


153

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

glOrthoКоманда виробляє «Oblique» виступ , який ви бачите в нижньому рядку. Незалежно від того, наскільки віддалені вершини знаходяться в напрямку 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.


90
о боже, Я КОХАЮ ТЕБЕ. Ви уявляєте, скільки часу потрібно, щоб знайти / зрозуміти цей рядок коду в Інтернеті? Дякую, за це я назву свою первістку
карпатію

2
Примітка: (на Android), навіть якщо модель має лише від’ємні значення z, здається, потрібно мати позитивне значення для остаточного (далекого) параметра. Я зробив простий тест трикутника (з відключеним вибракуванням), з вершинами на z= -2. Трикутник був невидимий , якщо я використовував glOrtho(.., 0.0f, -4.0f);, ..-1.0f, -3.0f)або ..-3.0f, -1.0f). Щоб бути видимим, далекий параметр повинен бути ПОЗИТИВНИМ 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

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.

Схема

Орто: камера - це площина, видимий обсяг - прямокутник:

введіть тут опис зображення

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

введіть тут опис зображення

Джерело зображення .

Параметри

Ми завжди шукаємо від + 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;
/* 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 вгору за течією .

Вихід:

введіть тут опис зображення

Матриця 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операції.


1
"що слід використовувати замість цього?" - побудувати власні матриці та призначити їх безпосередньо.
Kromster

4

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

Документація OpenGL (мій жирний шрифт)

Цифри визначають розташування площин відсікання (ліворуч, праворуч, знизу, зверху, поблизу та далеко).

"Нормальна" проекція - це перспективна проекція, яка створює ілюзію глибини. Вікіпедія визначає паралельну проекцію як:

Паралельні проекції мають лінії проекції, паралельні як у дійсності, так і в площині проекції.

Паралельна проекція відповідає проекції в перспективі з гіпотетичною точкою зору - наприклад, такою, де камера знаходиться на нескінченній відстані від об'єкта і має нескінченну фокусну відстань, або "масштабування".


привіт дякую за інформацію. я не міг повністю зрозуміти різницю між паралельною та перспективною проекцією. я трохи погуглив
ufk

6
На жаль, інформація, яку ви отримали на сайті answer.com, досить нікчемна. Наприклад, ізометричний вигляд є дуже тривимірним, проте це паралельна проекція без перспективи. Дивіться тут, а також є посилання на багато інших прикладів прогнозів: en.wikipedia.org/wiki/Isometric_projection
Бен Войгт,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.