Мінімальний приклад для запуску
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
операції.