glActiveTextureとglBindTextureの違いと関係


137

私が集めたものからglActiveTexture、アクティブな「テクスチャユニット」を設定します。各テクスチャユニットは複数のテクスチャターゲットを持つことができます(通常はGL_TEXTURE_1D、2D、3D、またはCUBE_MAP)。

私が正しく理解している場合、glActiveTexture最初にテクスチャユニットを設定するために(初期化してGL_TEXTURE0)呼び出す必要があり、次に(1つ以上の)「テクスチャターゲット」をそのテクスチャユニットにバインドしますか?

使用可能なテクスチャユニットの数はシステムに依存します。ライブラリに最大32個の列挙型が表示されます。これは本質的に、GPUの制限を小さくできることを意味します(これは168)およびGPUメモリ内の32のテクスチャーを同時に使用しますか?GPUの最大メモリ(おそらく1 GB)を超えないように、追加の制限があると思います。

テクスチャターゲットとテクスチャユニットの関係を正しく理解していますか?たとえば、16ユニットとそれぞれ4つのターゲットが許可されているとしましょう。それは、16 * 4 = 64ターゲットの余地があることを意味しますか、それともそのように機能しませんか?

次に、通常はテクスチャをロードします。これはを介して行うことができますglTexImage2D。最初の引数はテクスチャターゲットです。これがのようglBufferData機能する場合、基本的に「ハンドル」/「テクスチャ名」をテクスチャターゲットにバインドし、テクスチャデータをそのターゲットにロードして、間接的にそれをそのハンドルに関連付けます。

どうglTexParameterですか?テクスチャターゲットをバインドしてから、同じターゲットを最初の引数として再度選択する必要がありますか?または、正しいアクティブなテクスチャユニットがある限り、テクスチャターゲットをバインドする必要はありませんか?

glGenerateMipmap ターゲットでも動作します...そのターゲットを成功させるには、そのターゲットをテクスチャ名にバインドする必要がありますか?

次に、テクスチャのあるオブジェクトを描画する場合、アクティブなテクスチャユニットとテクスチャターゲットの両方を選択する必要がありますか?または、テクスチャユニットを選択してから、そのユニットに関連付けられた4つのターゲットのいずれかからデータを取得できますか?これは本当に私を混乱させている部分です。

回答:


259

OpenGLオブジェクトのすべて

OpenGLオブジェクトの標準モデルは次のとおりです。

オブジェクトには状態があります。それらをと考えてくださいstruct。したがって、次のように定義されたオブジェクトがあるとします。

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

オブジェクトには特定の値が格納されており、状態があります。OpenGLオブジェクトにも状態があります。

状態の変化

C / C ++では、タイプのインスタンスがある場合、Objectその状態を次のように変更します。obj.count = 5;オブジェクトのインスタンスを直接参照し、変更する特定の状態を取得して、値をそれに押し込みます。

OpenGLでは、これを行う必要はありません。

従来の理由から、説明を残さない方がよいです。OpenGLオブジェクトの状態を変更するには、まずそれをコンテキストにバインドする必要があります。これは、いくつかのfrom of glBind*コールで行われます。

これに相当するC / C ++は次のとおりです。

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

テクスチャは興味深いです。バインディングの特別なケースを表します。多くのglBind*呼び出しには「ターゲット」パラメーターがあります。これは、そのタイプのオブジェクトをバインドできるOpenGLコンテキストのさまざまな場所を表します。たとえば、読み取り(GL_READ_FRAMEBUFFER)または書き込み(GL_DRAW_FRAMEBUFFER)のためにフレームバッファオブジェクトをバインドできます。これは、OpenGLがバッファを使用する方法に影響します。これは、loc上記のパラメーターが表すものです。

テクスチャは、最初にターゲットにバインドしたときに特別な情報を取得するため、特別です。最初にテクスチャをとしてバインドすると、GL_TEXTURE_2D実際にはテクスチャに特別な状態が設定されます。このテクスチャは2Dテクスチャであると言っています。そして、それは常に 2Dテクスチャです。この状態を変更することはできません今まで。最初にとしてバインドされたテクスチャがある場合は、常にとしてバインドするGL_TEXTURE_2D必要があります。バインドしようとすると、エラーが発生します(実行時)。GL_TEXTURE_2DGL_TEXTURE_1D

