Bagaimana cara menggunakan glOrtho () di OpenGL?

88

Saya tidak mengerti penggunaan glOrtho. Adakah yang bisa menjelaskan kegunaannya?

Apakah digunakan untuk mengatur kisaran batas koordinat xy dan z?

glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);

Artinya rentang x, y, dan z adalah dari -1 hingga 1?

ufk
sumber
1
Video ini sangat membantu saya.
ViniciusArruda

Jawaban:

155

Lihat gambar ini: Proyeksi Grafis masukkan deskripsi gambar di sini

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:

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.

Mikepote
sumber
91
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.
basickarl
1
@Kari, Semoga link ini bisa membantu. > learnopengl.com/#!In-Practice/2D-Game/Rendering-Sprites
huahsin68
1
@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:

masukkan deskripsi gambar di sini

glFrustrum: lebih nyata seperti 3D, objek identik yang jauh tampak lebih kecil:

masukkan deskripsi gambar di sini

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:

./main 1

Jalankan dengan glFrustrum:

./main

Diuji di Ubuntu 18.10.

Skema

Ortho: kamera adalah bidang, volume terlihat persegi panjang:

masukkan deskripsi gambar di sini

Frustrum: kamera adalah sebuah titik, volume yang terlihat adalah sepotong piramida:

masukkan deskripsi gambar di sini

Sumber gambar .

Parameter

Kami selalu melihat dari + z ke -z dengan + y ke atas:

glOrtho(left, right, bottom, top, near, far)
  • left: minimal xkita lihat
  • right: maksimal yang xkami lihat
  • bottom: minimal ykita lihat
  • top: maksimal yang ykami lihat
  • -near: minimal zkita lihat. Ya , ini -1saatnya near. Jadi masukan negatif berarti positif z.
  • -far: maksimal yang zkami 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 glOrthoatau glFrustrum, itulah yang kita dapatkan.

glOrthodan glFrustrumhanya transformasi linier (perkalian matriks AKA) seperti itu:

  • glOrtho: mengambil persegi 3D tertentu ke dalam kubus default
  • glFrustrum: mengambil bagian piramida tertentu ke dalam kubus default

Transformasi ini kemudian diterapkan ke semua simpul. Inilah yang saya maksud dalam 2D:

Sumber gambar .

Langkah terakhir setelah transformasi sederhana:

  • 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:

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:

masukkan deskripsi gambar di sini

Matriks untuk glOrthosangat sederhana, hanya terdiri dari penskalaan dan terjemahan:

scalex, 0,      0,      translatex,
0,      scaley, 0,      translatey,
0,      0,      scalez, translatez,
0,      0,      0,      1

seperti yang disebutkan dalam dokumen OpenGL 2 .

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.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
1
"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:

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:

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".

ChrisF
sumber
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
Ben Voigt