GlActiveTexture ve glBindTexture arasındaki farklar ve ilişki


137

Topladığımdan glActiveTextureaktif "doku birimini" ayarlar. Her doku biriminin birden fazla doku hedefi olabilir (genellikle GL_TEXTURE_1D, 2D, 3D veya CUBE_MAP).

Doğru anlarsam, glActiveTextureönce doku birimini ayarlamak için (başlangıç ​​olarak ayarlanmış) çağırmanız GL_TEXTURE0ve daha sonra "doku hedeflerini" o doku birimine bağlamanız gerekir mi?

Mevcut doku birimi sayısı sisteme bağlıdır. Kütüphanemde 32'ye kadar numaralandırma görüyorum. Sanırım bu aslında GPU sınırımdan daha azına sahip olabileceğim anlamına geliyor (ki bence168) ve 32 doku herhangi bir anda GPU bellek? Sanırım GPU'mun maksimum hafızasını (sözde 1 GB) aşmadığım ek bir sınır var.

Doku hedefleri ve doku birimleri arasındaki ilişkiyi doğru anlıyor muyum? Her biri 16 birim ve 4 hedefe izin verildiğimi varsayalım, bu 16 * 4 = 64 hedef için yer olduğu anlamına mı geliyor yoksa böyle çalışmıyor mu?

Sonra genellikle bir doku yüklemek istiyorsunuz. Bunu ile yapabilirsiniz glTexImage2D. İlk argümanı bir doku hedefi. Bu şekilde çalışıyorsaglBufferData , "tutamaç" / "doku adı" nı doku hedefine bağlarız ve daha sonra doku verilerini bu hedefe yükleriz ve dolaylı olarak bu tutamaçla ilişkilendiririz.

Ne olmuş glTexParameter? Bir doku hedefini bağlamalıyız ve sonra ilk argümanla aynı hedefi tekrar mı seçmeliyiz? Veya doğru aktif doku birimine sahip olduğumuz sürece doku hedefinin bağlanması gerekmez mi?

glGenerateMipmap bir hedef üzerinde de çalışır ... bu hedefin başarılı olması için doku adına bağlı olması gerekir mi?

O zaman nesnemizi üzerinde bir doku ile çizmek istediğimizde, hem aktif bir doku birimi, hem de bir doku hedefi seçmeliyiz? Yoksa bir doku birimi mi seçiyoruz ve o birim ile ilişkili 4 hedefin herhangi birinden veri alabilir miyiz? Bu beni gerçekten şaşırtan kısım.

Yanıtlar:


259

OpenGL Nesneleri Hakkında Her Şey

OpenGL nesneleri için standart model aşağıdaki gibidir.

Nesnelerin durumu vardır. Onları bir olarak düşünün struct. Yani şöyle tanımlanmış bir nesneniz olabilir:

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

Nesnenin içinde belirli değerler vardır ve durumu vardır . OpenGL nesnelerinin durumu da vardır.

Değişen Devlet

C / C ++ 'da, bir tür örneğiniz Objectvarsa, durumunu aşağıdaki gibi değiştirirsiniz: obj.count = 5;Nesnenin bir örneğine doğrudan başvurur, değiştirmek istediğiniz belirli bir durum parçasını alırsınız ve buna bir değer itersiniz.

OpenGL'de bunu yapmazsınız .

Açıklanamayan daha iyi eski nedenlerden ötürü, bir OpenGL nesnesinin durumunu değiştirmek için önce onu bağlama bağlamalısınız . Bu, bazı glBind*çağrılarla yapılır.

Buna C / C ++ eşdeğeri şöyledir:

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

Dokular ilginç; özel bir bağlayıcı vakayı temsil ederler. Birçok glBind*çağrının bir "hedef" parametresi vardır. Bu, OpenGL bağlamında bu tür nesnelerin bağlanabileceği farklı konumları temsil eder. Örneğin, bir framebuffer nesnesini okuma ( GL_READ_FRAMEBUFFER) veya yazma ( GL_DRAW_FRAMEBUFFER) için bağlayabilirsiniz . Bu, OpenGL'nin arabelleği nasıl kullandığını etkiler. Bu nedir locparametre yukarıda temsil eder.

Dokular özeldir, çünkü onları bir hedefe ilk bağladığınızda, özel bilgiler alırlar. Bir dokuyu ilk olarak bir olarak bağladığınızda GL_TEXTURE_2D, aslında dokuda özel bir durum ayarlarsınız. Bu dokunun 2B bir doku olduğunu söylüyorsunuz. Ve her zaman 2B bir doku olacak; bu durum asla değiştirilemez . İlk önce a olarak bağlanmış bir dokuya GL_TEXTURE_2Dsahipseniz, onu her zaman a olarak bağlamanız gerekir GL_TEXTURE_2D; GL_TEXTURE_1D(çalışma zamanı sırasında) bir hataya neden olacağı için onu ciltlemeye çalışmak .