オブジェクトがバインドされると、その状態を変更できます。これは、そのオブジェクトに固有の汎用関数を介して行われます。これらも、変更するオブジェクトを表す場所を取ります。

C / C ++では、次のようになります。

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

この関数が、現在バインドされているloc値にあるものを設定することに注意してください。

テクスチャオブジェクトの場合、メインのテクスチャ状態変更関数はglTexParameterです。唯一の他の機能は、変更テクスチャの状態があることをglTexImage関数とそのバリエーション(glCompressedTexImageglCopyTexImage、最近glTexStorage)。さまざまなSubImageバージョンがテクスチャの内容を変更しますが、技術的には状態を変更しません。Image関数は、テクスチャ記憶を割り当て、テクスチャのフォーマットを設定します。SubImage関数は、単にピクセルを周りにコピーします。これはテクスチャの状態とは見なされません。

繰り返しますが、これらはテクスチャの状態を変更する唯一の関数です。glTexEnv環境状態を変更します。テクスチャオブジェクトに格納されているものには影響しません。

アクティブテクスチャ

テクスチャの状況はより複雑ですが、これもレガシーの理由から、非公開のままにするのが最善です。これがglActiveTexture出てくるところです。

テクスチャのために、単にターゲットは(存在しないGL_TEXTURE_1DGL_TEXTURE_CUBE_MAPなど)。テクスチャユニットもあります。C / C ++の例では、次のようになります。

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

ここで、Objectsの2Dリストだけでなく、現在のオブジェクトの概念もあることに注意してください。現在のオブジェクトを設定する関数があり、現在のオブジェクトの最大数の概念があり、すべてのオブジェクト操作関数は現在のオブジェクトから選択するように調整されています。

現在アクティブなオブジェクトを変更すると、ターゲットの場所のセット全体が変更されます。したがって、現在のオブジェクト0に入るものをバインドし、現在のオブジェクト4に切り替えて、完全に異なるオブジェクトを変更することができます。

テクスチャオブジェクトとのこの類似性は完璧です...ほぼ。

参照は、glActiveTexture整数を取ることはありません。それはとり列挙子を。理論的には、からGL_TEXTURE0まで何でも取り込めることを意味しGL_TEXTURE31ます。ただし、理解しておく必要があることが1つあります。

これは間違いです!

glActiveTexture取ることのできる実際の範囲は、によって管理されGL_MAX_COMBINED_TEXTURE_IMAGE_UNITSます。これは、実装で許可されている同時マルチテクスチャの最大数です。これらはそれぞれ、異なるシェーダーステージの異なるグループに分けられます。たとえば、GL 3.xクラスのハードウェアでは、16個の頂点シェーダーテクスチャ、16個のフラグメントシェーダーテクスチャ、16個のジオメトリシェーダーテクスチャが得られます。したがって、GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS48 になります。

しかし、48の列挙子はありません。これがglActiveTexture実際に列挙子をとらない理由です。正しいコールへの道は、glActiveTexture次のとおりです。

glActiveTexture(GL_TEXTURE0 + i);

ここで、i0との間の数ですGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS

レンダリング

では、これらすべてがレンダリングとどう関係しているのでしょうか?

シェーダーを使用するときは、サンプラーのユニフォームをテクスチャ画像単位(glUniform1i(samplerLoc, i)iは画像単位)に設定します。これは、で使用した数を表していますglActiveTexture。サンプラーは、サンプラータイプに基づいてターゲットを選択します。したがってsampler2DGL_TEXTURE_2Dターゲットから選択します。これが、サンプラーにさまざまなタイプがある理由の1つです。

これは、同じテクスチャイメージユニットを使用する異なるタイプの 2つのGLSLサンプラーを使用できるように、疑わしく聞こえます。しかし、それはできません。OpenGLはこれを禁止し、レンダリングしようとするとエラーが発生します。


