Perbedaan dan hubungan antara glActiveTexture dan glBindTexture

137

Dari apa yang saya kumpulkan, glActiveTexturesetel "unit tekstur" aktif. Setiap unit tekstur dapat memiliki beberapa target tekstur (biasanya GL_TEXTURE_1D, 2D, 3D, atau CUBE_MAP).

Jika saya mengerti dengan benar, Anda harus memanggil glActiveTextureuntuk menyetel unit tekstur terlebih dahulu (diinisialisasi ke GL_TEXTURE0), dan kemudian Anda mengikat (satu atau lebih) "target tekstur" ke unit tekstur itu?

Jumlah unit tekstur yang tersedia bergantung pada sistem. Saya melihat enum hingga 32 di perpustakaan saya. Saya kira ini pada dasarnya berarti saya dapat memiliki batas GPU yang lebih rendah (yang menurut saya adalah168) dan 32 tekstur dalam memori GPU pada satu waktu? Saya kira ada batas tambahan yang saya tidak melebihi memori maksimum GPU saya (seharusnya 1 GB).

Apakah saya memahami hubungan antara target tekstur dan unit tekstur dengan benar? Katakanlah saya diizinkan masing-masing 16 unit dan 4 target, apakah itu berarti ada ruang untuk 16 * 4 = 64 target, atau apakah itu tidak berfungsi seperti itu?

Selanjutnya Anda biasanya ingin memuat tekstur. Anda dapat melakukannya melalui glTexImage2D. Argumen pertama adalah target tekstur. Jika ini berfungsi seperti ituglBufferData , maka pada dasarnya kita mengikat "pegangan" / "nama tekstur" ke target tekstur, lalu memuat data tekstur ke target itu, dan dengan demikian secara tidak langsung mengaitkannya dengan pegangan itu.

Tentang apa glTexParameter? Kami harus mengikat target tekstur, dan kemudian memilih target yang sama lagi sebagai argumen pertama? Atau apakah target tekstur tidak perlu terikat selama kita memiliki unit tekstur aktif yang benar?

glGenerateMipmap beroperasi pada target juga ... target itu harus tetap terikat pada nama tekstur agar berhasil?

Kemudian ketika kita ingin menggambar objek kita dengan tekstur pada itu, kita harus baik memilih unit tekstur yang aktif, dan kemudian target tekstur? Atau apakah kita memilih unit tekstur, dan kemudian kita dapat mengambil data dari salah satu dari 4 target yang terkait dengan unit itu? Ini adalah bagian yang sangat membingungkan saya.

mpen
sumber

Jawaban:

262

Semua Tentang Objek OpenGL

Model standar untuk objek OpenGL adalah sebagai berikut.

Objek memiliki status. Anggap saja sebagai struct. Jadi Anda mungkin memiliki objek yang didefinisikan seperti ini:

struct Object
{
    int count;
    float opacity;
    char *name;
};

Objek memiliki nilai-nilai tertentu yang disimpan di dalamnya dan memiliki status . Objek OpenGL juga memiliki status.

Mengubah Status

Dalam C / C ++, jika Anda memiliki instance tipe Object, Anda akan mengubah statusnya sebagai berikut: obj.count = 5;Anda akan langsung mereferensikan instance objek, mendapatkan bagian tertentu dari status yang ingin Anda ubah, dan memasukkan nilai ke dalamnya.

Di OpenGL, Anda tidak melakukan ini.

Untuk alasan warisan lebih baik tidak dijelaskan, untuk mengubah status objek OpenGL, Anda harus mengikatnya terlebih dahulu ke konteks. Ini dilakukan dengan beberapa dari glBind*panggilan.

C / C ++ yang setara dengan ini adalah sebagai berikut:

Object *g_objs[MAX_LOCATIONS] = {NULL};    
void BindObject(int loc, Object *obj)
{
  g_objs[loc] = obj;
}

Teksturnya menarik; mereka mewakili kasus khusus yang mengikat. Banyak glBind*panggilan memiliki parameter "target". Ini mewakili lokasi berbeda dalam konteks OpenGL di mana objek jenis itu bisa diikat. Misalnya, Anda bisa mengikat objek framebuffer untuk reading ( GL_READ_FRAMEBUFFER) atau untuk menulis ( GL_DRAW_FRAMEBUFFER). Ini memengaruhi cara OpenGL menggunakan buffer. Inilah yang locdiwakili oleh parameter di atas.

