glOrtho
: los juegos en 2D, los objetos de cerca y de lejos parecen del mismo tamaño:
glFrustrum
: más de la vida real como en 3D, objetos idénticos alejar los más pequeños:
Código
#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;
}
esquema
Ortho: cámara es un plano, el volumen visible un rectángulo:
frustrum: cámara es un punto, el volumen visible una rebanada de una pirámide:
Image source.
Parámetros
Estamos siempre en busca de + z para -Z con + y hacia arriba:
glOrtho(left, right, bottom, top, near, far)
left
: mínimo x
vemos
right
: máximo x
vemos
bottom
: minim um y
vemos
top
: máximo y
vemos
-near
: mínimo z
vemos. Sí, esto es -1
veces near
. Entonces, una entrada negativa significa positivo z
.
-far
: máximo z
que vemos. También negativo.
esquema:
Image source.
Cómo funciona bajo el capó
Al final, OpenGL siempre "usos":
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
Si utilizamos ni glOrtho
ni glFrustrum
, eso es lo que tenemos.
glOrtho
y glFrustrum
son transformaciones simplemente lineales (También conocido como matriz de multiplicación) tal que:
glOrtho
: toma un rectángulo 3D dado en el cubo por defecto
glFrustrum
: toma una sección pirámide dado en el cubo por defecto
Esta transformación se aplica a todos los vértices. Esto es lo que quiero decir en 2D:
Image source.
El paso final después de la transformación es simple:
- eliminar cualquier puntos fuera del cubo (sacrificio): simplemente asegurar que
x
, y
y z
son en [-1, +1]
- ignoran el componente
z
y tome solamente x
y y
, que ahora se puede poner en una pantalla 2D
Con 0.123., z
se ignora, por lo que también podría utilizar siempre 0
.
Una razón por la que es posible que desee utilizar z != 0
es hacer que los sprites oculten el fondo con el búfer de profundidad.
Deprecation
glOrtho
está obsoleta de OpenGL 4.5: el perfil de compatibilidad 12,1. "TRANSFORMACIONES DE VERTEX DE FUNCIÓN FIJA" está en rojo.
Así que no lo use para la producción. En cualquier caso, entender que es una buena forma de obtener algo de información de OpenGL.
Los programas modernos OpenGL 4 calculan la matriz de transformación (que es pequeña) en la CPU y luego transfieren la matriz y todos los puntos a OpenGL, que pueden hacer miles de multiplicaciones de matriz para diferentes puntos realmente rápidos en paralelo .
Manualmente escrito vertex shaders luego haga la multiplicación explícitamente, generalmente con los tipos de datos vectoriales convenientes del Lenguaje de Sombreado OpenGL.
Dado que escribe el sombreador explícitamente, esto le permite ajustar el algoritmo a sus necesidades. Dicha flexibilidad es una característica principal de las GPU más modernas, que a diferencia de las antiguas que realizaban un algoritmo fijo con algunos parámetros de entrada, ahora pueden realizar cálculos arbitrarios.Ver también: https://stackoverflow.com/a/36211337/895245
Con una explícita GLfloat transform[]
se vería algo como esto:
#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;
}
salida generada: http://imgur.com/QVW14Gu
La matriz de glOrtho
es muy simple, compuesta sólo por la ampliación y la traducción:
scalex, 0, 0, translatex,
0, scaley, 0, translatey,
0, 0, scalez, translatez,
0, 0, 0, 1
como se menciona en el OpenGL 2 docs.
El glFrustum
matrix tampoco es demasiado difícil de calcular a mano, pero comienza a ser molesto. Tenga en cuenta que el triturado no se puede componer con solo escalas y traducciones como glOrtho
, más información en: https://gamedev.stackexchange.com/a/118848/25171
La biblioteca matemática GLM OpenGL C++ es una opción popular para calcular tales matrices. http://glm.g-truc.net/0.9.2/api/a00245.html documenta las operaciones ortho
y frustum
.
[Este] (https://www.youtube.com/watch?v=8ryJMO6rBT4) video me ayudó mucho. – ViniciusArruda