Tudo sobre objetos OpenGL
O modelo padrão para objetos OpenGL é o seguinte.
Objetos têm estado. Pense neles como um struct
. Portanto, você pode ter um objeto definido assim:
struct Object
{
int count;
float opacity;
char *name;
};
O objeto possui certos valores armazenados e possui estado . Objetos OpenGL também têm estado.
Mudando de estado
No C / C ++, se você tiver uma instância do tipo Object
, altere seu estado da seguinte maneira: obj.count = 5;
Você faria referência direta a uma instância do objeto, obteria a parte específica do estado que deseja alterar e colocaria um valor nela.
No OpenGL, você não faz isso.
Por motivos legados, é melhor deixar inexplicável, para alterar o estado de um objeto OpenGL, você deve primeiro vinculá- lo ao contexto. Isso é feito com alguns de glBind*
chamada.
O equivalente em C / C ++ a isso é o seguinte:
Object *g_objs[MAX_LOCATIONS] = {NULL};
void BindObject(int loc, Object *obj)
{
g_objs[loc] = obj;
}
Texturas são interessantes; eles representam um caso especial de ligação. Muitas glBind*
chamadas têm um parâmetro "target". Isso representa diferentes locais no contexto do OpenGL em que objetos desse tipo podem ser vinculados. Por exemplo, você pode vincular um objeto buffer de moldura para leitura ( GL_READ_FRAMEBUFFER
) ou para gravação ( GL_DRAW_FRAMEBUFFER
). Isso afeta como o OpenGL usa o buffer. É isso que o loc
parâmetro acima representa.
Texturas são especiais porque quando você primeiro ligá-los a um alvo, que recebem informações especiais. Quando você liga uma textura pela primeira vez GL_TEXTURE_2D
, você está realmente definindo um estado especial na textura. Você está dizendo que essa textura é uma textura 2D. E sempre será uma textura 2D; esse estado não pode ser alterado nunca . Se você tem uma textura que foi primeiro vinculada como a GL_TEXTURE_2D
, sempre deve vinculá-la como a GL_TEXTURE_2D
; tentar vinculá-lo, pois GL_TEXTURE_1D
causará um erro (durante o tempo de execução).
Depois que o objeto é vinculado, seu estado pode ser alterado. Isso é feito através de funções genéricas específicas para esse objeto. Eles também usam um local que representa qual objeto modificar.
Em C / C ++, isso se parece com:
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;
}
}
Observe como esta função define o que quer que esteja no loc
valor atualmente vinculado .
Para objetos de textura, as principais funções de alteração do estado da textura são glTexParameter
. As únicas outras funções que alteram o estado da textura são as glTexImage
funções e suas variações ( glCompressedTexImage
, glCopyTexImage
recentes glTexStorage
). As várias SubImage
versões alteram o conteúdo da textura, mas tecnicamente não alteram seu estado . As Image
funções alocam armazenamento de textura e definem o formato da textura; as SubImage
funções apenas copiam pixels ao redor. Esse não é considerado o estado da textura.
Permita-me repetir: estas são as únicas funções que modificam o estado da textura. glTexEnv
modifica o estado do ambiente; isso não afeta nada armazenado em objetos de textura.
Textura ativa
A situação das texturas é mais complexa, mais uma vez por motivos legados, é melhor deixar de lado. É aqui que glActiveTexture
entra.
Para texturas, não são alvos apenas ( GL_TEXTURE_1D
, GL_TEXTURE_CUBE_MAP
, etc.). Existem também unidades de textura . Em termos de nosso exemplo de C / C ++, o que temos é o seguinte:
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;
}
Observe que agora não temos apenas uma lista 2D de Object
s, mas também temos o conceito de um objeto atual. Temos uma função para definir o objeto atual, temos o conceito de um número máximo de objetos atuais e todas as nossas funções de manipulação de objetos são ajustadas para selecionar o objeto atual.
Quando você altera o objeto ativo no momento, altera todo o conjunto de locais de destino. Portanto, você pode vincular algo que entra no objeto atual 0, alterna para o objeto atual 4 e modifica um objeto completamente diferente.
Essa analogia com objetos de textura é perfeita ... quase.
Veja, glActiveTexture
não leva um número inteiro; é preciso um enumerador . O que, em teoria, significa que pode levar qualquer coisa de GL_TEXTURE0
para GL_TEXTURE31
. Mas há uma coisa que você deve entender:
ISTO É FALSO!
O intervalo real que glActiveTexture
pode ser adotado é controlado por GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
. Esse é o número máximo de multi-estruturas simultâneas que uma implementação permite. Cada um deles é dividido em diferentes agrupamentos para diferentes estágios do shader. Por exemplo, no hardware da classe GL 3.x, você obtém 16 texturas de shader de vértice, 16 de shader de fragmento e 16 de shaver de geometria. Portanto, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
será 48.
Mas não há 48 enumeradores. É por isso glActiveTexture
que realmente não recebe enumeradores. A maneira correta de ligar glActiveTexture
é a seguinte:
glActiveTexture(GL_TEXTURE0 + i);
onde i
é um número entre 0 e GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
.
Renderização
Então, o que tudo isso tem a ver com renderização?
Ao usar shaders, você define os uniformes do amostrador para uma unidade de imagem de textura ( glUniform1i(samplerLoc, i)
, onde i
é a unidade de imagem). Isso representa o número com o qual você usou glActiveTexture
. O amostrador selecionará o alvo com base no tipo de amostrador. Então, um sampler2D
vai escolher a partir do GL_TEXTURE_2D
alvo. Essa é uma das razões pelas quais os amostradores têm tipos diferentes.
Agora, parece suspeito que você pode ter dois amostradores GLSL, com tipos diferentes que usam a mesma unidade de imagem de textura. Mas você não pode; O OpenGL proíbe isso e gera um erro quando você tenta renderizar.
GL_TEXTURE0 + i
- eu pretendia inspecionar os valores da enumeração para ver se isso era válido ou não. E o último parágrafo - não sabia se isso era legal ou não. Excelente! Estou marcando todas as suas respostas para que eu possa consultá-las novamente.Vou dar uma chance ! Tudo isso não é tão complicado, apenas uma questão de termos, espero que eu me esclareça.
Você pode criar aproximadamente tantos Objetos de Textura quanto houver memória disponível em seu sistema. Esses objetos mantêm os dados reais (texels) de suas texturas, juntamente com os parâmetros fornecidos pelo glTexParameter (consulte as Perguntas frequentes ).
Ao ser criado, você tem que atribuir uma textura alvo a uma textura objeto, que representa o tipo de textura (
GL_TEXTURE_2D
,GL_TEXTURE_3D
,GL_TEXTURE_CUBE
, ...).Esses dois itens, objeto de textura e destino de textura, representam os dados da textura. Voltaremos a eles mais tarde.
Unidades de textura
Agora, o OpenGL fornece uma variedade de unidades de textura , que podem ser usadas simultaneamente ao desenhar. O tamanho da matriz depende do sistema OpenGL, o seu possui 8.
Você pode vincular um objeto de textura a uma unidade de textura para usar a textura fornecida durante o desenho.
Em um mundo simples e fácil, para desenhar com uma determinada textura, você vincularia um objeto de textura à unidade de textura e faria (pseudocódigo):
Como GL é uma máquina de estado, infelizmente, não funciona dessa maneira. Supondo que temos
textureObject
dados para oGL_TEXTURE_2D
destino da textura, expressaremos a atribuição anterior como:Observe que
GL_TEXTURE_2D
realmente depende do tipo de textura que você deseja vincular.Objetos de textura
No pseudo-código, para definir dados ou parâmetros de textura, você faria por exemplo:
O OpenGL não pode manipular diretamente objetos de textura, para atualizar / definir seu conteúdo ou alterar seus parâmetros, você deve primeiro vinculá-los à unidade de textura ativa (qualquer que seja). O código equivalente se torna:
Shaders
Os shaders têm acesso a todas as unidades de textura, eles não se importam com a textura ativa.
Uniformes do amostrador são
int
valores que representam o índice da unidade de textura a ser usada no amostrador (e não o objeto de textura a ser usado).Então você deve vincular seus objetos de textura às unidades que deseja usar.
O tipo do amostrador fará a correspondência com o destino da textura usado na unidade de textura:
Sampler2D
forGL_TEXTURE_2D
e assim por diante ...fonte
Imagine a GPU como uma planta de processamento de tinta.
Existem vários tanques, que fornecem corante a alguma máquina de pintura. Na máquina de pintura, o corante é então aplicado ao objeto. Esses tanques são as unidades de textura
Esses tanques podem ser equipados com diferentes tipos de corante. Cada tipo de corante requer algum outro tipo de solvente. O "solvente" é o alvo da textura . Por conveniência, cada tanque está conectado a algum suprimento de solvente, mas apenas um tipo de solvente pode ser usado por vez em cada tanque. Portanto, há uma válvula / switch
TEXTURE_CUBE_MAP
,TEXTURE_3D
,TEXTURE_2D
,TEXTURE_1D
. Você pode encher todos os tipos de corante no tanque ao mesmo tempo, mas como apenas um tipo de solvente entra, ele "dilui" apenas o tipo de correspondência de corante. Assim, você pode vincular cada tipo de textura, mas a ligação com o solvente "mais importante" entra no tanque e se mistura com o tipo de corante a que pertence.E depois há o próprio corante, que vem de um armazém e é enchido no tanque "ligando-o". Essa é a sua textura.
fonte
Se no seu shader você precisar procurar em 2 texturas:
é necessário indicar para tex1 e tex2 suas fontes da seguinte forma:
no loop de renderização:
Com uma gl_bindtexture, não é possível fazer isso. Por outro lado, um possível uso de um bind no loop de renderização é o caso em que você alimenta uma textura com um conteúdo em fluxo (vídeo, webcam):
fonte