Nesne bağlandıktan sonra durumu değiştirilebilir. Bu, o nesneye özgü genel işlevler aracılığıyla yapılır. Onlar da hangi nesnenin değiştirileceğini temsil eden bir konum alırlar.

C / C ++ 'da bu şöyle görünür:

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;
  }
}

Bu işlevin o anda bağlı olan locdeğerde ne olacağını nasıl ayarladığına dikkat edin .

Doku nesneleri için, ana doku durumu değiştirme işlevleri vardır glTexParameter. Doku durumunu değiştiren diğer işlevler yalnızca glTexImageişlevler ve bunların varyasyonlarıdır ( glCompressedTexImage, glCopyTexImageson glTexStorage). Çeşitli SubImageversiyonlar dokunun içeriğini değiştirir, ancak teknik olarak durumunu değiştirmez . ImageFonksiyonları doku depolama tahsis ve Doku biçimini ayarlamak; SubImagefonksiyonlar sadece pikselleri etrafında kopyalayın. Bu, dokunun durumu olarak kabul edilmez.

Tekrarlamama izin verin: bunlar doku durumunu değiştiren tek işlevlerdir. glTexEnvçevre durumunu değiştirir; doku nesnelerinde depolanan hiçbir şeyi etkilemez.

Aktif Doku

Dokular için durum daha karmaşıktır, yine eski nedenlerden dolayı açıklanamayan en iyi nedenlerdir. Burası devreye glActiveTexturegiriyor.

Dokular için, sadece hedefler (orada değil GL_TEXTURE_1D, GL_TEXTURE_CUBE_MAPvs). Ayrıca doku birimleri de vardır . C / C ++ örneğimiz açısından, sahip olduğumuz şey şudur:

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;
}

Şimdi, sadece 2 boyutlu bir Objects listemiz değil, aynı zamanda mevcut bir nesne kavramımızın da olduğuna dikkat edin . Mevcut nesneyi ayarlamak için bir fonksiyonumuz var, maksimum sayıda mevcut nesne konseptimiz var ve tüm nesne manipülasyon fonksiyonlarımız mevcut nesneden seçim yapacak şekilde ayarlandı.

Geçerli olarak etkin nesneyi değiştirdiğinizde, hedef konumların tamamını değiştirirsiniz. Böylece mevcut nesneye 0 giden bir şeyi bağlayabilir, mevcut nesneye 4 geçebilir ve tamamen farklı bir nesneyi değiştirebilirsiniz.

Doku nesneleriyle bu benzetme mükemmel ... neredeyse.

Bakın, glActiveTexturebir tamsayı almaz; bir numaralandırıcı alır . O bir şey alabilir teorisi araçlarının hangi GL_TEXTURE0için GL_TEXTURE31. Ama anlamanız gereken bir şey var:

BU YANLIŞ!

Olabilecek gerçek menzil glActiveTexturetarafından yönetilir GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS. Bu, bir uygulamanın izin verdiği maksimum eşzamanlı çoklu doku sayısıdır. Bunların her biri farklı gölgelendirici aşamaları için farklı gruplara ayrılmıştır. Örneğin, GL 3.x sınıfı donanımda, 16 köşe gölgelendirici dokusu, 16 parça gölgelendirici dokusu ve 16 geometri gölgelendirici dokusu elde edersiniz. Bu nedenle, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS48 olacak.

Ama 48 numaracı yok. Bu yüzden glActiveTexturegerçekten sayıcılar almıyor. Doğru çağrıya yolu glActiveTextureaşağıdaki gibidir:

glActiveTexture(GL_TEXTURE0 + i);

burada i0 ile arasında bir sayıdır GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS.

sıva

Peki tüm bunların render ile ne ilgisi var?

Gölgelendiricileri kullanırken, örnekleyici üniformalarınızı bir doku görüntü birimine (görüntü birimi glUniform1i(samplerLoc, i)nerede i) ayarlarsınız . Bu, kullandığınız sayıyı temsil eder glActiveTexture. Örnekleyici hedefi örnekleyici türüne göre seçecektir. Yani hedeften bir sampler2Dseçim yapacak GL_TEXTURE_2D. Örnekleyicilerin farklı tiplere sahip olmasının bir nedeni budur.