Tekstur itu istimewa karena saat Anda mengikatnya pertama kali ke target, tekstur itu mendapatkan informasi khusus. Saat Anda pertama kali mengikat tekstur sebagai a GL_TEXTURE_2D, Anda sebenarnya menyetel status khusus dalam tekstur. Anda mengatakan bahwa tekstur ini adalah tekstur 2D. Dan itu akan selalu menjadi tekstur 2D; keadaan ini tidak dapat diubah selamanya . Jika Anda memiliki tekstur yang pertama kali dijilid sebagai a GL_TEXTURE_2D, Anda harus selalu mengikatnya sebagai a GL_TEXTURE_2D; mencoba untuk mengikatnya karena GL_TEXTURE_1Dakan menimbulkan kesalahan (saat run-time).

Setelah objek diikat, statusnya dapat diubah. Ini dilakukan melalui fungsi generik khusus untuk objek itu. Mereka juga mengambil lokasi yang mewakili objek mana yang akan dimodifikasi.

Di C / C ++, ini terlihat seperti:

void ObjectParameteri(int loc, ObjectParameters eParam, int value)
{
  if(g_objs[loc] == NULL)
    return;

  switch(eParam)
  {
    case OBJECT_COUNT:
      g_objs[loc]->count = value;
      break;
    case OBJECT_OPACITY:
      g_objs[loc]->opacity = (float)value;
      break;
    default:
      //INVALID_ENUM error
      break;
  }
}

Perhatikan bagaimana fungsi ini mengatur apa pun yang terjadi pada locnilai yang saat ini terikat .

Untuk objek tekstur, fungsi perubahan status tekstur utama adalah glTexParameter. Satu-satunya fungsi lain yang mengubah status tekstur adalah glTexImagefungsi dan variasinya ( glCompressedTexImage,, glCopyTexImageterbaru glTexStorage). Berbagai SubImageversi mengubah isi dari tekstur, tetapi mereka tidak secara teknis mengubah nya negara . The Imagefungsi mengalokasikan penyimpanan tekstur dan mengatur format tekstur ini; yang SubImagefungsi hanya menyalin piksel sekitar. Itu tidak dianggap status tekstur.

Izinkan saya untuk mengulangi: ini adalah satu - satunya fungsi yang mengubah status tekstur. glTexEnvmengubah keadaan lingkungan; itu tidak mempengaruhi apa pun yang disimpan dalam objek tekstur.

Tekstur Aktif

Situasi tekstur lebih kompleks, sekali lagi untuk alasan warisan sebaiknya tidak diungkapkan. Di sinilah glActiveTexturemasuk.

Untuk tekstur, ada tidak hanya target ( GL_TEXTURE_1D, GL_TEXTURE_CUBE_MAP, dll). Ada juga unit tekstur . Dalam contoh C / C ++ kami, yang kami miliki adalah ini:

Object *g_objs[MAX_OBJECTS][MAX_LOCATIONS] = {NULL};
int g_currObject = 0;

void BindObject(int loc, Object *obj)
{
  g_objs[g_currObject][loc] = obj;
}

void ActiveObject(int currObject)
{
  g_currObject = currObject;
}

Perhatikan bahwa sekarang, kami tidak hanya memiliki daftar 2D Objects, tetapi kami juga memiliki konsep objek saat ini. Kami memiliki fungsi untuk mengatur objek saat ini, kami memiliki konsep jumlah maksimum objek saat ini, dan semua fungsi manipulasi objek kami disesuaikan untuk memilih dari objek saat ini.

Saat Anda mengubah objek yang saat ini aktif, Anda mengubah seluruh kumpulan lokasi target. Jadi Anda dapat mengikat sesuatu yang masuk ke objek saat ini 0, beralih ke objek 4 saat ini, dan akan memodifikasi objek yang sama sekali berbeda.

Analogi dengan objek tekstur ini sempurna ... hampir.

