Kebingungan tentang GLViewport

9

Saya berharap seseorang dapat membantu saya memahami GLViewport dan apa yang terjadi ketika kami mengubah ukurannya

Ini akan menggambarkan kebingungan saya ....

masukkan deskripsi gambar di sini

Jadi, di sini saya punya quad yang macet di tengah layar. Jika saya memiliki GLViewport saya cocok dengan lebar dan tinggi perangkat, saya mendapatkan apa yang ada di gambar pertama (kiri). Persis seperti yang saya harapkan.

  • Resolusi perangkat, 2560 x 1600
  • Resolusi viewport 2560 x 1600
  • Ukuran quad 200 x 200 (Catatan, gambar di atas bukan untuk skala !!! :-))
  • Bentuk quad, muncul sebagai kotak

Sekarang, untuk gambar ke-2 (kanan) ...

  • Resolusi perangkat, 2560 x 1600
  • Resolusi viewport 2560 x 1200 (dan berpusat vertikal)
  • Ukuran quad (200, 200)
  • Bentuk quad, muncul sebagai persegi panjang

Pertanyaan saya adalah, mengapa quad ditampilkan sebagai persegi panjang sekarang dan bukan persegi? Saya telah mengkonfirmasi dengan mencatat bahwa quad saya berukuran 200 x 200 piksel - tentunya ukuran piksel fisiknya tetap sama? Mereka tidak bisa berubah. Jadi apa yang terjadi di sini?

Saya pikir (jelas salah) bahwa ketika saya memperbesar viewport, itu benar-benar memotong piksel.

Akan sangat menghargai jika seseorang dapat menjelaskan cara kerjanya.

Edit

Saat ini, saya mengatur viewport saya seperti ini:

width = (int) Math.min(deviceWidth, deviceHeight * 1.702127659574468);    
height = (int) Math.min(deviceHeight, deviceWidth / 1.702127659574468);

ratio = width / height;
GLES20.glViewport(offsetX, offsetY, width, height);

Matrix.orthoM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);

offsetX dan offsetY hanya agar ketika ada kotak surat, viewport terpusat.

BungleBonce
sumber
Apakah Anda mengubah matriks proyeksi dan viewport saat mengubah dari 1600 menjadi 1200 piksel vertikal? (Anda perlu)
bcrist
Hai @ bcrist, silakan lihat edit saya untuk menunjukkan bagaimana saya mengatur viewport saya. Semua yang saya gambar yang memiliki jumlah piksel yang sama (katakan 50 x 50, atau 100 x 100) menggambar 'memanjang' - Adakah gagasan ?!
BungleBonce
BTW, jika Anda menggunakan glScissorbukan glViewport, itu hanya akan memotong piksel tanpa mengubah apa pun yang diberikan. glViewportberperilaku lebih seperti mengubah ukuran gambar asli, daripada memotongnya; itu sebabnya itu meremas kotak Anda.
Nathan Reed
Terima kasih @NathanReed, saya masih sangat bingung. Apa yang saya miliki adalah ini: Sebuah viewport yang membawa seluruh layar ke atas. Sebuah kotak gunting yang diskalakan untuk menjaga rasio perangkat asli (jadi saya bisa menampilkan permainan di kotak gunting dan menggambar hal-hal di luar ini untuk pengisi), semuanya tampak membentang (vertikal), jadi lebih panjang daripada lebar. Apa yang saya lakukan salah?
BungleBonce
Hmm. Scissor rect seharusnya tidak mengubah apa pun tentang rasio aspek atau penskalaan gambar. Jika terlihat benar tanpa gunting, maka hanya mengaktifkan gunting - menjaga viewport yang sama, matriks proyeksi, dll. - cukup memotong gambar.
Nathan Reed

Jawaban:

11

Untuk memahami apa yang terjadi, Anda harus memahami jalur render:

Geometri Anda (quad) awalnya didefinisikan dalam ruang dunia, anggap ini sebagai beberapa sistem koordinat global. Di dalam vertex shader yang ditransformasikan menjadi koordinat perangkat dinormalisasi (NDC), sistem koordinat virtual didefinisikan demikian, sehingga semuanya mulai -1 hingga 1 akan tertarik ke layar. Perhatikan, NDC berkisar dari -1 hingga 1 di X dan Y, dan itu benar-benar independen dari rasio aspek perangkat dan resolusi. Transformasi dari ruang dunia ke NDC dilakukan oleh model, view, dan matrix proyeksi (dalam bentuk sederhana, hanya satu matriks untuk semuanya atau geometri bahkan didefinisikan dalam NDC untuk memulainya!).

Unit rasterisasi perlu mengetahui di mana dan seberapa besar raster seharusnya dan ini adalah apa yang Anda definisikan dengan panggilan glViewport: di mana memulainya adalah dua parameter pertama, ukurannya adalah dua yang kedua. Perangkat keras kemudian akan mengkonversi dari koordinat NDC ke piksel dengan skala dan pergeseran sederhana - dan inilah yang Anda lihat dalam contoh Anda: skala pada sumbu Y.

Jadi untuk memastikan quad Anda dirender dalam rasio aspek yang benar, Anda juga perlu menyesuaikan matriks proyeksi untuk memasukkan rasio aspek yang diharapkan dari panggilan glViewport.