Şimdi bu , aynı doku görüntü birimini kullanan farklı türlerde iki GLSL örnekleyiciye sahip olabileceğiniz gibi kuşkuyla geliyor . Ama yapamazsın; OpenGL bunu yasaklar ve oluşturmaya çalıştığınızda size bir hata verir.


12
Vaov! Yine harika bir cevap - teşekkürler Nicol! Özellikle 2D bir doku her zaman 2D bir doku olmakla ilgili paragrafı seviyorum . Şimdi bunlardan bazılarına bir paket yapıyorum ve değişime açık bırakıp bırakmamam gerektiğinden emin değildim. Ve ilgili bölüm GL_TEXTURE0 + i- bunun geçerli olup olmadığını görmek için enum değerlerini incelemek demekti. Ve son paragraf - bunun yasal olup olmadığını bilmiyordum. Mükemmel! Tüm yanıtlarınızı yer imlerine ekliyorum, böylece tekrar başvurabilirim.
2'de mpen

6
@Nicol Bolas: Bu gerçekten çok iyi açıklanmış. Bunlardan bazılarını çevrimiçi opengl kitabınızdaki doku bölümüne kopyalamanız gerekir. Bence bu çok daha açık ve bölüme iyi bir iltifat.
WesDec

3
@Nicol Bolas OpenGL öğrenmeye başladım ve bu cevap bana çok yardımcı oldu. Teşekkür ederim!
satır içi

2
Hey nico, sadece küçük yazımını belirtmek istiyorum
GL_DRAW_FRAMEBUFFER

3
@Nicol: Vay be, bundan önce sahip olduğum en iyi tanım, arsentez öğreticilerinizdi, şimdi bu parlak kaynağı bile geçtiniz. Teşekkür
Baggers

20

Bir deneyeceğim! Bütün bunlar o kadar karmaşık değil, sadece bir terimler meselesi, umarım kendimi açıklığa kavuşturacağım.


Kabaca sisteminizde kullanılabilir bellek olduğu kadar çok Doku Nesnesi oluşturabilirsiniz . Bu nesneler tarafından sağlanan parametreleri ile birlikte, dokuların gerçek verileri (teksel) tutun glTexParameter (bkz SSS ).

Oluşturulan zaman, bir atamak zorunda Doku Target doku tipini temsil etmektedir doku nesnesine ( GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE, ...).

Bu iki öğe, doku nesnesi ve doku hedefi doku verilerini temsil eder. Onlara daha sonra geri döneceğiz.

Doku birimleri

Şimdi, OpenGL çizim sırasında aynı anda kullanılabilecek bir dizi doku birimi sunmaktadır . Dizinin boyutu OpenGL sistemine bağlıdır, sizinki 8'dir.

Sen edebilirsiniz bağlamak çizerken verilen doku kullanmak için bir doku birimine bir doku nesnesi.

Basit ve kolay bir dünyada, belirli bir doku ile çizim yapmak için, bir doku nesnesini doku birimine bağlarsınız ve şunları yaparsınız (sözde kod):

glTextureUnit[0] = textureObject

GL bir durum makinesi olduğundan, ne yazık ki, bu şekilde çalışmaz. Doku hedefimiz textureObjectiçin veriler bulunduğunu varsayarsak GL_TEXTURE_2D, önceki atamayı şu şekilde ifade ederiz:

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

GL_TEXTURE_2DGerçekten bağlamak istediğiniz dokunun türüne bağlı olduğunu unutmayın .

Doku nesneleri

Sahte kodda, doku verilerini veya doku parametrelerini ayarlamak için aşağıdakileri yaparsınız:

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

OpenGL, doku nesnelerini doğrudan manipüle edemez, içeriğini güncellemek / ayarlamak veya parametrelerini değiştirmek için, önce bunları aktif doku birimine (hangisi olursa olsun) bağlamanız gerekir. Eşdeğer kod şöyle olur:

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)

Shader

Gölgelendiricilerin tüm doku birimlerine erişimleri vardır, aktif dokuları umursamazlar.

Örnekleyici üniformaları int, örnekleyici için kullanılacak doku biriminin dizinini temsil eden değerlerdir ( kullanılacak doku nesnesini değil ).

Bu nedenle doku nesnelerinizi kullanmak istediğiniz birimlere bağlamanız gerekir.

Örnekleyicinin türü, doku biriminde kullanılan doku hedefi ile eşleşir: Sampler2Diçin GL_TEXTURE_2D, vb.


Anlamadığım bir şey var. Bazı dokularım olduğunu ve farklı doku birimlerinde birçok gölgelendiricide kullanıldığını varsayalım. Çalışma zamanında doku filtrelemesini değiştirmek istediğimi varsayalım. Hangi doku birimini kullanmalıyım? Ünite 0'daki doku durumunu değiştirebilir ve ardından bu dokuyu farklı ünitede kullanabilir miyim?
majakthecoder

