glOrtho
: giochi 2D, oggetti vicini e lontani appaiono le stesse dimensioni:
glFrustrum
: più vita reale come 3D, apparire più lontani oggetti identici più piccola:
Codice
#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;
}
Schema
Ortho: camera è un piano, il volume visibile un rettangolo:
a tronco: telecamera è un punto, il volume visibile una fetta di una piramide:
Image source.
Parametri
Siamo sempre alla ricerca da + z alla -z con + y verso l'alto:
glOrtho(left, right, bottom, top, near, far)
left
: minimo x
vediamo
right
: massima x
vediamo
bottom
: minimo um y
vediamo
top
: massimo y
vediamo
-near
: minimo z
vediamo. Sì, questo è -1
volte near
. Quindi un input negativo significa positivo z
.
-far
: massimo z
vediamo. Anche negativo.
schema:
Image source.
Come funziona sotto il cofano
Alla fine, OpenGL sempre "usi":
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
Se usiamo né glOrtho
né glFrustrum
, che è quello che si ottiene.
glOrtho
e glFrustrum
sono trasformazioni appena lineari (alias moltiplicazione matriciale) tale che:
glOrtho
: prende un determinato rettangolo 3D nel cubo di default
glFrustrum
: prende una data sezione piramide nel cubo predefinito
Questa trasformazione viene quindi applicata a tutti i vertici. Questo è ciò che intendo in 2D:
Image source.
Il passo finale dopo trasformazione è semplice:
- rimuovere eventuali punti al di fuori del cubo (abbattimento): solo garantire che
x
, y
e z
sono in [-1, +1]
- ignorare il componente
z
e prendere solo x
e , che ora può essere inserito in uno schermo 2D
Con glOrtho
, z
viene ignorato, così si potrebbe anche utilizzare sempre 0
.
Una ragione si potrebbe desiderare di utilizzare z != 0
è quello di rendere sprite nascondere lo sfondo con il buffer di profondità.
Deprecation
glOrtho
è obsoleto partire OpenGL 4.5: il profilo compatibilità 12.1. "Fixed-FUNZIONE VERTICE trasformazioni" è in rosso.
Quindi non ne fanno uso per la produzione. In ogni caso, comprenderlo è un buon modo per ottenere una visione di OpenGL.
I moderni programmi OpenGL 4 calcolano la matrice di trasformazione (che è piccola) sulla CPU e quindi forniscono la matrice e tutti i punti da trasformare in OpenGL, che può fare le migliaia di moltiplicazioni di matrice per diversi punti molto velocemente in parallelo .
Scritto manualmente vertex shaders quindi fare esplicitamente la moltiplicazione, solitamente con i convenienti tipi di dati vettoriali di OpenGL Shading Language.
Dal momento che si scrive lo shader in modo esplicito, questo consente di ottimizzare l'algoritmo per le vostre esigenze. Tale flessibilità è una caratteristica importante di GPU più moderni, che a differenza di quelli vecchi che hanno fatto un algoritmo fisso con alcuni parametri di input, ora possono fare calcoli arbitrari.Vedi anche: https://stackoverflow.com/a/36211337/895245
Con un esplicito GLfloat transform[]
sarebbe simile a questa:
#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;
}
output generato: http://imgur.com/QVW14Gu
La matrice per glOrtho
è davvero semplice, composta solo di ridimensionamento e traduzione:
scalex, 0, 0, translatex,
0, scaley, 0, translatey,
0, 0, scalez, translatez,
0, 0, 0, 1
come indicato nello OpenGL 2 docs.
Il glFrustum
matrix non è troppo difficile da calcolare a mano, ma inizia a diventare fastidioso. Si noti come tronco non può essere fatta solo con il ridimensionamento e traduzioni come glOrtho
, maggiori informazioni a: https://gamedev.stackexchange.com/a/118848/25171
Il GLM OpenGL C++ libreria matematica è una scelta popolare per il calcolo di tali matrici. http://glm.g-truc.net/0.9.2/api/a00245.html documenta entrambe le operazioni ortho
e frustum
.
[Questo] (https://www.youtube.com/watch?v=8ryJMO6rBT4) video mi ha aiutato molto. – ViniciusArruda