Robert
sumber
Hi @Robert, terima kasih banyak atas ini, saya mengalami kesulitan mencoba menerapkan ini, dapatkah Anda memberikan contoh pengaturan matriks proyeksi dalam jawaban Anda? Terima kasih!
BungleBonce
4
Halo pengguna22241, tidak ada perintah OpenGL untuk melakukan ini untuk Anda, Anda perlu mendefinisikan matriks sendiri (array 4 dengan 4 float) dan memberikannya ke vertex shader sebagai seragam. Proyeksi apa yang Anda butuhkan (perspektif, ortogonal) tergantung pada apa yang ingin Anda lakukan. Jika konsep matematika dari proyeksi 3D baru bagi Anda, Anda mungkin ingin mencari tutorial OpenGL yang mengajarkan profil inti yang berfungsi sama dengan OpenGL ES dalam hal ini.
Robert
@BungleBonce sementara tidak ada perintah OpenGL untuk ini di profil inti, Anda selalu dapat melihat dokumentasi dari perintah usang seperti glFrustum, yang tidak mencantumkan matriks persis yang dibangun oleh fungsi-fungsi ini. Matriks proyeksi umum lainnya yang Anda inginkan dibuat oleh glOrtho. Sementara semua perintah ini sudah usang, dokumen pada mereka adalah sumber yang bagus untuk beberapa matematika yang diturunkan.
Ruslan
0

Dalam panggilan Anda ke glViewport Anda menentukan lebih sedikit piksel dalam lebar dan tinggi. Matriks proyeksi Anda didefinisikan dalam bentuk piksel. Itu sebabnya Anda harus menghitung matriks proyeksi baru setelah panggilan glViewport.

bogglez
sumber
Hai @bogglez, terima kasih atas jawaban Anda. Saya telah mengedit pertanyaan saya untuk menunjukkan (pada akhirnya) kode apa yang saya gunakan untuk viewport saya. Yang saya gunakan hanyalah glViewPort - apakah ini cara yang salah untuk melakukan ini? Bisakah Anda menunjukkan contoh apa yang Anda maksud dalam jawaban Anda? Terima kasih.
BungleBonce
Baca ini: songho.ca/opengl/gl_projectionmatrix.html Untuk 2D Anda menginginkan matriks proyeksi ortogonal, untuk 3D Anda mungkin menginginkan matriks perspektif. Jika Anda menggunakan OpenGL modern, Anda akan menyediakan matriks ke shader. Jika Anda menggunakan OpenGL yang sudah tidak digunakan lagi dengan pipeline grafik yang tetap, Anda akan menggunakan fungsi-fungsi seperti glLoadMatrix, glFrustum, dll. Untuk menghitung matriks proyeksi. Pustaka utilitas GLU memiliki fungsi gluPerspective ( opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml ) dan gluOrtho2D ( opengl.org/sdk/docs/man2/xhtml/gluOrtho2D.xml ) untuk membantu Anda dengan itu.
bogglez
0

Untuk gambar pertama:

  1. Kami membuat matriks proyeksi. Sebagai contoh, parameter yang digunakan adalah: Left1, Right1, Bottom1, Top1, Near1, Far1. Di sini, Aspect_Ratio_1A = (Kanan1 - Kiri1) / (Top1 - Bottom1);

  2. Kemudian kami melakukan transformasi view port. Sebagai contoh, parameter yang digunakan adalah: Width1, Height1. (Saya tidak menyebutkan parameter shift untuk kesederhanaan). Di sini, Aspect_Ratio_1B = Width1 / Height1;

Untuk gambar kedua:

  1. Kami membuat matriks proyeksi yang sama dengan parameter yang sama seperti sebelumnya. Jadi: Aspect_Ratio_2A = Aspect_Ratio_1A;

  2. Kali ini transformasi port tampilan memiliki parameter yang berbeda: Width2 dan Height2. Di sini, Aspect_Ratio_2B = Width2 / Height2.

Kami perhatikan bahwa Aspect_Ratio_1B dan Aspect_Ratio_2B berbeda. Secara khusus mereka adalah:

Aspect_Ratio_1B = 2560/1600 = 1,6 Aspect_Ratio_2B = 2560/1200 = 2.13

Jadi, jika kita meringkas apa yang kita lakukan untuk kasus kedua adalah:

Kami memproyeksikan gambar ke pesawat dengan Aspect_Ratio_2A dan sebelum menunjukkannya kepada pengguna, kami menskalakannya ke Aspect_Ratio_2B. Pengguna lain meminta kami untuk mengoreksi matriks proyeksi kami sedemikian rupa sehingga Aspect_Ratio_2A = Aspect_Ratio_2B.

Kita juga dapat membaca komentar Robert beberapa kali untuk memiliki pemahaman yang lebih baik.

Juga, saya tidak bisa setuju bahwa kuad merah di gambar kedua memiliki ukuran 200 x 200 piksel ketika digambar di layar. Karena kita tahu, sebuah pixel adalah bujur sangkar, jika quad memiliki salah satu sisi lebih panjang dari yang lain, sisi yang lebih panjang membutuhkan lebih banyak pixel untuk digambar pasti. Saya, secara pribadi, tidak tahu apa itu logging, tetapi mungkin itu tidak mengembalikan ukuran piksel layar.

Jamil
sumber