Mascaramento de regiões em OpenGL
Uso do STENCIL Bufffer


A utilização de áreas de mascaramento telas geradas com programas OpenGL tem o objetivo de criar regiões onde os objetos não podem ser desenhados.
Um exemplo disto são as áreas reservadas a menus e telas de ajuda. Estas, quando presentes na tela, não devem ser afetadas pelos desenhos 3D gerados com comandos OpenGL.
Para dar suporte a este tipo de situação a biblioteca OpenGL possui um recurso chamado de Stencil Buffer.

O Stencil Buffer é uma matriz bidimensional que possui o mesmo tamanho da janela de desenho usada em para exibir os objetos OpenGL. Para criá-la deve-se usar a constante  GLUT_STENCIL no comando de inicialização da GLUT:

glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH |GLUT_STENCIL);

Funcionamento do Stencil Buffer

Limpeza do Stencil Buffer

Todas as vezes que a janela desenho de OpenGL for redimensionada, é necessário limpar o Stencil Buffer. Isto é feito da seguinte forma:
 
 // Habilita o uso do Stencil neste programa
 glEnable(GL_STENCIL_TEST);
 // Define que "0" será usado para limpar o Stencil
 glClearStencil(0);
 // limpa o Stencil
 glClear(GL_STENCIL_BUFFER_BIT);

Incialização do Stencil Buffer

A seguir, é necessário inicializar o Stencil Buffer. Como foi dito anteriormente o Stencil é uma matriz bidimensional. Nesta matiz devem ser colocados valor que serão posteriormente testados para determinar se é possível, ou não, desenhar numa certa área da tela.

Não há, entretanto, funções que permitam o acesso direto ao Stencil Buffer.

O armazenamento de dados no Stencil é feito desenhando-se sobre a tela OpenGL como normalmente se faz. No caso de se desejar colocar dados no Stencil, entretato, define-se funções de teste que irão colocar (ou não) dados na área do Stencil, dependendo do resiltado dos testes.
Por exemplo,

Referencia = 1;
NovoDado = 1;
glStencilFunc(GL_ALWAYS, Referencia, NovoDado);
glStencilOp(GL_REPLACE,GL_REPLACE,GL_REPLACE);
definem que o valor ''NovoDado" será usado para substituir (GL_REPLACE) o conteúdo do Stencil Buffer sempre (GL_ALWAYS) que um desenho for feito na tela de OpenGL.
A partir disto, então, pode-se desenhar qualquer coisa na tela que o Stencil Buffer irá receber o valor "NovoDado" nos pontos correspondentes aos pontos ocupados pelo desenho.
O trecho de código a seguir, por exemplo, define a janela de OpenGL como uma região com coordenadas lógicas de (0,0) a (10,10) e desenha dentro desta região um retângulo. A área ocupada por este retângulo, no Stencil Buffer, será setada com  "NovoDado".
 
 // Define uma área de desenha com coordenadas
 // lógicas (0,0)->(10,10)

 // Ativa matriz de projeção (necessário para usar a gluOrtho2D)
 glMatrixMode(GL_PROJECTION);
 glLoadIdentity ();
 gluOrtho2D(0, 10, 0, 10); // define coordenadas lógicas de desenho

 // Volta para a matrix de transformações geométricas
 glMatrixMode(GL_MODELVIEW);

 // Desenha um retângulo
 glRectf(0,4.5, 10,5.5);

Redimensionamento do Stencil Buffer

Como o Stencil deve ter sempre o mesmo tamanho da janela de exibição dos objetos, então a rotina que trata do redimensionamento da janela deve sempre reinicializar o Stencil Buffer com os comandos apresentados acima.

Desenhando DENTRO da área marcada do Stencil Buffer

Para desenhar na área do Stencil Buffer que foi previamente inicializada com um deve-se habilitar esta operação através dos seguintes comandos:
glStencilFunc(GL_EQUAL, 1, 1);
glStencilOp(GL_KEEP,GL_KEEP, GL_KEEP);
Estas funções fazem com que sempre que o Stencil Buffer tiver valor igual (GL_EQUAL) a 1 então o desenho pode ser exibido na tela. O valor existente no Stencil Buffer, no ponto onde o desenho está sendo realizado deverá, ser mantido como está (GL_KEEP).

O trecho de programa seguir exibe dois retângulos (um verde e outro vermelho) na área do Stencil. Note que se faz uso do comando glDisable(GL_DEPTH_TEST) a fim de desabilitar o teste do ZBUFFER. isto é feito pois neste caso o desenho é 2D, não necessitando do uso de teste de profundidade.

glStencilFunc(GL_EQUAL, 1, 1);
glStencilOp(GL_KEEP,GL_KEEP, GL_KEEP);

// Ativa matriz de projeção (necessário para usar a gluOrtho2D)
glMatrixMode(GL_PROJECTION);
glLoadIdentity ();
// define coordenadas lógicas de desenho
gluOrtho2D(0, 10, 0, 10);

// Volta para a matrix de transformações geométricas
glMatrixMode(GL_MODELVIEW);

glDisable(GL_DEPTH_TEST); // Desabilita o ZBuffer

glColor3f(0,1,0);  // Desenha um retângulo verde
glRectf(4.5f, 4.5f, 5.5f, 5.5f);

glColor3f(1,0,0);  // Desenha um retângulo vermelho
glRectf(2.0f, 4.7f, 8.0f, 5.3f);

glEnable(GL_DEPTH_TEST); // Habilita  o ZBuffer

O código descrito aqui deve ser colocado na roina que trata do redesenho da tela.
 

Desenhando FORA da área marcada do Stencil Buffer

Para desenhar fora da área do Stencil Buffer que foi previamente inicializada com um deve-se habilitar esta operação através dos seguintes comandos:
glStencilFunc(GL_NOTEQUAL, 1, 1);
glStencilOp(GL_KEEP,GL_KEEP, GL_KEEP);
Estas funções fazem com que sempre que o Stencil Buffer tiver valor diferente (GL_NOTEQUAL) de 1 então o desenho pode ser exibido na tela. O valor existente no Stencil Buffer, no ponto onde o desenho está sendo realizado, deverá ser mantido como está (GL_KEEP).

O trecho de programa seguir exibe dois cubos FORA da área do Stencil.

glMatrixMode(GL_MODELVIEW);
glLoadIdentity ();
glPushMatrix();
glTranslatef ( 1.0f, 0.0f, 0.0f );
glRotatef(ang,0,1,0);
glColor3f(0.5f,0.3f,0.0f);
DesenhaCubo();
glPopMatrix();

glLoadIdentity ();
glPushMatrix();

glTranslatef ( -1.0f, 0.0f, -5.0f );
glRotatef(45,0,1,0);
glColor3f(0.5f,0.3f,0.0f);
DesenhaCubo();
glPopMatrix();

ang = ang + 2;

Programa Exemplo

Clique aqui para copiar o programa-fonte com o código do exemplo apresentado acima.

Exercício

A partir do exemplo acima, crie um programa que permita a movimentação de um retângulo no Stencil Buffer, conforme este exemplo. Use as teclas de seta para mover o retângulo verde.