Lihat, glActiveTexturetidak mengambil integer; dibutuhkan pencacah . Yang secara teori berarti dapat mengambil apa saja dari GL_TEXTURE0hingga GL_TEXTURE31. Tetapi ada satu hal yang harus Anda pahami:

INI SALAH!

Jangkauan sebenarnya yang glActiveTexturedapat diambil diatur oleh GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS. Itu adalah jumlah maksimum multitekstur simultan yang memungkinkan penerapan. Masing-masing dibagi menjadi kelompok berbeda untuk tahapan shader yang berbeda. Misalnya, pada perangkat keras kelas GL 3.x, Anda mendapatkan 16 tekstur shader vertex, 16 tekstur shader fragmen, dan 16 tekstur shader geometri. Oleh karena itu, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITSakan menjadi 48.

Tapi tidak ada 48 pencacah. Itulah mengapa glActiveTexturetidak menerima pencacah. The benar cara untuk panggilan glActiveTextureadalah sebagai berikut:

glActiveTexture(GL_TEXTURE0 + i);

dimana iadalah angka antara 0 dan GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS.

Merender

Jadi, apa hubungan semua ini dengan rendering?

Saat menggunakan shader, Anda menyetel seragam sampler Anda ke unit gambar tekstur ( glUniform1i(samplerLoc, i), di mana iunit gambar). Itu mewakili nomor yang Anda gunakan glActiveTexture. Sampler akan memilih target berdasarkan jenis sampler. Jadi sampler2Dakan memilih dari GL_TEXTURE_2Dtarget. Inilah salah satu alasan mengapa sampler memiliki tipe yang berbeda.

Sekarang ini terdengar mencurigakan seperti Anda dapat memiliki dua sampler GLSL, dengan tipe berbeda yang menggunakan unit gambar tekstur yang sama. Tapi Anda tidak bisa; OpenGL melarang ini dan akan memberi Anda kesalahan saat Anda mencoba merender.

Nicol Bolas
sumber
12
Wow! Namun jawaban luar biasa lainnya - terima kasih Nicol! Saya terutama menyukai paragraf tentang tekstur 2D yang selalu menjadi tekstur 2D. Saya sedang membangun pembungkus di sekitar beberapa hal ini sekarang, dan saya tidak yakin apakah saya harus membiarkannya terbuka untuk berubah. Dan bagian tentang GL_TEXTURE0 + i- saya bermaksud memeriksa nilai enum untuk melihat apakah itu valid atau tidak. Dan paragraf terakhir - tidak tahu apakah itu legal atau tidak. Luar biasa! Saya menandai semua jawaban Anda sehingga saya dapat merujuknya lagi.
mpen
6
@Nicol Bolas: Ini dijelaskan dengan sangat baik. Anda harus menyalin sebagian dari ini ke bab tekstur di buku terbuka online Anda. Saya pikir ini jauh lebih jelas dan akan melengkapi bab ini dengan baik.
WesDec
3
@Nicol Bolas Saya baru mulai belajar OpenGL dan jawaban ini sangat membantu saya. Terima kasih!
sebaris
2
Hai nico, hanya ingin menunjukkan kesalahan ketik kecil Anda: GL_DRAW_FRAMEBUFFER bukan GL_WRITE_FRAMEBUFFER
Defd
3
@Nicol: Wow, definisi terbaik yang saya miliki tentang ini sebelumnya sekarang adalah dari tutorial arcsynthesis Anda, sekarang Anda bahkan telah mengalahkan sumber yang brilian itu. Terima kasih
Baggers
21

Saya akan mencobanya ! Semua ini tidak terlalu rumit, hanya masalah istilah, semoga saya bisa menjelaskan.


Anda dapat membuat Objek Tekstur kira-kira sebanyak yang tersedia memori di sistem Anda. Objek ini menyimpan data aktual (texel) tekstur Anda, bersama dengan parameter, yang disediakan oleh glTexParameter (lihat FAQ ).

Ketika sedang dibuat, Anda harus menetapkan satu Tekstur Sasaran untuk satu objek tekstur, yang merupakan jenis tekstur ( GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE, ...).

Kedua item ini, objek tekstur dan target tekstur merupakan data tekstur. Kami akan kembali lagi nanti.

Unit tekstur

