The glOrthoperintah menghasilkan "Oblique" proyeksi yang Anda lihat di baris bawah. Tidak peduli seberapa jauh titik sudut dalam arah z, mereka tidak akan mundur ke kejauhan.
Saya menggunakan glOrtho setiap kali saya perlu melakukan grafik 2D di OpenGL (seperti bilah kesehatan, menu, dll.) Menggunakan kode berikut setiap kali jendela diubah ukurannya:
Ini akan memetakan ulang koordinat OpenGL menjadi nilai piksel yang setara (X bergerak dari 0 ke windowWidth dan Y dari 0 ke windowHeight). Perhatikan bahwa saya telah membalik nilai Y karena koordinat OpenGL dimulai dari sudut kiri bawah jendela. Jadi dengan membalik, saya mendapatkan yang lebih konvensional (0,0) mulai dari sudut kiri atas jendela.
Perhatikan bahwa nilai Z dipotong dari 0 ke 1. Jadi berhati-hatilah saat Anda menentukan nilai Z untuk posisi puncak Anda, nilai itu akan dipotong jika berada di luar rentang itu. Jika tidak, jika berada di dalam kisaran itu, itu tidak akan berpengaruh pada posisi kecuali untuk pengujian Z.
oh my god, aku mencintaimu. Apakah Anda tahu berapa lama waktu yang dibutuhkan untuk menemukan / mengetahui satu baris kode ini secara online? Terima kasih, saya akan menamai anak pertama saya setelah Anda untuk ini
karpathy
2
Catatan: (di Android) meskipun model hanya memiliki nilai z negatif, tampaknya perlu untuk memiliki nilai positif untuk parameter akhir (jauh). Saya melakukan tes segitiga sederhana (dengan pemusnahan dinonaktifkan), dengan simpul di z= -2. Segitiga itu tak terlihat jika saya menggunakan glOrtho(.., 0.0f, -4.0f);, ..-1.0f, -3.0f)atau ..-3.0f, -1.0f). Agar terlihat, parameter jauh harus POSITIF 2 atau lebih besar; tampaknya tidak peduli apa parameter dekat itu. Semua ini bekerja: ..0.0f, 2.0f), ..-1.0f, 2.0f), ..-3.0f, 2.0f), atau ..0.0f, 1000.0f.
ToolmakerSteve
10
Sungguh konyol jumlah tutorial buruk di OpenGl yang ada.
@mgouin Rentang z menentukan di mana bidang Z-dekat dan bidang Z-jauh Anda berada. Saat Anda menggambar geometri, nilai Z-nya harus berada di dalam dua bidang Z. Jika mereka berada di luar bidang Z, geometri Anda tidak akan ditampilkan. Penyaji Anda juga hanya memiliki resolusi tertentu untuk kedalaman. Jika Anda mengatur pesawat jauh Anda menjadi 1000 unit dan Anda mencoba menggambar model kecil dengan wajah kecil 0,1 unit dari satu sama lain, OpenGL tidak akan dapat memberi Anda resolusi kedalaman yang Anda butuhkan dan Anda akan mendapatkan pertempuran-Z (berkedip) di antara wajah.
Mikepote
55
Contoh minimal runnable
glOrtho: Game 2D, objek dekat dan jauh tampak berukuran sama:
glFrustrum: lebih nyata seperti 3D, objek identik yang jauh tampak lebih kecil:
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;
}
hapus poin di luar kubus (pemusnahan): pastikan saja x, ydan zberada di[-1, +1]
abaikan zkomponen tersebut dan ambil saja xdan y, yang sekarang dapat dimasukkan ke dalam layar 2D
Dengan glOrtho, zdiabaikan, jadi sebaiknya Anda selalu menggunakan 0.
Salah satu alasan Anda mungkin ingin menggunakan z != 0adalah membuat sprite menyembunyikan latar belakang dengan buffer kedalaman.
Bantahan
glOrthotidak digunakan lagi sejak OpenGL 4.5 : profil kompatibilitas 12.1. "TRANSFORMASI VERTEX FUNGSI TETAP" berwarna merah.
Jadi jangan gunakan untuk produksi. Bagaimanapun, memahaminya adalah cara yang baik untuk mendapatkan beberapa wawasan OpenGL.
Program OpenGL 4 modern menghitung matriks transformasi (yang kecil) pada CPU, dan kemudian memberikan matriks dan semua titik untuk diubah menjadi OpenGL, yang dapat melakukan ribuan perkalian matriks untuk berbagai titik dengan sangat cepat secara paralel.
Vertex shader yang ditulis secara manual kemudian melakukan perkalian secara eksplisit, biasanya dengan tipe data vektor yang sesuai dari OpenGL Shading Language.
Karena Anda menulis shader secara eksplisit, ini memungkinkan Anda menyesuaikan algoritme dengan kebutuhan Anda. Fleksibilitas semacam itu adalah fitur utama dari GPU yang lebih modern, yang tidak seperti yang lama yang melakukan algoritme tetap dengan beberapa parameter input, sekarang dapat melakukan penghitungan sewenang-wenang. Lihat juga: https://stackoverflow.com/a/36211337/895245
Dengan eksplisit GLfloat transform[]akan terlihat seperti ini:
The glFrustummatriks tidak terlalu sulit untuk menghitung dengan tangan baik, tapi mulai mendapatkan menjengkelkan. Perhatikan bagaimana frustum tidak dapat dibuat hanya dengan penskalaan dan terjemahan seperti glOrtho, info lebih lanjut di: https://gamedev.stackexchange.com/a/118848/25171
Pustaka matematika GLM OpenGL C ++ adalah pilihan populer untuk menghitung matriks semacam itu. http://glm.g-truc.net/0.9.2/api/a00245.html mendokumentasikan baik orthodan frustumoperasi.
"apa yang harus digunakan sebagai gantinya?" - buat matriks Anda sendiri dan tetapkan secara langsung.
Kromster
Saya mengalami kesulitan untuk mencoba mengompilasi contoh kode terakhir Anda (mengubah segitiga), saya telah mengkloning repositori tetapi saya baru saja mendapatkan kesalahancommon.h:19:23: error: ‘TIME_UTC’ undeclared (first use in this function) timespec_get(&ts, TIME_UTC);
Ivanzinho
1
@Ivanzinho Saya tidak dapat mereproduksi di Ubuntu 20.04, mungkin terjadi karena itu di C11 yang belum diterapkan oleh GCC Anda. Tapi sekarang saya meminimalkan contoh pada jawaban ini tanpa kesamaan.h seperti yang seharusnya saya lakukan sebelumnya sehingga seharusnya berhasil :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
4
glOrtho menjelaskan transformasi yang menghasilkan proyeksi paralel . Matriks saat ini (lihat glMatrixMode) dikalikan dengan matriks ini dan hasilnya menggantikan matriks saat ini, seolah-olah glMultMatrix dipanggil dengan matriks berikut sebagai argumennya:
Angka-angka menentukan lokasi bidang kliping (kiri, kanan, bawah, atas, dekat dan jauh).
Proyeksi "normal" adalah proyeksi perspektif yang memberikan ilusi kedalaman. Wikipedia mendefinisikan proyeksi paralel sebagai:
Proyeksi paralel memiliki garis proyeksi yang sejajar baik dalam realita maupun dalam bidang proyeksi.
Proyeksi paralel berhubungan dengan proyeksi perspektif dengan sudut pandang hipotetis — misalnya, proyeksi di mana kamera berada dalam jarak tak terhingga dari objek dan memiliki panjang fokus tak terbatas, atau "zoom".
hai terima kasih atas infonya saya tidak bisa begitu memahami perbedaan antara proyeksi paralel dan perspektif. Saya mencari di Google sedikit dan menemukan jawabannya di wiki.answers.com/Q/…
ufk
6
Sayangnya informasi yang Anda dapatkan dari answer.com sangat tidak berharga. Sebuah pandangan isometrik, misalnya, sangat 3-D, namun merupakan proyeksi paralel tanpa perspektif. Lihat di sini, dan terdapat juga tautan ke banyak contoh proyeksi lainnya: en.wikipedia.org/wiki/Isometric_projection
Jawaban:
Lihat gambar ini: Proyeksi Grafis
The
glOrtho
perintah menghasilkan "Oblique" proyeksi yang Anda lihat di baris bawah. Tidak peduli seberapa jauh titik sudut dalam arah z, mereka tidak akan mundur ke kejauhan.Saya menggunakan glOrtho setiap kali saya perlu melakukan grafik 2D di OpenGL (seperti bilah kesehatan, menu, dll.) Menggunakan kode berikut setiap kali jendela diubah ukurannya:
glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0f, windowWidth, windowHeight, 0.0f, 0.0f, 1.0f);
Ini akan memetakan ulang koordinat OpenGL menjadi nilai piksel yang setara (X bergerak dari 0 ke windowWidth dan Y dari 0 ke windowHeight). Perhatikan bahwa saya telah membalik nilai Y karena koordinat OpenGL dimulai dari sudut kiri bawah jendela. Jadi dengan membalik, saya mendapatkan yang lebih konvensional (0,0) mulai dari sudut kiri atas jendela.
Perhatikan bahwa nilai Z dipotong dari 0 ke 1. Jadi berhati-hatilah saat Anda menentukan nilai Z untuk posisi puncak Anda, nilai itu akan dipotong jika berada di luar rentang itu. Jika tidak, jika berada di dalam kisaran itu, itu tidak akan berpengaruh pada posisi kecuali untuk pengujian Z.
sumber
z= -2
. Segitiga itu tak terlihat jika saya menggunakanglOrtho(.., 0.0f, -4.0f);
,..-1.0f, -3.0f)
atau..-3.0f, -1.0f)
. Agar terlihat, parameter jauh harus POSITIF 2 atau lebih besar; tampaknya tidak peduli apa parameter dekat itu. Semua ini bekerja:..0.0f, 2.0f)
,..-1.0f, 2.0f)
,..-3.0f, 2.0f)
, atau..0.0f, 1000.0f
.Contoh minimal runnable
glOrtho
: Game 2D, objek dekat dan jauh tampak berukuran sama:glFrustrum
: lebih nyata seperti 3D, objek identik yang jauh tampak lebih kecil: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 .
Menyusun:
gcc -ggdb3 -O0 -o main -std=c99 -Wall -Wextra -pedantic main.c -lGL -lGLU -lglut
Jalankan dengan
glOrtho
:Jalankan dengan
glFrustrum
:Diuji di Ubuntu 18.10.
Skema
Ortho: kamera adalah bidang, volume terlihat persegi panjang:
Frustrum: kamera adalah sebuah titik, volume yang terlihat adalah sepotong piramida:
Sumber gambar .
Parameter
Kami selalu melihat dari + z ke -z dengan + y ke atas:
glOrtho(left, right, bottom, top, near, far)
left
: minimalx
kita lihatright
: maksimal yangx
kami lihatbottom
: minimaly
kita lihattop
: maksimal yangy
kami lihat-near
: minimalz
kita lihat. Ya , ini-1
saatnyanear
. Jadi masukan negatif berarti positifz
.-far
: maksimal yangz
kami lihat. Juga negatif.Skema:
Sumber gambar .
Cara kerjanya di bawah tenda
Pada akhirnya, OpenGL selalu "menggunakan":
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
Jika kita tidak menggunakan
glOrtho
atauglFrustrum
, itulah yang kita dapatkan.glOrtho
danglFrustrum
hanya transformasi linier (perkalian matriks AKA) seperti itu:glOrtho
: mengambil persegi 3D tertentu ke dalam kubus defaultglFrustrum
: mengambil bagian piramida tertentu ke dalam kubus defaultTransformasi ini kemudian diterapkan ke semua simpul. Inilah yang saya maksud dalam 2D:
Sumber gambar .
Langkah terakhir setelah transformasi sederhana:
x
,y
danz
berada di[-1, +1]
z
komponen tersebut dan ambil sajax
dany
, yang sekarang dapat dimasukkan ke dalam layar 2DDengan
glOrtho
,z
diabaikan, jadi sebaiknya Anda selalu menggunakan0
.Salah satu alasan Anda mungkin ingin menggunakan
z != 0
adalah membuat sprite menyembunyikan latar belakang dengan buffer kedalaman.Bantahan
glOrtho
tidak digunakan lagi sejak OpenGL 4.5 : profil kompatibilitas 12.1. "TRANSFORMASI VERTEX FUNGSI TETAP" berwarna merah.Jadi jangan gunakan untuk produksi. Bagaimanapun, memahaminya adalah cara yang baik untuk mendapatkan beberapa wawasan OpenGL.
Program OpenGL 4 modern menghitung matriks transformasi (yang kecil) pada CPU, dan kemudian memberikan matriks dan semua titik untuk diubah menjadi OpenGL, yang dapat melakukan ribuan perkalian matriks untuk berbagai titik dengan sangat cepat secara paralel.
Vertex shader yang ditulis secara manual kemudian melakukan perkalian secara eksplisit, biasanya dengan tipe data vektor yang sesuai dari OpenGL Shading Language.
Karena Anda menulis shader secara eksplisit, ini memungkinkan Anda menyesuaikan algoritme dengan kebutuhan Anda. Fleksibilitas semacam itu adalah fitur utama dari GPU yang lebih modern, yang tidak seperti yang lama yang melakukan algoritme tetap dengan beberapa parameter input, sekarang dapat melakukan penghitungan sewenang-wenang. Lihat juga: https://stackoverflow.com/a/36211337/895245
Dengan eksplisit
GLfloat transform[]
akan terlihat seperti ini: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 .
Kompilasi dan jalankan:
gcc -ggdb3 -O0 -o glfw_transform.out -std=c99 -Wall -Wextra -pedantic glfw_transform.c -lGL -lGLU -lglut -lGLEW -lglfw -lm ./glfw_transform.out
Keluaran:
Matriks untuk
glOrtho
sangat sederhana, hanya terdiri dari penskalaan dan terjemahan:seperti yang disebutkan dalam dokumen OpenGL 2 .
The
glFrustum
matriks tidak terlalu sulit untuk menghitung dengan tangan baik, tapi mulai mendapatkan menjengkelkan. Perhatikan bagaimana frustum tidak dapat dibuat hanya dengan penskalaan dan terjemahan sepertiglOrtho
, info lebih lanjut di: https://gamedev.stackexchange.com/a/118848/25171Pustaka matematika GLM OpenGL C ++ adalah pilihan populer untuk menghitung matriks semacam itu. http://glm.g-truc.net/0.9.2/api/a00245.html mendokumentasikan baik
ortho
danfrustum
operasi.sumber
common.h:19:23: error: ‘TIME_UTC’ undeclared (first use in this function) timespec_get(&ts, TIME_UTC);
Dokumentasi OpenGL (cetak tebal saya)
Angka-angka menentukan lokasi bidang kliping (kiri, kanan, bawah, atas, dekat dan jauh).
Proyeksi "normal" adalah proyeksi perspektif yang memberikan ilusi kedalaman. Wikipedia mendefinisikan proyeksi paralel sebagai:
sumber