@majakthecoder Cevabımda, filtrelemeyi doku nesnesinin bir özelliği olarak görüyorum - bu, özellikle bir doku biriminde değiştiremeyeceğiniz anlamına geliyor. Hedeflediğiniz OpenGL aromasına bağlı olarak, bu sorunu çözmek için nesneleri örnekleyebilirsiniz ( opengl.org/wiki/Sampler_Object ), aksi takdirde, birden fazla eşzamanlı filtreleme için doku nesnesini çoğaltmanız gerekebilir.
rotoglup

12

GPU'yu bazı boya işleme tesisleri gibi düşünün.

Bazı boyama makinelerine boya veren bir dizi tank vardır. Boyama makinesinde boya daha sonra nesneye uygulanır. Bu tanklar doku birimleridir

Bu tanklar farklı tipte boyalarla donatılabilir. Her bir boya türü başka bir tür çözücü gerektirir. "Çözücü" doku hedefidir . Kolaylık için her bir tank bir miktar çözücü kaynağına bağlanır ve her bir tankta aynı anda sadece bir tür çözücü kullanılabilir. Yani bir valf / anahtar var TEXTURE_CUBE_MAP, TEXTURE_3D, TEXTURE_2D, TEXTURE_1D. Tüm boya tiplerini aynı anda depoya doldurabilirsiniz, ancak sadece bir tür çözücü girdiği için sadece boya eşleştirmesini "seyreltir". Böylece her türlü dokuya bağlanabilirsiniz, ancak "en önemli" çözücü ile bağlanma aslında tanka girecek ve ait olduğu boya türüyle karışacaktır.

Ve sonra bir depodan gelen ve onu "bağlayarak" tanka doldurulan boyanın kendisi var. Doku sizin.


2
Biraz garip bir benzetme ... Gerçekten bir şeyi temizlediğinden emin değilim. Özellikle "seyreltme" ve "en önemli çözücü" ile ilgili kısım. Hem 2d dokuyu hem de 3d dokuyu bağlarsam, bunlardan sadece birini kullanabilir miyim diyorsun? Hangisi en önemli kabul edilir?
12'de

2
@ Mark: Gerçekten boyayla çalışan bir ressam açısından konuşmaya çalışıyordum (yağ bazlı ve su bazlı diyelim). Her neyse, evet birden fazla doku hedefini bağlar ve etkinleştirirseniz öncelik vardır: CUBE_MAP> 3D> TEXTURE_ARRAY> 2D> 1D.
datenwolf

1
Temiz! Önceliği bilmiyordum. Doku birimi başına yalnızca bir doku hedefinin kullanılabileceğini biliyorum.
mpen

1
@ legends2k: Şimdi ilginç hale geliyor. Çekirdek veya uyumluluk profilinden mi bahsediyoruz? İdeal ya da buggy sürücüleri var mı? Teoride, üniformanın tipi, doku biriminin hangi hedefini seçeceğini seçer. Uygulamada bu çekirdek profilde gerçekleşir. Uyumluluk profilinde, doku biriminin önceki hedefi örnekleyicinin türüyle eşleşmiyorsa, bazı buggy sürücülerin size tüm beyaz varsayılan dokuyu sunmasını bekler.
datenwolf

1
@ legends2k: Ayrıca, aynı birime bağlı bir 2B ve 3B doku varsa ve aynı birime bağladığınız bir 2B ve 3B örnekleyici üniformanız varsa ne olacağını düşünün? Bununla her türlü garip sürücü hatasını tetikleyebilirsiniz. Uygulamada, eski sabit işlev öncelik modelinde düşünmek zihninizi aklınızdan çıkarmayın ve programınızın çalışmasını sağlar, çünkü çoğu sürücünün öngörülebilir şekilde davranması bu şekilde olur.
datenwolf

2

Gölgelendiricinizde 2 dokudan aramaya ihtiyacınız varsa:

uniform sampler2D tex1;  
uniform sampler2D tex2;  

tex1 ve tex2 için kaynaklarını aşağıdaki gibi belirtmek gerekir:

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");

render döngüsünde:

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

Bir gl_bindtexture ile böyle bir şey yapmak mümkün değildir. Öte yandan, oluşturma döngüsünde bir bağlamanın olası kullanımı, akıştaki bir içerikle (video, web kamerası) bir doku beslediğiniz durumdur:

gl.bindTexture(gl.TEXTURE_2D, tex1);  
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video);  
// in the render loop
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.