Sekarang, OpenGL menyediakan larik unit tekstur , yang dapat digunakan secara bersamaan saat menggambar. Ukuran array bergantung pada sistem OpenGL, milik Anda memiliki 8.

Anda dapat mengikat objek tekstur ke unit tekstur untuk menggunakan tekstur yang diberikan saat menggambar.

Di dunia yang sederhana dan mudah, untuk menggambar dengan tekstur tertentu, Anda akan mengikat objek tekstur ke unit tekstur, dan Anda akan melakukannya (kodesemu):

glTextureUnit[0] = textureObject

Karena GL adalah mesin negara, sayangnya, GL tidak bekerja dengan cara ini. Misalkan kami textureObjectmemiliki data untuk GL_TEXTURE_2Dtarget tekstur, kami akan mengekspresikan tugas sebelumnya sebagai:

glActiveTexture(GL_TEXTURE0);                   // select slot 0 of the texture units array
glBindTexture(GL_TEXTURE_2D, textureObject);    // do the binding

Perhatikan bahwa GL_TEXTURE_2Dsangat tergantung pada jenis tekstur yang ingin Anda ikat.

Objek tekstur

Dalam pseudo code, untuk mengatur data tekstur atau parameter tekstur, Anda akan melakukan misalnya:

setTexData(textureObject, ...)
setTexParameter(textureObject, TEXTURE_MIN_FILTER, LINEAR)

OpenGL tidak dapat secara langsung memanipulasi objek tekstur, untuk memperbarui / menyetel kontennya, atau mengubah parameternya, Anda harus mengikatnya terlebih dahulu ke unit tekstur aktif (mana pun itu). Kode yang setara menjadi:

glBindTexture(GL_TEXTURE_2D, textureObject)       // this 'installs' textureObject in texture unit
glTexImage2D(GL_TEXTURE_2D, ...)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)

Naungan

Shader memiliki akses ke semua unit tekstur, mereka tidak peduli dengan tekstur aktif.

Seragam sampler adalah intnilai yang mewakili indeks unit tekstur yang akan digunakan untuk sampler (dan bukan objek tekstur yang akan digunakan).

Jadi, Anda harus mengikat objek tekstur ke unit yang ingin Anda gunakan.

Jenis sampler akan melakukan pencocokan dengan target tekstur yang digunakan dalam unit tekstur: Sampler2Duntuk GL_TEXTURE_2D, dan seterusnya ...

rotoglup
sumber
Satu hal yang saya tidak mengerti. Mari kita asumsikan saya memiliki beberapa tekstur dan digunakan di banyak shader pada unit tekstur yang berbeda. Mari kita asumsikan saya ingin mengubah pemfilteran tekstur saat dijalankan. Unit tekstur apa yang harus saya gunakan? Dapatkah saya mengubah status tekstur pada Unit 0 dan kemudian menggunakan tekstur tersebut pada unit yang berbeda?
majakthecoder
@majakthecoder Dalam jawaban saya, saya menganggap pemfilteran sebagai properti objek tekstur - yang berarti Anda tidak dapat mengubahnya secara khusus dalam unit tekstur. Bergantung pada jenis OpenGL yang Anda targetkan, Anda mungkin dapat mengambil sampel objek untuk memecahkan masalah ini ( opengl.org/wiki/Sampler_Object ), jika tidak, Anda mungkin harus menduplikasi objek tekstur, untuk mendapatkan beberapa pemfilteran secara bersamaan.
rotoglup
12

Bayangkan GPU seperti pabrik pemrosesan cat.

Ada sejumlah tangki, yang mengirimkan pewarna ke beberapa mesin pengecatan. Di mesin lukis pewarna kemudian dioleskan ke objek. Tangki tersebut adalah unit tekstur

Tangki tersebut dapat dilengkapi dengan berbagai jenis pewarna. Setiap jenis pewarna membutuhkan jenis pelarut lainnya. "Pelarut" adalah target tekstur . Untuk kenyamanan, setiap tangki dihubungkan ke beberapa pasokan pelarut, dan hanya satu jenis pelarut yang dapat digunakan di setiap tangki dalam satu waktu. Jadi ada katup / switch TEXTURE_CUBE_MAP, TEXTURE_3D, TEXTURE_2D, TEXTURE_1D. Anda dapat mengisi semua jenis pewarna ke dalam tangki pada saat yang sama, tetapi karena hanya satu jenis pelarut yang masuk, ini hanya akan "mengencerkan" jenis pewarna yang cocok. Jadi Anda dapat memiliki setiap jenis tekstur yang terikat, tetapi pengikatan dengan pelarut "terpenting" akan benar-benar masuk ke dalam tangki dan bercampur dengan jenis pewarna yang dimilikinya.

