Exemple exécutable minimal
glOrtho
: Les jeux 2D, les objets proches et éloignés ont la même taille:
glFrustrum
: plus réaliste que la 3D, les objets identiques plus éloignés apparaissent 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 {
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 en amont .
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:
Source de l'image .
Paramètres
Nous cherchons toujours de + z à -z avec + y vers le haut:
glOrtho(left, right, bottom, top, near, far)
left
: minimum x
on voit
right
: maximum que x
nous voyons
bottom
: minimum y
on voit
top
: maximum que y
nous voyons
-near
: minimum que z
nous voyons. Oui , c'est le -1
temps near
. Donc, une entrée négative signifie positive z
.
-far
: maximum que z
nous voyons. Aussi négatif.
Schéma:
Source de l'image .
Comment ça marche sous le capot
Au final, 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 ne glFrustrum
sont que des transformations linéaires (multiplication de matrice AKA) telles que:
glOrtho
: prend un rectangle 3D donné dans le cube par défaut
glFrustrum
: prend une section de pyramide donnée dans le cube par défaut
Cette transformation est ensuite appliquée à tous les sommets. C'est ce que je veux dire en 2D:
Source de l'image .
La dernière étape après la transformation est simple:
- supprimer tous les points de l' extérieur du cube (culling): il suffit de faire en sorte que
x
, y
et z
sont[-1, +1]
- ignorer le
z
composant et ne prendre que x
et y
, qui peut maintenant être placé dans un écran 2D
Avec glOrtho
, z
est ignoré, vous pouvez donc toujours utiliser 0
.
Une des raisons que vous pourriez vouloir utiliser z != 0
est de faire en sorte que les sprites masquent l'arrière-plan avec le tampon de profondeur.
Désapprobation
glOrtho
est obsolète à partir d' OpenGL 4.5 : le profil de compatibilité 12.1. «TRANSFORMATIONS VERTEX À FONCTION FIXE» est en rouge.
Alors 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 modernes d'OpenGL 4 calculent la matrice de transformation (qui est petite) sur le CPU, puis donnent la matrice et tous les points à transformer en OpenGL, qui peut faire les milliers de multiplications de matrice pour différents points très rapidement en parallèle.
Les nuanceurs de vertex écrits manuellement effectuent ensuite la multiplication explicitement, généralement avec les types de données vectorielles pratiques du langage OpenGL Shading.
Puisque vous écrivez explicitement le shader, cela vous permet d'ajuster l'algorithme à vos besoins. Une telle flexibilité 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 explicite, GLfloat transform[]
cela ressemblerait à quelque chose comme ceci:
#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 en amont .
Production:
La matrice pour glOrtho
est vraiment simple, composée uniquement de mise à l'échelle et de translation:
scalex, 0, 0, translatex,
0, scaley, 0, translatey,
0, 0, scalez, translatez,
0, 0, 0, 1
comme mentionné dans la documentation OpenGL 2 .
La glFrustum
matrice n'est pas trop difficile à calculer à la main non plus, mais commence à devenir ennuyeuse. Notez comment le frustum ne peut pas être composé uniquement avec une 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 populaire pour le calcul de telles matrices. http://glm.g-truc.net/0.9.2/api/a00245.html documente à la fois les opérations ortho
et frustum
.