El glOrthocomando produce una proyección "oblicua" que se ve en la fila inferior. No importa qué tan lejos estén los vértices en la dirección z, no retrocederán en la distancia.
Yo uso glOrtho cada vez que necesito hacer gráficos 2D en OpenGL (como barras de salud, menús, etc.) usando el siguiente código cada vez que se cambia el tamaño de la ventana:
Esto reasignará las coordenadas OpenGL en los valores de píxeles equivalentes (X va de 0 a windowWidth e Y va de 0 a windowHeight). Tenga en cuenta que cambié los valores de Y porque las coordenadas de OpenGL comienzan en la esquina inferior izquierda de la ventana. Entonces, al voltear, obtengo un (0,0) más convencional que comienza en la esquina superior izquierda de la ventana.
Tenga en cuenta que los valores de Z se recortan de 0 a 1. Así que tenga cuidado cuando especifique un valor de Z para la posición de su vértice, se recortará si cae fuera de ese rango. De lo contrario, si está dentro de ese rango, parecerá que no tiene ningún efecto en la posición, excepto en las pruebas Z.
oh dios mio TE AMO. ¿Tiene alguna idea de cuánto tiempo lleva encontrar / descifrar esta única línea de código en línea? Gracias,
nombraré a
2
Nota: (en Android) incluso si el modelo solo tiene valores z negativos, parece ser necesario tener un valor positivo para el parámetro final (lejos). Hice una prueba de triángulo simple (con la selección deshabilitada), con vértices en z= -2. El triángulo era invisible si utilizaba glOrtho(.., 0.0f, -4.0f);, ..-1.0f, -3.0f)o ..-3.0f, -1.0f). Para ser visible, el parámetro lejano tenía que ser POSITIVO 2 o mayor; no parecía importar cuál era el parámetro cercano. Cualquiera de ellos trabajaron: ..0.0f, 2.0f), ..-1.0f, 2.0f), ..-3.0f, 2.0f), o ..0.0f, 1000.0f.
ToolmakerSteve
9
Es ridículo la cantidad de malos tutoriales sobre OpenGl que hay.
@mgouin El rango z especifica dónde están su plano Z-cerca y su plano Z-lejos. Cuando dibuja su geometría, sus valores Z deben estar dentro de los dos planos Z. Si caen fuera de los planos Z, su geometría no se renderizará. Además, su renderizador solo tiene una determinada resolución de profundidad. Si tiene su plano lejano configurado a 1000 unidades de distancia e intenta dibujar un modelo pequeño con caritas a 0.1 unidades de distancia entre sí, OpenGL no podrá brindarle la resolución de profundidad que necesita y obtendrá Z-fight (parpadeo) entre las caras.
Mikepote
54
Ejemplo mínimo ejecutable
glOrtho: Juegos en 2D, los objetos cercanos y lejanos parecen del mismo tamaño:
glFrustrum: más real como 3D, los objetos idénticos más alejados parecen más pequeños:
C Principal
#include<stdlib.h>#include<GL/gl.h>#include<GL/glu.h>#include<GL/glut.h>staticint ortho = 0;
staticvoiddisplay(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();
}
staticvoidreshape(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);
}
intmain(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;
}
El paso final después de la transformación es simple:
eliminar cualquier punto fuera del cubo (selección): solo asegúrese de que x, yy zestén en[-1, +1]
ignorar el zcomponente y tomar solo xy y, que ahora se puede poner en una pantalla 2D
Con glOrtho, zse ignora, por lo que es mejor usar siempre 0.
Una razón por la que podría querer usar z != 0es hacer que los sprites oculten el fondo con el búfer de profundidad.
Deprecación
glOrthoestá obsoleto a partir de OpenGL 4.5 : el perfil de compatibilidad 12.1. "TRANSFORMACIONES DE VÉRTEX DE FUNCIÓN FIJA" está en rojo.
Así que no lo use para la producción. En cualquier caso, comprenderlo es una buena forma de obtener información sobre OpenGL.
Los programas modernos de OpenGL 4 calculan la matriz de transformación (que es pequeña) en la CPU, y luego dan la matriz y todos los puntos para ser transformados a OpenGL, que puede hacer miles de multiplicaciones de matrices para diferentes puntos muy rápido en paralelo.
Los sombreadores de vértices escritos manualmente luego hacen 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. Tal flexibilidad es una característica importante de las GPU más modernas, que a diferencia de las antiguas que tenían un algoritmo fijo con algunos parámetros de entrada, ahora pueden realizar cálculos arbitrarios. Véase también: https://stackoverflow.com/a/36211337/895245
Con un explícito GLfloat transform[], se vería así:
La glFrustummatriz tampoco es demasiado difícil de calcular a mano, pero empieza a resultar molesta. Tenga en cuenta que el frustum no se puede crear solo con 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 dichas matrices. http://glm.g-truc.net/0.9.2/api/a00245.html documenta las operaciones orthoy frustum.
"¿Qué debería usarse en su lugar?" - construya sus propias matrices y asígnelas directamente.
Kromster
4
glOrtho describe una transformación que produce una proyección paralela . La matriz actual (ver glMatrixMode) se multiplica por esta matriz y el resultado reemplaza la matriz actual, como si glMultMatrix se llamara con la siguiente matriz como argumento:
Los números definen las ubicaciones de los planos de recorte (izquierda, derecha, abajo, arriba, cerca y lejos).
La proyección "normal" es una proyección en perspectiva que proporciona la ilusión de profundidad. Wikipedia define una proyección paralela como:
Las proyecciones paralelas tienen líneas de proyección que son paralelas tanto en la realidad como en el plano de proyección.
La proyección paralela corresponde a una proyección en perspectiva con un punto de vista hipotético, por ejemplo, una en la que la cámara se encuentra a una distancia infinita del objeto y tiene una distancia focal infinita, o "zoom".
Hola, gracias por la información. No pude entender la diferencia entre proyección paralela y proyección en perspectiva. Busqué un poco en Google
ufk
6
Desafortunadamente, la información que obtuvo de answers.com no tiene ningún valor. Una vista isométrica, por ejemplo, es muy tridimensional, pero es una proyección paralela sin perspectiva. Vea aquí, y también hay enlaces a muchos otros ejemplos de proyecciones: en.wikipedia.org/wiki/Isometric_projection
Respuestas:
Eche un vistazo a esta imagen: Proyecciones gráficas
El
glOrtho
comando produce una proyección "oblicua" que se ve en la fila inferior. No importa qué tan lejos estén los vértices en la dirección z, no retrocederán en la distancia.Yo uso glOrtho cada vez que necesito hacer gráficos 2D en OpenGL (como barras de salud, menús, etc.) usando el siguiente código cada vez que se cambia el tamaño de la ventana:
glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0f, windowWidth, windowHeight, 0.0f, 0.0f, 1.0f);
Esto reasignará las coordenadas OpenGL en los valores de píxeles equivalentes (X va de 0 a windowWidth e Y va de 0 a windowHeight). Tenga en cuenta que cambié los valores de Y porque las coordenadas de OpenGL comienzan en la esquina inferior izquierda de la ventana. Entonces, al voltear, obtengo un (0,0) más convencional que comienza en la esquina superior izquierda de la ventana.
Tenga en cuenta que los valores de Z se recortan de 0 a 1. Así que tenga cuidado cuando especifique un valor de Z para la posición de su vértice, se recortará si cae fuera de ese rango. De lo contrario, si está dentro de ese rango, parecerá que no tiene ningún efecto en la posición, excepto en las pruebas Z.
fuente
z= -2
. El triángulo era invisible si utilizabaglOrtho(.., 0.0f, -4.0f);
,..-1.0f, -3.0f)
o..-3.0f, -1.0f)
. Para ser visible, el parámetro lejano tenía que ser POSITIVO 2 o mayor; no parecía importar cuál era el parámetro cercano. Cualquiera de ellos trabajaron:..0.0f, 2.0f)
,..-1.0f, 2.0f)
,..-3.0f, 2.0f)
, o..0.0f, 1000.0f
.Ejemplo mínimo ejecutable
glOrtho
: Juegos en 2D, los objetos cercanos y lejanos parecen del mismo tamaño:glFrustrum
: más real como 3D, los objetos idénticos más alejados parecen más pequeños:C Principal
#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 aguas arriba .
Compilar:
gcc -ggdb3 -O0 -o main -std=c99 -Wall -Wextra -pedantic main.c -lGL -lGLU -lglut
Ejecutar con
glOrtho
:./main 1
Ejecutar con
glFrustrum
:Probado en Ubuntu 18.10.
Esquema
Ortho: la cámara es un plano, el volumen visible un rectángulo:
Frustrum: la cámara es un punto, el volumen visible es una porción de una pirámide:
Fuente de la imagen .
Parámetros
Siempre estamos mirando de + z a -z con + y hacia arriba:
glOrtho(left, right, bottom, top, near, far)
left
: mínimox
que vemosright
: máximox
vemosbottom
: mínimoy
que vemostop
: máximoy
vemos-near
: mínimoz
que vemos. Sí , esto es-1
tiemponear
. Entonces, una entrada negativa significa positivaz
.-far
: máximoz
que vemos. También negativo.Esquema:
Fuente de la imagen .
Cómo funciona bajo el capó
Al final, OpenGL siempre "usa":
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
Si no usamos
glOrtho
niglFrustrum
, eso es lo que obtenemos.glOrtho
yglFrustrum
son solo transformaciones lineales (también conocido como multiplicación de matrices) tales que:glOrtho
: toma un rectángulo 3D dado en el cubo predeterminadoglFrustrum
: lleva una sección de pirámide determinada al cubo predeterminadoEsta transformación luego se aplica a todos los vértices. Esto es lo que quiero decir en 2D:
Fuente de la imagen .
El paso final después de la transformación es simple:
x
,y
yz
estén en[-1, +1]
z
componente y tomar solox
yy
, que ahora se puede poner en una pantalla 2DCon
glOrtho
,z
se ignora, por lo que es mejor usar siempre0
.Una razón por la que podría querer usar
z != 0
es hacer que los sprites oculten el fondo con el búfer de profundidad.Deprecación
glOrtho
está obsoleto a partir de OpenGL 4.5 : el perfil de compatibilidad 12.1. "TRANSFORMACIONES DE VÉRTEX DE FUNCIÓN FIJA" está en rojo.Así que no lo use para la producción. En cualquier caso, comprenderlo es una buena forma de obtener información sobre OpenGL.
Los programas modernos de OpenGL 4 calculan la matriz de transformación (que es pequeña) en la CPU, y luego dan la matriz y todos los puntos para ser transformados a OpenGL, que puede hacer miles de multiplicaciones de matrices para diferentes puntos muy rápido en paralelo.
Los sombreadores de vértices escritos manualmente luego hacen 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. Tal flexibilidad es una característica importante de las GPU más modernas, que a diferencia de las antiguas que tenían un algoritmo fijo con algunos parámetros de entrada, ahora pueden realizar cálculos arbitrarios. Véase también: https://stackoverflow.com/a/36211337/895245
Con un explícito
GLfloat transform[]
, se vería así:#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 aguas arriba .
Salida:
La matriz de
glOrtho
es realmente simple, compuesta solo de escalado y traducción:scalex, 0, 0, translatex, 0, scaley, 0, translatey, 0, 0, scalez, translatez, 0, 0, 0, 1
como se menciona en los documentos de OpenGL 2 .
La
glFrustum
matriz tampoco es demasiado difícil de calcular a mano, pero empieza a resultar molesta. Tenga en cuenta que el frustum no se puede crear solo con escalas y traducciones comoglOrtho
, más información en: https://gamedev.stackexchange.com/a/118848/25171La biblioteca matemática GLM OpenGL C ++ es una opción popular para calcular dichas matrices. http://glm.g-truc.net/0.9.2/api/a00245.html documenta las operaciones
ortho
yfrustum
.fuente
Documentación de OpenGL (mi negrita)
Los números definen las ubicaciones de los planos de recorte (izquierda, derecha, abajo, arriba, cerca y lejos).
La proyección "normal" es una proyección en perspectiva que proporciona la ilusión de profundidad. Wikipedia define una proyección paralela como:
fuente