glOrthoKomenda wywołuje projekcję „ukryte”, które można zobaczyć w dolnym rzędzie. Bez względu na to, jak daleko znajdują się wierzchołki w kierunku z, nie cofną się one w dal.
Używam glOrtho za każdym razem, gdy potrzebuję zrobić grafikę 2D w OpenGL (takie jak paski zdrowia, menu itp.), Używając następującego kodu za każdym razem, gdy zmienia się rozmiar okna:
Spowoduje to przemapowanie współrzędnych OpenGL na równoważne wartości pikseli (X od 0 do windowWidth, a Y od 0 do windowHeight). Zwróć uwagę, że odwróciłem wartości Y, ponieważ współrzędne OpenGL zaczynają się od lewego dolnego rogu okna. Tak więc, odwracając, otrzymuję bardziej konwencjonalny (0,0), zaczynając raczej od lewego górnego rogu okna.
Zwróć uwagę, że wartości Z są obcinane od 0 do 1. Dlatego uważaj, kiedy określasz wartość Z dla położenia wierzchołka, zostanie on obcięty, jeśli wykracza poza ten zakres. W przeciwnym razie, jeśli znajduje się w tym zakresie, wydaje się, że nie ma wpływu na pozycję, z wyjątkiem testów Z.
o mój Boże, KOCHAM CIĘ. Czy masz pojęcie, ile czasu zajmuje znalezienie / wymyślenie tej pojedynczej linii kodu online? Dziękuję,
nadam
2
Uwaga: (na Androidzie) nawet jeśli model ma tylko ujemne wartości z, wydaje się konieczne mieć dodatnią wartość dla końcowego (dalekiego) parametru. Zrobiłem prosty test trójkąta (z wyłączonym eliminowaniem), z wierzchołkami na poziomie z= -2. Trójkąt był niewidoczny jeśli użyłem glOrtho(.., 0.0f, -4.0f);, ..-1.0f, -3.0f)albo ..-3.0f, -1.0f). Aby był widoczny, parametr far musiał być DODATNI 2 lub większy; wydawało się, że nie ma znaczenia, jaki był parametr w pobliżu. Każda z nich pracował: ..0.0f, 2.0f), ..-1.0f, 2.0f), ..-3.0f, 2.0f), lub ..0.0f, 1000.0f.
ToolmakerSteve
10
To absurdalne, ile jest złych tutoriali na temat OpenGl.
@mgouin Zakres z określa, gdzie znajduje się Twoja płaszczyzna bliska Z i płaszczyzna daleka. Kiedy rysujesz geometrię, jej wartości Z muszą znajdować się wewnątrz dwóch płaszczyzn Z. Jeśli wypadną poza płaszczyzny Z, geometria nie zostanie renderowana. Ponadto twój renderer ma tylko określoną rozdzielczość dla głębi. Jeśli masz odległą płaszczyznę ustawioną na 1000 jednostek i spróbujesz narysować malutki model z małymi twarzami w odległości 0,1 jednostki od siebie, OpenGL nie będzie w stanie zapewnić wymaganej rozdzielczości głębi, a otrzymasz walkę Z (migotanie) między twarzami.
Mikepote
56
Minimalny działający przykład
glOrtho: Gry 2D, obiekty bliskie i dalekie mają ten sam rozmiar:
glFrustrum: bardziej realistyczne, jak 3D, identyczne obiekty dalej wydają się mniejsze:
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;
}
usunięcia jakichkolwiek punktów poza sześcianu (uboju): po prostu upewnić się, że x, yi zsą w[-1, +1]
zignoruj zkomponent i weź tylko xi y, które teraz można umieścić na ekranie 2D
Z glOrtho, zjest ignorowane, więc równie dobrze możesz zawsze używać 0.
Jednym z powodów, dla których możesz chcieć użyć, z != 0jest sprawienie, by duszki ukrywały tło za pomocą bufora głębi.
Dezaprobata
glOrthojest przestarzałe od OpenGL 4.5 : profil zgodności 12.1. „TRANSFORMACJE VERTEX O STAŁEJ FUNKCJI” jest zaznaczone na czerwono.
Więc nie używaj go do produkcji. W każdym razie zrozumienie tego jest dobrym sposobem na uzyskanie wglądu w OpenGL.
Nowoczesne programy OpenGL 4 obliczają macierz transformacji (która jest mała) na CPU, a następnie przekazują macierz i wszystkie punkty do przekształcenia do OpenGL, który może wykonywać tysiące mnożenia macierzy dla różnych punktów naprawdę szybko równolegle.
Następnie ręcznie napisane moduły cieniujące wierzchołków wykonują mnożenie w sposób jawny, zwykle z wygodnymi typami danych wektorowych języka OpenGL Shading Language.
Ponieważ moduł cieniujący piszesz jawnie, pozwala to dostosować algorytm do własnych potrzeb. Taka elastyczność jest główną cechą nowocześniejszych procesorów graficznych, które w przeciwieństwie do starych, które stosowały stały algorytm z niektórymi parametrami wejściowymi, mogą teraz wykonywać dowolne obliczenia. Zobacz też: https://stackoverflow.com/a/36211337/895245
Z wyraźnym GLfloat transform[]wyglądem wyglądałoby to mniej więcej tak:
Biblioteka matematyczna GLM OpenGL C ++ jest popularnym wyborem do obliczania takich macierzy. http://glm.g-truc.net/0.9.2/api/a00245.html dokumentuje zarówno an, jak orthoi frustumoperacje.
„czego należy użyć zamiast tego?” - skonstruuj własne macierze i przypisz je bezpośrednio.
Kromster
Mam trudności ze skompilowaniem ostatniego przykładu kodu (przekształcanie trójkąta), sklonowałem repozytorium, ale po prostu common.h:19:23: error: ‘TIME_UTC’ undeclared (first use in this function) timespec_get(&ts, TIME_UTC);
pojawia się
1
@Ivanzinho Nie mogłem odtworzyć na Ubuntu 20.04, prawdopodobnie dzieje się tak, ponieważ jest to w C11, którego GCC jeszcze nie implementuje. Ale teraz zminimalizowałem przykład tej odpowiedzi bez wspólnego. H tak jak powinienem był zrobić wcześniej, więc powinno działać :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
4
glOrtho opisuje transformację, która tworzy równoległą projekcję. Bieżąca macierz (patrz glMatrixMode) jest mnożona przez tę macierz, a wynik zastępuje aktualną macierz, tak jakby glMultMatrix zostało wywołane z następującą macierzą jako argumentem:
Liczby określają położenie płaszczyzn tnących (lewa, prawa, dół, góra, blisko i daleko).
„Normalna” projekcja to rzut perspektywiczny, który zapewnia iluzję głębi. Wikipedia definiuje równoległe odwzorowanie jako:
Rzuty równoległe mają linie rzutowania, które są równoległe zarówno w rzeczywistości, jak i na płaszczyźnie rzutowania.
Rzutowanie równoległe odpowiada rzutowi perspektywicznemu z hipotetycznym punktem widzenia - np. Takim, w którym kamera znajduje się w nieskończonej odległości od obiektu i ma nieskończoną ogniskową, czyli „zoom”.
cześć dzięki za informację. Nie mogłem do końca zrozumieć różnicy między rzutowaniem równoległym a perspektywicznym. trochę googlowałem
ufk
6
Niestety informacje, które otrzymałeś z responsów.com, są bezwartościowe. Na przykład widok izometryczny jest bardzo trójwymiarowy, ale jest to rzut równoległy bez perspektywy. Zobacz tutaj, a są tam również linki do wielu innych przykładów projekcji: en.wikipedia.org/wiki/Isometric_projection
Odpowiedzi:
Spójrz na ten obrazek: Projekcje graficzne
glOrtho
Komenda wywołuje projekcję „ukryte”, które można zobaczyć w dolnym rzędzie. Bez względu na to, jak daleko znajdują się wierzchołki w kierunku z, nie cofną się one w dal.Używam glOrtho za każdym razem, gdy potrzebuję zrobić grafikę 2D w OpenGL (takie jak paski zdrowia, menu itp.), Używając następującego kodu za każdym razem, gdy zmienia się rozmiar okna:
glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0f, windowWidth, windowHeight, 0.0f, 0.0f, 1.0f);
Spowoduje to przemapowanie współrzędnych OpenGL na równoważne wartości pikseli (X od 0 do windowWidth, a Y od 0 do windowHeight). Zwróć uwagę, że odwróciłem wartości Y, ponieważ współrzędne OpenGL zaczynają się od lewego dolnego rogu okna. Tak więc, odwracając, otrzymuję bardziej konwencjonalny (0,0), zaczynając raczej od lewego górnego rogu okna.
Zwróć uwagę, że wartości Z są obcinane od 0 do 1. Dlatego uważaj, kiedy określasz wartość Z dla położenia wierzchołka, zostanie on obcięty, jeśli wykracza poza ten zakres. W przeciwnym razie, jeśli znajduje się w tym zakresie, wydaje się, że nie ma wpływu na pozycję, z wyjątkiem testów Z.
źródło
z= -2
. Trójkąt był niewidoczny jeśli użyłemglOrtho(.., 0.0f, -4.0f);
,..-1.0f, -3.0f)
albo..-3.0f, -1.0f)
. Aby był widoczny, parametr far musiał być DODATNI 2 lub większy; wydawało się, że nie ma znaczenia, jaki był parametr w pobliżu. Każda z nich pracował:..0.0f, 2.0f)
,..-1.0f, 2.0f)
,..-3.0f, 2.0f)
, lub..0.0f, 1000.0f
.Minimalny działający przykład
glOrtho
: Gry 2D, obiekty bliskie i dalekie mają ten sam rozmiar:glFrustrum
: bardziej realistyczne, jak 3D, identyczne obiekty dalej wydają się mniejsze: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 .
Skompilować:
gcc -ggdb3 -O0 -o main -std=c99 -Wall -Wextra -pedantic main.c -lGL -lGLU -lglut
Uruchom z
glOrtho
:Uruchom z
glFrustrum
:Testowane na Ubuntu 18.10.
Schemat
Orto: kamera to płaszczyzna, widoczna objętość to prostokąt:
Frustrum: kamera to punkt, widoczna objętość kawałek piramidy:
Źródło obrazu .
Parametry
Zawsze patrzymy od + z do -z z + y w górę:
glOrtho(left, right, bottom, top, near, far)
left
: minimumx
widzimyright
: maksimum,x
które widzimybottom
: minimumy
widzimytop
: maksimum,y
które widzimy-near
: minimumz
widzimy. Tak , to-1
czasynear
. Zatem ujemne wejście oznacza pozytywnez
.-far
: maksimum,z
które widzimy. Również negatywne.Schemat:
Źródło obrazu .
Jak to działa pod maską
Ostatecznie OpenGL zawsze „używa”:
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
Jeśli nie użyjemy ani
glOrtho
nieglFrustrum
, otrzymamy to.glOrtho
iglFrustrum
są po prostu przekształceniami liniowymi (mnożenie macierzy AKA), takimi, że:glOrtho
: pobiera dany prostokąt 3D do domyślnej kostkiglFrustrum
: przenosi daną sekcję piramidy do domyślnej kostkiTa transformacja jest następnie stosowana do wszystkich wierzchołków. To właśnie mam na myśli w 2D:
Źródło obrazu .
Ostatni krok po transformacji jest prosty:
x
,y
iz
są w[-1, +1]
z
komponent i weź tylkox
iy
, które teraz można umieścić na ekranie 2DZ
glOrtho
,z
jest ignorowane, więc równie dobrze możesz zawsze używać0
.Jednym z powodów, dla których możesz chcieć użyć,
z != 0
jest sprawienie, by duszki ukrywały tło za pomocą bufora głębi.Dezaprobata
glOrtho
jest przestarzałe od OpenGL 4.5 : profil zgodności 12.1. „TRANSFORMACJE VERTEX O STAŁEJ FUNKCJI” jest zaznaczone na czerwono.Więc nie używaj go do produkcji. W każdym razie zrozumienie tego jest dobrym sposobem na uzyskanie wglądu w OpenGL.
Nowoczesne programy OpenGL 4 obliczają macierz transformacji (która jest mała) na CPU, a następnie przekazują macierz i wszystkie punkty do przekształcenia do OpenGL, który może wykonywać tysiące mnożenia macierzy dla różnych punktów naprawdę szybko równolegle.
Następnie ręcznie napisane moduły cieniujące wierzchołków wykonują mnożenie w sposób jawny, zwykle z wygodnymi typami danych wektorowych języka OpenGL Shading Language.
Ponieważ moduł cieniujący piszesz jawnie, pozwala to dostosować algorytm do własnych potrzeb. Taka elastyczność jest główną cechą nowocześniejszych procesorów graficznych, które w przeciwieństwie do starych, które stosowały stały algorytm z niektórymi parametrami wejściowymi, mogą teraz wykonywać dowolne obliczenia. Zobacz też: https://stackoverflow.com/a/36211337/895245
Z wyraźnym
GLfloat transform[]
wyglądem wyglądałoby to mniej więcej tak:glfw_transform.c
#include <math.h> #include <stdio.h> #include <stdlib.h> #define GLEW_STATIC #include <GL/glew.h> #include <GLFW/glfw3.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 }; /* Build and compile shader program, return its ID. */ GLuint common_get_shader_program( const char *vertex_shader_source, const char *fragment_shader_source ) { GLchar *log = NULL; GLint log_length, success; GLuint fragment_shader, program, vertex_shader; /* Vertex shader */ vertex_shader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL); glCompileShader(vertex_shader); glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success); glGetShaderiv(vertex_shader, GL_INFO_LOG_LENGTH, &log_length); log = malloc(log_length); if (log_length > 0) { glGetShaderInfoLog(vertex_shader, log_length, NULL, log); printf("vertex shader log:\n\n%s\n", log); } if (!success) { printf("vertex shader compile error\n"); exit(EXIT_FAILURE); } /* Fragment shader */ fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL); glCompileShader(fragment_shader); glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success); glGetShaderiv(fragment_shader, GL_INFO_LOG_LENGTH, &log_length); if (log_length > 0) { log = realloc(log, log_length); glGetShaderInfoLog(fragment_shader, log_length, NULL, log); printf("fragment shader log:\n\n%s\n", log); } if (!success) { printf("fragment shader compile error\n"); exit(EXIT_FAILURE); } /* Link shaders */ program = glCreateProgram(); glAttachShader(program, vertex_shader); glAttachShader(program, fragment_shader); glLinkProgram(program); glGetProgramiv(program, GL_LINK_STATUS, &success); glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length); if (log_length > 0) { log = realloc(log, log_length); glGetProgramInfoLog(program, log_length, NULL, log); printf("shader link log:\n\n%s\n", log); } if (!success) { printf("shader link error"); exit(EXIT_FAILURE); } /* Cleanup. */ free(log); glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); return program; } 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 .
Skompiluj i uruchom:
gcc -ggdb3 -O0 -o glfw_transform.out -std=c99 -Wall -Wextra -pedantic glfw_transform.c -lGL -lGLU -lglut -lGLEW -lglfw -lm ./glfw_transform.out
Wynik:
Matryca dla
glOrtho
jest naprawdę prosta, składa się tylko ze skalowania i tłumaczenia:jak wspomniano w dokumentacji OpenGL 2 .
glFrustum
Matryca nie jest zbyt trudne do obliczenia przez strony albo, ale zaczyna się denerwujące. Zwróć uwagę, że frustum nie można nadrobić tylko ze skalowania i tłumaczeńglOrtho
, więcej informacji na: https://gamedev.stackexchange.com/a/118848/25171Biblioteka matematyczna GLM OpenGL C ++ jest popularnym wyborem do obliczania takich macierzy. http://glm.g-truc.net/0.9.2/api/a00245.html dokumentuje zarówno an, jak
ortho
ifrustum
operacje.źródło
common.h:19:23: error: ‘TIME_UTC’ undeclared (first use in this function) timespec_get(&ts, TIME_UTC);
Dokumentacja OpenGL (moja pogrubienie)
Liczby określają położenie płaszczyzn tnących (lewa, prawa, dół, góra, blisko i daleko).
„Normalna” projekcja to rzut perspektywiczny, który zapewnia iluzję głębi. Wikipedia definiuje równoległe odwzorowanie jako:
źródło