Não consigo entender o uso de glOrtho
. Alguém pode explicar para que é usado?
É usado para definir o intervalo do limite das coordenadas xy e z?
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
Isso significa que o intervalo x, y e z é de -1 a 1?
Não consigo entender o uso de glOrtho
. Alguém pode explicar para que é usado?
É usado para definir o intervalo do limite das coordenadas xy e z?
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
Isso significa que o intervalo x, y e z é de -1 a 1?
Respostas:
Dê uma olhada nesta imagem: Projeções Gráficas
O glOrtho
comando produz uma projeção "Oblíqua" que você vê na linha inferior. Não importa a distância que os vértices estejam na direção z, eles não se distanciarão.
Eu uso glOrtho sempre que preciso fazer gráficos 2D em OpenGL (como barras de saúde, menus etc.) usando o seguinte código sempre que a janela é redimensionada:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0f, windowWidth, windowHeight, 0.0f, 0.0f, 1.0f);
Isso irá remapear as coordenadas OpenGL nos valores de pixel equivalentes (X indo de 0 para windowWidth e Y indo de 0 para windowHeight). Observe que inverti os valores Y porque as coordenadas OpenGL começam no canto inferior esquerdo da janela. Assim, ao virar, obtenho um (0,0) mais convencional começando no canto superior esquerdo da janela.
Observe que os valores Z são recortados de 0 a 1. Portanto, tome cuidado ao especificar um valor Z para a posição do seu vértice, ele será recortado se ficar fora desse intervalo. Caso contrário, se estiver dentro desse intervalo, parecerá não ter efeito sobre a posição, exceto para testes Z.
z= -2
. O triângulo era invisível se eu usei glOrtho(.., 0.0f, -4.0f);
, ..-1.0f, -3.0f)
ou ..-3.0f, -1.0f)
. Para ser visível, o parâmetro distante precisava ser POSITIVO 2 ou maior; não parecia importar qual era o parâmetro próximo. Qualquer um destes trabalhou: ..0.0f, 2.0f)
, ..-1.0f, 2.0f)
, ..-3.0f, 2.0f)
, ou ..0.0f, 1000.0f
.
Exemplo de execução mínima
glOrtho
: Jogos 2D, objetos próximos e distantes parecem do mesmo tamanho:
glFrustrum
: mais real como 3D, objetos idênticos mais distantes parecem menores:
main.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;
}
Compilar:
gcc -ggdb3 -O0 -o main -std=c99 -Wall -Wextra -pedantic main.c -lGL -lGLU -lglut
Executar com glOrtho
:
./main 1
Executar com glFrustrum
:
./main
Testado em Ubuntu 18.10.
Esquema
Ortho: a câmera é um plano, o volume visível um retângulo:
Frustrum: a câmera é um ponto, volume visível uma fatia de uma pirâmide:
Parâmetros
Estamos sempre olhando de + z a -z com + y para cima:
glOrtho(left, right, bottom, top, near, far)
left
: mínimo x
que vemosright
: máximo x
que vemosbottom
: mínimo y
que vemostop
: máximo y
que vemos-near
: mínimo z
que vemos. Sim , esta é a -1
hora near
. Portanto, uma entrada negativa significa positiva z
.-far
: máximo z
que vemos. Também negativo.Esquema:
Como funciona nos bastidores
No final das contas, OpenGL sempre "usa":
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
Se não usarmos glOrtho
nem glFrustrum
, é isso que obtemos.
glOrtho
e glFrustrum
são apenas transformações lineares (também conhecida como multiplicação de matriz), de modo que:
glOrtho
: leva um determinado retângulo 3D para o cubo padrãoglFrustrum
: leva uma determinada seção da pirâmide para o cubo padrãoEssa transformação é então aplicada a todos os vértices. Isso é o que quero dizer em 2D:
A etapa final após a transformação é simples:
x
, y
e z
estão dentro[-1, +1]
z
componente e leve apenas x
e y
, que agora pode ser colocado em uma tela 2DCom glOrtho
, z
é ignorado, então você pode sempre usar 0
.
Uma razão que você pode querer usar z != 0
é fazer com que os sprites ocultem o fundo com o buffer de profundidade.
Suspensão de uso
glOrtho
está obsoleto a partir do OpenGL 4.5 : o perfil de compatibilidade 12.1. "TRANSFORMAÇÕES DE VERTEX COM FUNÇÕES FIXAS" está a vermelho.
Portanto, não o use para produção. Em qualquer caso, entendê-lo é uma boa maneira de obter alguns insights do OpenGL.
Os programas OpenGL 4 modernos calculam a matriz de transformação (que é pequena) na CPU e, em seguida, fornecem a matriz e todos os pontos a serem transformados em OpenGL, que pode fazer milhares de multiplicações de matrizes para diferentes pontos em paralelo muito rápido.
Os sombreadores de vértice escritos manualmente fazem a multiplicação explicitamente, geralmente com os tipos de dados vetoriais convenientes da OpenGL Shading Language.
Como você escreve o sombreador explicitamente, isso permite ajustar o algoritmo de acordo com suas necessidades. Essa flexibilidade é uma característica importante das GPUs mais modernas, que ao contrário das antigas que faziam um algoritmo fixo com alguns parâmetros de entrada, agora podem fazer cálculos arbitrários. Veja também: https://stackoverflow.com/a/36211337/895245
Com um explícito GLfloat transform[]
, seria algo assim:
#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;
}
Resultado:
A matriz para glOrtho
é muito simples, composta apenas de escalonamento e tradução:
scalex, 0, 0, translatex,
0, scaley, 0, translatey,
0, 0, scalez, translatez,
0, 0, 0, 1
conforme mencionado nos documentos do OpenGL 2 .
A glFrustum
matriz não é muito difícil de calcular manualmente, mas começa a ficar chata. Observe como o frustum não pode ser feito apenas com escalas e traduções como glOrtho
, mais informações em: https://gamedev.stackexchange.com/a/118848/25171
A biblioteca matemática GLM OpenGL C ++ é uma escolha popular para calcular tais matrizes. http://glm.g-truc.net/0.9.2/api/a00245.html documenta as operações ortho
e frustum
.
glOrtho descreve uma transformação que produz uma projeção paralela . A matriz atual (consulte glMatrixMode) é multiplicada por esta matriz e o resultado substitui a matriz atual, como se glMultMatrix fosse chamado com a seguinte matriz como seu argumento:
Documentação OpenGL (meu negrito)
Os números definem as localizações dos planos de recorte (esquerda, direita, inferior, superior, próximo e distante).
A projeção "normal" é uma projeção em perspectiva que fornece a ilusão de profundidade. A Wikipedia define uma projeção paralela como:
As projeções paralelas têm linhas de projeção que são paralelas tanto na realidade quanto no plano de projeção.
A projeção paralela corresponde a uma projeção em perspectiva com um ponto de vista hipotético - por exemplo, aquele em que a câmera fica a uma distância infinita do objeto e tem um comprimento focal infinito, ou "zoom".