12
うわー!さらに別の素晴らしい答え-ニコルに感謝!私は特に、2Dテクスチャが常に 2Dテクスチャであるという段落を気に入っています。現在、これらのいくつかのラッパーを構築していますが、変更するためにそれを開いたままにしておくべきかどうかはわかりませんでした。そして、約GL_TEXTURE0 + i-列挙値を調べて、それが有効かどうかを確認するつもりでした。そして最後の段落-それが合法であるかどうか知りませんでした。優れた!私はあなたのすべての回答をブックマークして、再び参照できるようにします。
mpen 2012年

6
@Nicol Bolas:これは本当によく説明されています。この一部をオンラインのopenglブックのテクスチャの章にコピーする必要があります。これは非常に明確で、この章をよく補完すると思います。
WesDec 2012

3
@Nicol Bolas私はOpenGLを学び始めたばかりで、この答えは私にとても役立ちました。ありがとうございました!
インライン

2
ちょっとニコ、あなたの小さなタイプミスを指摘したいだけです:GL_WRITE_FRAMEBUFFERではなくGL_DRAW_FRAMEBUFFERです
Defd

3
@Nicol:うわー、これまで私がこれまでに持っていた最高の定義はあなたのアークシンセシスのチュートリアルからだった。ありがとう
バガーズ2013

20

やってみるよ!これはそれほど複雑ではなく、単に用語の問題です。はっきりさせておきます。


システムで使用可能なメモリの数とほぼ同じ数のテクスチャオブジェクトを作成できます。これらのオブジェクトは、テクスチャの実際のデータ(テクセル)を、glTexParameterFAQを参照)によって提供されるパラメーターとともに保持します。

作成されたとき、あなたは1割り当てる必要がテクスチャターゲットをテクスチャの種類を表す1つのテクスチャオブジェクトに(GL_TEXTURE_2DGL_TEXTURE_3DGL_TEXTURE_CUBE、...)。

テクスチャオブジェクトテクスチャターゲットの 2つのアイテムは、テクスチャデータを表します。後でまた連絡します。

テクスチャユニット

OpenGLは、描画中に同時に使用できるテクスチャユニットの配列を提供します。配列のサイズはOpenGLシステムによって異なりますが、8を使用しています。

あなたはできるバインド描画中に与えられたテクスチャを使用するようにテクスチャユニットにテクスチャオブジェクトを。

シンプルで簡単な世界では、特定のテクスチャで描画するには、テクスチャオブジェクトをテクスチャユニットにバインドし、次のようにします(疑似コード)。

glTextureUnit[0] = textureObject

GLはステートマシンであるため、残念ながら、この方法では機能しません。私たちのことを仮にtextureObjectための持っているデータGL_TEXTURE_2Dのテクスチャのターゲットは、我々は以前の割り当てをとして表現されます:

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

GL_TEXTURE_2Dバインドするテクスチャのタイプに実際に依存することに注意してください。

テクスチャオブジェクト

擬似コードでは、テクスチャデータまたはテクスチャパラメータを設定するには、たとえば次のようにします。

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

OpenGLは、テクスチャオブジェクトを直接操作することはできません。そのコンテンツを更新/設定したり、パラメータを変更したりするには、まずアクティブなテクスチャユニット(どちらでもかまいません)にバインドする必要があります。同等のコードは次のようになります。

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)

シェーダー

シェーダーはすべてのテクスチャユニットにアクセスでき、アクティブなテクスチャを気にしません。

サンプラーの制服は、intサンプラー(及び使用するテクスチャユニットのインデックスを表す値ではない用途にテクスチャオブジェクト)。

したがって、使用する単位にテクスチャオブジェクトをバインドする必要があります。

サンプラーの種類は、テクスチャユニットに使用されているテクスチャターゲットとマッチを行います:Sampler2DのためにGL_TEXTURE_2D、そして上のように...


わからないことが1つあります。いくつかのテクスチャがあり、さまざまなテクスチャユニットの多くのシェーダーで使用されているとします。実行時にテクスチャフィルタリングを変更したいとします。どのテクスチャユニットを使用すればよいですか?ユニット0でテクスチャの状態を変更してから、そのテクスチャを別のユニットで使用できますか?
majakthecoder 2015年

