O glOrthocomando 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:
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.
oh meu deus EU TE AMO. Você tem ideia de quanto tempo leva para encontrar / descobrir essa única linha de código online? Obrigado, devo nomear meu primeiro filho com seu nome
karpathy
2
Nota: (no Android) mesmo que o modelo tenha apenas valores z negativos, parece ser necessário ter um valor positivo para o parâmetro final (distante). Fiz um teste de triângulo simples (com seleção desativada), com vértices em 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.
Toolmaker Steve
9
É ridículo a quantidade de tutoriais ruins sobre OpenGl que existem.
@mgouin O intervalo z especifica onde o seu plano Z-próximo e o seu plano Z-distante estão. Quando você desenha sua geometria, seus valores Z devem estar dentro dos dois planos Z. Se eles ficarem fora dos planos Z, sua geometria não será renderizada. Além disso, seu renderizador só tem uma determinada resolução de profundidade. Se você tiver seu plano distante definido para 1000 unidades de distância e tentar desenhar um modelo minúsculo com rostos pequenos a 0,1 unidades de distância um do outro, o OpenGL não será capaz de fornecer a resolução de profundidade que você precisa e você obterá Z-fighting (piscando) entre os rostos.
Mikepote
54
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>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;
}
remova quaisquer pontos fora do cubo (abate): apenas certifique-se de que x, ye zestão dentro[-1, +1]
ignore o zcomponente e leve apenas xe y, que agora pode ser colocado em uma tela 2D
Com 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
glOrthoestá 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:
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 orthoe frustum.
"o que deve ser usado em vez disso?" - construa suas próprias matrizes e atribua-as diretamente.
Kromster
4
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:
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".
Oi, obrigado pela informação. Eu não conseguia entender a diferença entre projeção paralela e em perspectiva. Pesquisei um pouco no Google e encontrei a resposta em wiki.answers.com/Q/…
ufk
6
Infelizmente, as informações que você obteve em answers.com são muito inúteis. Uma vista isométrica, por exemplo, é muito 3-D, mas é uma projeção paralela sem perspectiva. Veja aqui, e também há links para muitos outros exemplos de projeções: en.wikipedia.org/wiki/Isometric_projection
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.
fonte
z= -2
. O triângulo era invisível se eu useiglOrtho(.., 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; }
GitHub upstream .
Compilar:
gcc -ggdb3 -O0 -o main -std=c99 -Wall -Wextra -pedantic main.c -lGL -lGLU -lglut
Executar com
glOrtho
:./main 1
Executar com
glFrustrum
: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:
Fonte da imagem .
Parâmetros
Estamos sempre olhando de + z a -z com + y para cima:
glOrtho(left, right, bottom, top, near, far)
left
: mínimox
que vemosright
: máximox
que vemosbottom
: mínimoy
que vemostop
: máximoy
que vemos-near
: mínimoz
que vemos. Sim , esta é a-1
horanear
. Portanto, uma entrada negativa significa positivaz
.-far
: máximoz
que vemos. Também negativo.Esquema:
Fonte da imagem .
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
nemglFrustrum
, é isso que obtemos.glOrtho
eglFrustrum
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:
Fonte da imagem .
A etapa final após a transformação é simples:
x
,y
ez
estão dentro[-1, +1]
z
componente e leve apenasx
ey
, que agora pode ser colocado em uma tela 2DCom
glOrtho
,z
é ignorado, então você pode sempre usar0
.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; }
GitHub upstream .
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 comoglOrtho
, mais informações em: https://gamedev.stackexchange.com/a/118848/25171A 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
efrustum
.fonte
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:
fonte