Je ne comprends pas l'utilisation de glOrtho
. Quelqu'un peut-il expliquer à quoi il sert?
Est-il utilisé pour définir la plage des coordonnées x y et z?
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
Cela signifie que les plages x, y et z vont de -1 à 1?
Regardez cette image: Projections Graphiques
La commande glOrtho
produit une projection "oblique" que vous voyez dans la ligne du bas. Peu importe la distance qui sépare les vertex dans la direction z, ils ne reculeront pas dans la distance.
J'utilise glOrtho chaque fois que je dois créer des graphiques 2D sous OpenGL (barres de santé, menus, etc.) à l'aide du code suivant chaque fois que la fenêtre est redimensionnée:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0f, windowWidth, windowHeight, 0.0f, 0.0f, 1.0f);
Cela remappera les coordonnées OpenGL en valeurs de pixels équivalentes (X allant de 0 à windowWidth et Y allant de 0 à windowHeight). Notez que j'ai inversé les valeurs Y car les coordonnées OpenGL partent du coin inférieur gauche de la fenêtre. Donc, en retournant, je reçois un plus conventionnel (0,0) en partant du coin en haut à gauche de la fenêtre.
Notez que les valeurs Z sont coupées de 0 à 1. Soyez donc prudent lorsque vous spécifiez une valeur Z pour la position de votre sommet, elle sera coupée si elle se situe en dehors de cette plage. Sinon, si elle se situe à l'intérieur de cette plage, cela semblera n'avoir aucun effet sur la position, à l'exception des tests Z.
Exemple minimal exécutable
glOrtho
: les jeux 2D, les objets proches et lointains apparaissent de la même taille:
glFrustrum
: plus réel que la 3D, les objets identiques plus éloignés paraissent plus petits:
principal 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;
}
Compiler:
gcc -ggdb3 -O0 -o main -std=c99 -Wall -Wextra -pedantic main.c -lGL -lGLU -lglut
Courir avec glOrtho
:
./main 1
Courir avec glFrustrum
:
./main
Testé sur Ubuntu 18.10.
Schéma
Ortho: la caméra est un plan, le volume visible un rectangle:
Frustrum: la caméra est un point, le volume visible une tranche de pyramide:
Paramètres
Nous cherchons toujours de + z à -z avec + y vers le haut:
glOrtho(left, right, bottom, top, near, far)
left
: minimum x
on voitright
: maximum x
on voitbottom
: minimum y
on voittop
: maximum y
on voit-near
: minimum z
on voit. Oui, c'est -1
fois near
. Donc, une entrée négative signifie positive z
.-far
: maximum z
on voit. Aussi négatif.Schéma:
Comment ça marche sous le capot
En fin de compte, OpenGL "utilise" toujours:
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
Si nous n'utilisons ni glOrtho
ni glFrustrum
, c'est ce que nous obtenons.
glOrtho
et glFrustrum
ne sont que des transformations linéaires (multiplication de matrice AKA) telles que:
glOrtho
: prend un rectangle 3D donné dans le cube par défautglFrustrum
: prend une section pyramidale donnée dans le cube par défautCette transformation est ensuite appliquée à tous les sommets. C'est ce que je veux dire en 2D:
La dernière étape après la transformation est simple:
x
, y
et z
sont dans [-1, +1]
z
et ne prendre que x
et y
, qui peuvent maintenant être placés dans un écran 2DAvec glOrtho
, z
est ignoré, vous pouvez donc toujours utiliser 0
.
Une des raisons pour lesquelles vous voudrez peut-être utiliser z != 0
est de faire en sorte que les sprites cachent l’arrière-plan avec le tampon de profondeur.
Déprécation
glOrtho
est obsolète à compter de OpenGL 4.5 : le profil de compatibilité 12.1. "TRANSFORMATIONS VERTEX À FONCTION FIXE" est en rouge.
Donc, ne l'utilisez pas pour la production. Dans tous les cas, le comprendre est un bon moyen d’obtenir un aperçu d’OpenGL.
Les programmes OpenGL 4 modernes calculent la matrice de transformation (ce qui est petit) sur la CPU, puis attribuent la matrice et tous les points à transformer à OpenGL, ce qui permet de réaliser très rapidement, en parallèle, des milliers de multiplications de matrice pour différents points.
Ecrivez manuellement vertex shaders puis faites la multiplication de manière explicite, généralement avec les types de données vectoriels pratiques du langage de tramage OpenGL.
Puisque vous écrivez le shader de manière explicite, cela vous permet d’ajuster l’algorithme à vos besoins. Une telle souplesse est une caractéristique majeure des GPU plus modernes, qui, contrairement aux anciens qui utilisaient un algorithme fixe avec certains paramètres d'entrée, peuvent désormais effectuer des calculs arbitraires. Voir aussi: https://stackoverflow.com/a/36211337/895245
Avec un GLfloat transform[]
cela ressemblerait à quelque chose comme ça:
#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;
}
Sortie:
La matrice pour glOrtho
est très simple, composée uniquement de mise à l'échelle et de traduction:
scalex, 0, 0, translatex,
0, scaley, 0, translatey,
0, 0, scalez, translatez,
0, 0, 0, 1
comme mentionné dans le OpenGL 2 docs .
La glFrustum
matrice n'est pas trop difficile à calculer à la main, mais commence à devenir ennuyeuse. Notez comment frustum ne peut pas être compensé par une simple mise à l'échelle et des traductions comme glOrtho
, plus d'informations sur: https://gamedev.stackexchange.com/a/118848/25171
La bibliothèque mathématique GLM OpenGL C++ est un choix courant pour calculer de telles matrices. http://glm.g-truc.net/0.9.2/api/a00245.html documente à la fois une opération ortho
et frustum
.
glOrtho décrit une transformation qui produit une projection parallèle. La matrice actuelle (voir glMatrixMode) est multipliée par cette matrice et le résultat remplace la matrice actuelle, comme si glMultMatrix était appelée avec la matrice suivante comme argument:
documentation OpenGL (mon gras)
Les nombres définissent les emplacements des plans de découpage (gauche, droite, bas, haut, proche et éloigné).
La projection "normale" est une projection en perspective qui fournit l'illusion de profondeur. Wikipedia définit une projection parallèle comme:
Les projections parallèles ont des lignes de projection parallèles à la fois dans la réalité et dans le plan de projection.
La projection parallèle correspond à une projection en perspective avec un point de vue hypothétique, par exemple un point où la caméra se trouve à une distance infinie de l’objet et possède une distance focale infinie, ou "zoom".