@majakthecoder私の答えでは、フィルタリングをテクスチャオブジェクトのプロパティと見なしています。つまり、テクスチャユニットで具体的に変更することはできません。対象とするOpenGLの種類によっては、この問題を解決するためにサンプラーオブジェクトopengl.org/wiki/Sampler_Object)を使用できる場合があります。それ以外の場合は、テクスチャオブジェクトを複製して、複数の同時フィルタリングを行う必要があります。
rotoglup 2015年

12

ある塗装処理工場のようなGPUを想像してみてください。

いくつかのタンクがあり、いくつかの塗装機に染料を送ります。次に、塗装機で染料がオブジェクトに適用されます。これらの戦車はテクスチャユニットです

これらのタンクには、さまざまな種類の染料を装備できます。各種類の染料は他の種類の溶媒を必要とします。「溶媒」はテクスチャターゲットです。便宜上、各タンクは溶媒供給装置に接続されていますが、各タンクでは一度に1種類の溶媒しか使用できません。だから、バルブ/スイッチがありますTEXTURE_CUBE_MAPTEXTURE_3DTEXTURE_2DTEXTURE_1D。すべての染料タイプを同時にタンクに充填することができますが、1種類の溶媒しか入っていないため、一致する種類の染料のみが「希釈」されます。したがって、それぞれの種類のテクスチャをバインドできますが、「最も重要な」溶剤とのバインドは実際にタンクに入り、それが属する種類の染料と混ざります。

そして、倉庫から出て、それを「結合」することによってタンクに満たされる染料自体があります。それがテクスチャです。


2
奇妙な類推のようなもの...それが本当に何かを明らかにするかどうかはわかりません。特に「希釈」と「最も重要な溶剤」に関する部分。2Dテクスチャと3Dテクスチャの両方をバインドすると、どちらか一方しか使用できないというのですか、それとも何ですか?最も重要だと思われるのはどれですか?
mpen 2012年

2
@マーク:ええと、私は文字通りの染料(油ベースと水ベースなど)を扱う画家の立場で話そうとしていました。とにかく、複数のテクスチャターゲットをバインドして有効にすると、CUBE_MAP> 3D> TEXTURE_ARRAY> 2D> 1Dという優先順位があります。
datenwolf 2012年

1
きちんと!優先順位については知りませんでした。テクスチャユニットごとに使用できるテクスチャターゲットは1つだけであることがわかったので、これはもっと理にかなっています。
mpen 2012年

1
@ legends2k:さて、今では面白くなっています。コアについてですか、それとも互換性プロファイルについてですか。理想的なドライバー、またはバグのあるドライバーを想定していますか?理論的には、ユニフォームのタイプによって、テクスチャユニットのどのターゲットを選択するかが選択されます。実際には、これはコアプロファイルで発生します。互換性プロファイルでは、テクスチャユニットの前のターゲットがサンプラーのタイプと一致しない場合、一部のバグのあるドライバーがすべて白のデフォルトテクスチャを提示することを期待しています。
datenwolf 2012

1
@ legends2k:また、2Dと3Dのテクスチャが同じユニットにバインドされていて、2Dと3Dのサンプラーのユニフォームが同じユニットにバインドされている場合はどうなるでしょうか。これで、あらゆる種類の奇妙なドライバーバグをトリガーできます。実際には、古い固定機能優先モデルで考えることで、あなたの心は正気でプログラムは機能し続けます。これが、ほとんどのドライバーが予測可能な方法で動作するためです。
datenwolf 2012

2

シェーダーで2つのテクスチャからのルックアップが必要な場合:

uniform sampler2D tex1;  
uniform sampler2D tex2;  

tex1とtex2のソースを次のように示す必要があります。

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

レンダーループで:

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

gl_bindtextureでは、そのようなことはできません。一方、レンダリングのループでバインドを使用する可能性があるのは、ストリーム(ビデオ、ウェブカメラ)のコンテンツでテクスチャをフィードする場合です。

gl.bindTexture(gl.TEXTURE_2D, tex1);  
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video);  
// in the render loop
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.