Lalu ada pewarna itu sendiri, yang berasal dari gudang dan diisi ke dalam tangki dengan "mengikatnya". Itu teksturmu.

datenwolf
sumber
2
Jenis analogi yang aneh ... Saya tidak yakin itu benar-benar menjelaskan semuanya. Terutama bagian tentang "pengenceran" dan "pelarut paling penting". Maksudnya jika saya mengikat tekstur 2d dan tekstur 3d, saya hanya dapat menggunakan salah satunya, atau apa? Manakah yang akan dianggap paling penting?
mpen
2
@ Mark: Ya, saya mencoba berbicara tentang seorang pelukis yang bekerja dengan pewarna literal (katakanlah berbasis minyak dan berbasis air). Bagaimanapun, ya jika Anda mengikat dan mengaktifkan beberapa target tekstur, ada yang diutamakan: CUBE_MAP> 3D> TEXTURE_ARRAY> 2D> 1D.
datenwolf
1
Rapi! Saya tidak tahu tentang yang diutamakan. Lebih masuk akal sekarang karena saya tahu hanya satu target tekstur yang dapat digunakan per unit tekstur.
mpen
1
@ legends2k: Nah, sekarang semakin menarik. Apakah kita berbicara tentang inti atau profil kompatibilitas. Apakah kita menganggap pengemudi ideal, atau buggy. Secara teori, jenis seragam memilih target mana dari unit tekstur yang akan dipilih. Dalam prakteknya ini terjadi pada profil inti. Dalam profil kompatibilitas, harapkan beberapa driver buggy menampilkan tekstur default putih semua jika target sebelumnya dari unit tekstur tidak cocok dengan jenis sampler.
datenwolf
1
@ legends2k: Juga, pikirkan tentang apa yang akan terjadi jika ada tekstur 2D dan 3D yang terikat pada unit yang sama dan Anda memiliki seragam sampler 2D dan 3D, yang Anda ikat ke unit yang sama? Anda dapat memicu semua jenis bug driver aneh dengan ini. Dalam praktiknya, berpikir dalam model prioritas fungsi tetap yang lama membuat pikiran Anda tetap waras dan program Anda berfungsi, karena begitulah cara sebagian besar pengemudi akan berperilaku dengan cara yang dapat diprediksi.
datenwolf
2

Jika di shader Anda, Anda memerlukan pencarian dari 2 tekstur:

uniform sampler2D tex1;  
uniform sampler2D tex2;  

untuk tex1 dan tex2 perlu disebutkan sumbernya sebagai berikut:

tex1 = gl.createTexture();  
gl.activeTexture(gl.TEXTURE3);  
gl.bindTexture(gl.TEXTURE_2D, tex1); 
gl.texParameteri(gl.TEXTURE_2D, ...);  
....


tex2 = gl.createTexture();  
gl.activeTexture(gl.TEXTURE7);  
gl.bindTexture(gl.TEXTURE_2D, tex2); 
gl.texParameteri(gl.TEXTURE_2D, ...);  
....  
var tex1Loc  = gl.getUniformLocation(your_shader,"tex1");  
var tex2Loc  = gl.getUniformLocation(your_shader,"tex2");

di loop render:

gl.uniform1i(tex1Loc, 3);  
gl.uniform1i(tex2Loc, 7);  
// but you can dynamically change these values

Dengan gl_bindtexture, tidak mungkin melakukan hal seperti itu. Di sisi lain, kemungkinan penggunaan pengikatan dalam loop rendering, adalah kasus di mana Anda memberi makan tekstur dengan konten dalam aliran (video, webcam):

gl.bindTexture(gl.TEXTURE_2D, tex1);  
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video);  
// in the render loop
Philippe Oceangermanique
sumber