Fonte de luz pontual em opengl. Continuamos nosso estudo sobre OpenGL: iluminação segundo Phong. Juntando tudo

A iluminação no OpenGL ES é um recurso útil que pode adicionar um toque agradável aos jogos 3D. Para usar funcionalidades como essa, primeiro precisamos entender o modelo de iluminação OpenGL ES.

Como funciona a iluminação

Vamos pensar em como funciona a iluminação. Primeiro precisamos de uma fonte de luz que emita luz. Você também precisará de um objeto iluminado. Por fim, também precisamos de um sensor, como um olho ou uma câmera, que receba os fótons enviados pela fonte de luz e refletidos pelo objeto. A iluminação altera a cor percebida de um objeto dependendo: do tipo de fonte de luz; cor ou intensidade da fonte de luz; posição da fonte de luz e sua direção em relação ao objeto iluminado; material e textura do objeto.

A intensidade com que a luz é refletida por um objeto depende de muitos fatores. O fator mais importante que observamos é o ângulo em que o feixe de luz atinge a superfície. Quanto mais próximo esse ângulo estiver do ângulo reto, maior será a intensidade com que a luz será refletida no objeto. Isso está ilustrado na figura. 11.1.

Quando um feixe de luz atinge uma superfície, ele será refletido em duas direções diferentes. A maior parte da luz será refletida difusamente. Isso significa que os raios de luz refletidos são espalhados de forma desigual e aleatória pela superfície do objeto. Alguns raios são refletidos especularmente. Isto significa que os raios de luz serão refletidos de volta como se estivessem atingindo um espelho perfeito. Na Fig. A Figura 11.2 mostra a diferença entre reflexões difusas e especulares.

Arroz. 11.1. Quanto mais próximo o ângulo estiver do ângulo reto, maior será a intensidade da luz refletida

Arroz. 11.2. Reflexões dispersas e especulares

As reflexões especulares aparecerão como realces nos objetos. Se a luz reflete especularmente em um objeto depende do material do qual ela é feita. Objetos com superfície irregular ou áspera, como a pele, provavelmente não terão realces especulares. Objetos que possuem uma superfície lisa como vidro ou mármore exibirão esses artefatos de luz. É claro que o vidro ou o mármore não são perfeitamente lisos, mas comparados à madeira ou à pele humana, são.

Quando a luz atinge uma superfície, seu reflexo também muda de cor dependendo da composição química objeto iluminado. Objetos que nos parecem vermelhos refletem apenas porções vermelhas de luz e absorvem todas as outras frequências. Um objeto preto é aquele que absorve quase toda a luz que incide sobre ele.

OpenGL ES permite simular comportamento do mundo real definindo fontes de luz e materiais de objetos.

Fontes de luz

Estamos rodeados por muitas fontes de iluminação diferentes. O sol envia constantemente seus fótons. Os monitores emitem luz que nos envolve com um brilho agradável à noite. Lâmpadas e faróis nos ajudam a evitar colisões com vários objetos no escuro. OpenGL ES permite criar quatro tipos de fontes de luz.

Luz de fundo. Não é uma fonte de luz em si, mas o resultado do aparecimento de fótons de outras fontes de luz. Juntos, esses fótons aleatórios criam um nível constante de iluminação que não tem direção e ilumina todos os objetos igualmente.

Fontes de luz pontuais. Eles ocupam uma posição no espaço e emitem luz em todas as direções. Por exemplo, uma fonte pontual de luz é uma lâmpada.

Fontes de iluminação direcional. Expressado como instruções no OpenGL ES. Supõe-se que eles estejam infinitamente distantes. Idealmente, o Sol poderia ser essa fonte. Podemos supor que todos os raios de luz vindos do Sol atingem a Terra no mesmo ângulo devido à distância entre a Terra e o Sol.

Sobre lâmpadas. Essas luzes são semelhantes às luzes pontuais porque têm uma determinada posição no espaço. Além disso, possuem uma direção na qual emitem raios de luz. Eles criam um cone de luz limitado a um determinado raio. Um exemplo de tal fonte de luz é um poste de luz.

Consideraremos apenas a retroiluminação, bem como fontes de luz pontuais e direcionais. As luzes costumam ser difíceis de usar em dispositivos Android limitados por GPU devido à forma como o OpenGL ES calcula a iluminação. Você logo entenderá por que isso acontece.

Além da posição e direção da fonte de luz, o OpenGL ES permite determinar a cor ou intensidade da luz. Essas características são expressas usando cores RGBA. No entanto, o OpenGL ES exige que você defina quatro cores diferentes por fonte, em vez de apenas uma.

Destaque - A intensidade/cor que contribui para o sombreamento de um objeto. O objeto será iluminado igualmente por todos os lados, independentemente de sua posição ou orientação em relação à fonte de luz.

Difusa – intensidade/cor da luz que iluminará o objeto após calcular a reflexão difusa. As bordas do objeto que não estão voltadas para a fonte de luz não serão iluminadas, assim como na vida real.

Especular – intensidade/cor semelhante à cor difusa. Porém, afeta apenas os pontos do objeto que possuem uma determinada orientação em relação à fonte de luz e ao sensor.

Emissivo é um cálculo de cores muito complexo que tem uso extremamente limitado em aplicações físicas do mundo real, por isso não iremos abordá-lo.

Na maioria das vezes usaremos intensidades difusas e especulares da fonte de luz e forneceremos os outros dois valores padrão. Além disso, na maioria das vezes usaremos a mesma cor RGBA para intensidade difusa e especular.

Materiais

Todos os objetos em nosso mundo consistem em algum tipo de material. Cada material determina como a luz que incide sobre um objeto será refletida e mudará a cor da luz refletida. OpenGL ES permite definir as mesmas quatro cores RGBA para um material e para uma fonte de luz.

A luz de fundo é uma cor que combina com a cor de fundo de qualquer fonte de luz na cena.

Difusa é uma cor que combina com a cor difusa de qualquer fonte de luz.

Especular é uma cor que combina com a cor especular de qualquer fonte de luz. É usado para criar destaques na superfície de um objeto.

Emissiva – continuamos ignorando esse tipo de cor, pois praticamente não é utilizada em nosso contexto.

A Figura 11.3 ilustra os três primeiros tipos de propriedades do material/fonte de luz: luz de fundo, difusa e especular.

Arroz. 11.3. Diferentes tipos de materiais/fontes de luz: apenas luz de fundo (esquerda), apenas difusa (meio), luz de fundo e cor difusa com realces especulares (direita)

Na Fig. A Figura 11.3 mostra o efeito de diversas propriedades de materiais e fontes de luz na cor. A luz de fundo ilumina o tamanho uniformemente. A luz dispersa será refletida dependendo do ângulo em que os raios de luz atingem o objeto; As áreas que estão diretamente voltadas para a fonte de luz serão iluminadas com mais brilho, as áreas que a luz não consegue alcançar ficarão escuras. Na imagem certa você pode ver uma combinação de luz de fundo, luz difusa e especular. A luz especular aparece como destaques brancos na esfera.

Como o OpenGL ES calcula a iluminação: normais de vértice

Você sabe que a intensidade da luz refletida por um objeto depende do seu ângulo de incidência no objeto. OpenGL ES usa esse fato para calcular a iluminação. Para isso, ele usa normais de vértice, que devem ser definidas no código da mesma forma que coordenadas de textura ou cores de vértice. Na Fig. A Figura 11.4 mostra uma esfera e suas normais de vértice.

Arroz. 11.4. Esfera e suas normais de vértice

Normais são vetores unitários que indicam a direção na qual uma superfície é girada. No nosso caso, a superfície é um triângulo. Em vez de definir a normal da superfície, definimos a normal do vértice. A diferença entre essas normais é que a normal do vértice pode não apontar na mesma direção que a normal da superfície. Isto é claramente visível na Fig. 11.4, onde cada normal do vértice é a normal média de todos os triângulos aos quais o vértice pertence. Essa média é executada para criar um sombreamento suave do objeto.

Ao renderizar um objeto usando iluminação e normais de vértice, o OpenGL ES determinará o ângulo entre cada vértice e a fonte de luz. Se ele conhecer esse ângulo, poderá calcular a cor do vértice com base nas propriedades do material. O resultado final é a cor de cada vértice, que é então aplicada a cada triângulo em combinação com as cores calculadas dos outros vértices. Esta cor utilizada será combinada com quaisquer transformações de textura que aplicarmos ao objeto.

Isso parece muito assustador, mas na verdade não é tão ruim assim. Precisamos ativar a iluminação e definir fontes de luz, material renderizável e normais de vértice (além dos parâmetros de vértice que normalmente definiríamos, como posição ou coordenadas de textura). Vejamos como isso pode ser implementado usando OpenGL ES.

Na prática

Agora vamos realizar todos os passos necessários para trabalhar com iluminação utilizando OpenGL ES. Vamos criar algumas pequenas classes auxiliares que facilitarão um pouco o trabalho com fontes de luz e colocá-las no pacote com.badlogi with.androi dgames.framework.gl.

Permissão e proibição de iluminação

Tal como acontece com outros estados do OpenGL ES, você deve primeiro ativar a funcionalidade nomeada. Isso pode ser feito da seguinte forma:

Depois disso, a iluminação será aplicada a todos os objetos renderizados. Para obter o resultado, você precisa definir fontes de luz e materiais, bem como normais de vértice. Assim que terminarmos de desenhar todos os objetos necessários, a iluminação pode ser desligada:

Determinando fontes de luz

OpenGL ES fornece 4 tipos de fontes de luz: luz de fundo, spot, direcional e holofote. Vejamos como determinar os três primeiros. Para que as luminárias sejam eficazes e tenham boa aparência, cada modelo deve consistir em um grande número de triângulos. Isto não é possível para muitos dispositivos móveis atuais.

OpenGL ES permite definir no máximo 8 fontes de luz simultaneamente, bem como uma fonte de luz global. Cada uma das 8 fontes de luz possui um identificador, de GL10.GL LIGHT0 a GL10.GL LIGHT7. Se precisar alterar as propriedades de uma dessas luzes, você pode fazer isso definindo o ID correspondente.

Você pode ativar o uso de luzes usando a seguinte sintaxe:

A seguir, o OpenGL ES receberá as propriedades desta fonte de luz e as aplicará a todos os objetos renderizados. Se precisarmos desabilitar o uso de uma fonte de luz, podemos fazê-lo com a seguinte afirmação:

O destaque é um caso especial porque não possui ID. Apenas um destaque pode existir em uma cena OpenGL ES. Vamos dar uma olhada mais de perto nesta fonte de iluminação.

Luz de fundo

A retroiluminação é um tipo especial de iluminação. Não tem posição ou direção, apenas uma cor que é aplicada igualmente a todos os objetos iluminados. OpenGL ES permite definir o realce global da seguinte forma:

O ambi entCol ou array contém valores RGBA da cor da luz de fundo, representados como números de ponto flutuante variando de 0 a 1. O método gl LightModel fv toma como primeiro parâmetro uma constante especificando que queremos definir a cor da luz de fundo source, uma matriz de números de ponto flutuante, que contém a cor de origem e o deslocamento da matriz flutuante a partir da qual o método começará a ler os valores RGBA. Vamos colocar o código que resolve esse problema em uma classe pequena. Seu código é mostrado na Listagem 11.2.

Listagem 11.2. Classe AmbienteLight.java. abstração simples de iluminação global ODenGL ES

Tudo o que fazemos é armazenar a cor de destaque em um array flutuante e fornecer dois métodos: um usado para definir a cor e outro usado para informar ao OpenGL ES para usar essa cor. A cor padrão é cinza.

Fontes de iluminação pontual

As luzes pontuais têm posição, bem como cor/intensidade de fundo, difusa e especular (não consideramos cor/intensidade emissiva). Definir tipos diferentes cores da seguinte forma:

O primeiro parâmetro é o identificador da fonte de luz. Neste caso usamos a quarta fonte. O próximo parâmetro especifica o atributo que queremos alterar. O terceiro parâmetro é uma matriz de números de ponto flutuante contendo os valores RGBA, e o último é o deslocamento nesta matriz. Determinar a posição da fonte é igualmente fácil:

Definimos novamente o atributo que queremos alterar (neste caso, posição), uma matriz de quatro elementos contendo as coordenadas x, y e z da fonte de luz no mundo que está sendo criado. Observe que o quarto elemento da matriz deve ser igual a um se a fonte de luz tiver uma posição. Vamos colocar isso em uma classe auxiliar. Seu código está contido na Listagem 11.3.

Listagem 11.3. Classe Point.Light.java, uma abstração simples de luzes pontuais OpenGL ES

Nossa classe auxiliar contém os componentes de fundo, difuso e de cor especular da luz, bem como a posição (o quarto elemento é um). Além disso, armazenamos o último identificador utilizado para uma determinada fonte, assim torna-se possível criar um método disableO que apagará a luz se necessário. Também temos o método enableO, que utiliza uma instância da classe GL10 e um identificador de fonte de luz (por exemplo GL10.GL LIGHT6). Permite a utilização da iluminação, define seus atributos e armazena o ID utilizado. O método disableO simplesmente desabilita o uso de iluminação usando o membro da classe 1ast.Ligh.tId definido no método enable.

Usamos valores padrão razoáveis ​​para cores de fundo, difusas e especulares ao inicializar matrizes de membros de classe. A luz será branca e não criará brilho, pois seu componente especular é preto.

Fontes de luz direcionais

As fontes de luz direcionais são quase idênticas às pontuais. A única diferença é que eles têm uma direção em vez de uma posição. A forma como a direção é expressa é um tanto confusa. Em vez de usar um vetor para indicar uma direção, o OpenGL ES espera que definamos um único ponto. A direção será então determinada usando um vetor conectando este ponto e a origem. O trecho a seguir permite criar uma fonte de luz direcional vinda do lado direito do mundo:

Podemos convertê-lo em um vetor:

Outros atributos, como fundo ou cor difusa, são idênticos aos de uma luz pontual. A Listagem 11.4 mostra o código para uma pequena classe auxiliar usada para criar luzes direcionais.

Listagem 11.4. Classe Directi onLi ght.java, uma abstração simples de fontes de luz direcionais em OpenGL ES

Esta classe auxiliar é quase idêntica à classe PointLight. A única diferença é que no array de direção o quarto elemento é um. Além disso, em vez do método setPosition, apareceu o método setDirecti on. Permite definir a direção, por exemplo assim: (-1; 0; 0), neste caso a luz virá do lado direito. Dentro do método, todos os componentes do vetor mudam de sinal, então convertemos a direção para o formato esperado pelo OpenGL ES.

Determinando materiais

Um material é definido por vários atributos. Como acontece com qualquer outro objeto OpenGL ES, um material é um estado que permanecerá ativo até que o alteremos novamente ou o contexto do OpenGL ES seja perdido. Para definir os atributos atuais do material, podemos fazer o seguinte:

Como de costume, precisamos definir as cores RGBA de fundo, difusas e especulares. Isso pode ser feito da mesma maneira que antes - usando matrizes de números de ponto flutuante compostas por quatro elementos.

Combinar essas ações em uma classe auxiliar é muito simples. Você pode ver o resultado na Listagem 11.5.

Listagem 11.5. Classe Material Java, uma abstração simples de materiais OpenGL ES

Não há nada de surpreendente aqui também. Simplesmente armazenamos três componentes que descrevem o material e também fornecemos funções para definir seus valores e um método de habilitação que os transfere para o OpenGL ES.

OpenGL ES tem outro ás na manga quando se trata de materiais. Geralmente usa algo chamado material color em vez do método glMaterialfvO. Isso significa que em vez das cores de fundo e difusas determinadas pelo método glMateri al fv, o OpenGL ES tomará a cor dos vértices de nossos modelos como fundo e cores difusas do material. Para ativar esse recurso, basta chamá-lo:

Geralmente é isso que eu faço porque o fundo e as cores difusas costumam ser iguais. Como não uso destaques especulares na maioria dos meus jogos e demos, posso facilmente usar esse método e nem chamar o método glMaterial fv. A forma de usá-lo cabe a você decidir.

Definindo normais

Para que a iluminação funcione no OpenGL ES, você precisa definir normais de vértice para cada vértice no modelo. A normal de um vértice deve ser um vetor unitário apontando (geralmente) na direção em que a superfície à qual o vértice pertence é girada. Na Fig. A Figura 11.5 ilustra as normais dos vértices do nosso cubo.

Arroz. 11.5. Normais de vértice para cada vértice do nosso cubo

Um vértice normal é outro atributo de um vértice, assim como posição ou cor. Para aproveitar as normais dos vértices, precisamos alterar a classe Vertices3 novamente. Para informar ao OpenGL ES onde ele pode encontrar normais para cada vértice, usaremos o método gl Normal PointerO, assim como usamos anteriormente os métodos gl VertexPointer ou gl Col ou Pointer. A Listagem 11.6 mostra a versão final da classe Vertices3.

Listagem 11.6. Classe Vertices3.Java, versão final com suporte a normais

A classe tem um novo membro hasNormal.s que rastreia se os vértices têm normais.

O construtor agora também aceita um parâmetro hasNormals. Ainda precisamos modificar o cálculo do membro vertexSize adicionando três pontos flutuantes a cada vértice sempre que possível.

Como você pode ver, os métodos setVertices e setlndices permanecem inalterados.

No método bindO que acabamos de demonstrar, usamos as mesmas técnicas com o ByteBuffer de antes, mas desta vez adicionamos normais usando o método gl Normal Pointer. Para calcular o deslocamento do ponteiro normal, é necessário levar em consideração se as coordenadas de textura e cor estão especificadas.

Como você pode ver, o método draw também não mudou; toda a ação acontece no método bind O.

Finalmente, modificamos ligeiramente o método unbindO. Desativamos o uso de ponteiros normais, se houver, limpando o estado do OpenGL ES adequadamente.

Aplicar a classe Verti ces3 modificada é tão fácil quanto antes. Vejamos um pequeno exemplo:

Criamos uma matriz de ponto flutuante para armazenar três vértices, cada um dos quais possui uma posição (os três primeiros números em cada linha) e uma normal (os três últimos números em cada linha). Neste caso, definimos um triângulo no plano xy, com suas normais apontando na direção da parte positiva do eixo z.

Tudo o que precisamos fazer é criar uma instância da classe Vertices3 e definir os valores dos vértices. Muito fácil, não é?

Todo o trabalho de encadernação, desenho e desvinculação é feito exatamente da mesma forma que na versão anterior da aula. Como antes, podemos adicionar cores de vértices e coordenadas de textura.

Juntando tudo

Vamos juntar tudo. Precisamos desenhar uma cena que tenha iluminação global, fontes de luz pontuais e direcionais. Eles iluminarão o cubo localizado na origem. Também precisamos chamar o método gl uLookAt. para posicionar a câmera. Na Fig. 11,6 mostrado aparência nosso mundo.

Assim como em todos os outros exemplos, vamos criar uma classe chamada LightTest, estendendo a classe GLGame como de costume. Ele retornará uma instância da classe LightScreen usando o método getStartScreenO. A classe LightScreen herda da classe GLScreen (Listagem 11.7).

Arroz. 11.6. Nossa primeira cena iluminada

Listagem 11.7. Fragmentos da classe LightTest.java. criando iluminação usando OpenGL ES

Vamos começar descrevendo alguns membros da classe. O membro angle armazena informações sobre o ângulo atual de rotação do cubo em torno do eixo y. O membro Verti ces3 armazena os vértices do modelo de cubo, que definiremos em breve. Além disso, temos instâncias das classes AmbientLight, PointLight e Directional Light, bem como uma instância da classe Material.

Em seguida vem o construtor. Aqui são criados os vértices do modelo de cubo e a textura da caixa é carregada. Também inicializamos as luzes e os materiais. A cor da luz de fundo é verde claro. A fonte direcionada é vermelha e está localizada no ponto (3; 3; 0) do nosso mundo. A fonte de luz direcional tem uma cor azul difusa, a luz incide da esquerda. Para o material usamos os valores padrão (um pouco de fundo, branco para o componente difuso e preto para o componente especular).

No método resume garantimos que nossa textura será (re)carregada se o contexto for perdido.

O método createCube permanece essencialmente inalterado em relação aos exemplos anteriores. Entretanto, desta vez adicionamos normais a cada vértice, como mostrado na Fig. 11.5. Fora isso, tudo permanece igual.

No método de atualização simplesmente aumentamos o ângulo de rotação do cubo.

É aqui que fica mais interessante. As primeiras linhas são código padrão para limpar o buffer de cor e profundidade, habilitar testes de profundidade e definir o escopo.

A seguir, definimos a matriz de projeção igual à matriz de projeção em perspectiva usando o método gl uPerspecti ve, e também usamos o método gl uLookAt para a matriz de visualização do modelo, graças ao qual a câmera funciona da mesma forma que na Fig. 11.6.

Então permitimos o uso de iluminação. Neste ponto, nenhuma luz foi definida ainda, então as definiremos nas próximas linhas chamando o método sobre luzes e materiais.

Como de costume, também habilitamos a texturização e vinculamos a textura da caixa. Finalmente, chamamos o método gl RotatefC) para girar o cubo e então desenhar seus vértices usando chamadas bem posicionadas para uma instância da classe Vertices3.

Ao final do método, desativamos as luzes spot e direcionais (lembre-se, a luz de fundo é um estado global), bem como os testes de texturização e profundidade. É isso para iluminação em OpenGL ES.

O resto da sala está vazio; não precisamos realizar nenhuma ação em caso de pausa. Na Fig. A Figura 11.7 mostra o resultado do programa.

Arroz. 11.7. A cena mostrada na Fig. 11.6, renderizado com OpenGL ES

Algumas notas sobre iluminação no OpenGL ES

Embora o uso da iluminação possa adicionar sabor ao seu jogo, ela tem suas limitações e armadilhas. Existem algumas coisas das quais você deve estar ciente.

Usar iluminação consome muitos recursos, especialmente perceptível em dispositivos lentos. Use a iluminação com cuidado. Quanto mais fontes de luz você descrever, mais cálculos serão necessários para renderizar a cena.

A posição/direção das fontes de luz pontuais/direcionais deve ser determinada após o carregamento das matrizes da câmera e antes da matriz de visualização do modelo ser multiplicada por quaisquer outras matrizes para mover e girar objetos. Isto é crítico. Se estas instruções não forem seguidas, poderão ocorrer artefatos de luz inexplicáveis.

Quando você usa o método gl Seal ef para redimensionar o modelo, suas normais também serão dimensionadas. Isso é ruim porque o OpenGL ES espera que os normais tenham parâmetros nas unidades fornecidas. Para contornar esse problema, você pode usar o comando glEnable(GL10.GL NORMALIZE) ou, em algumas circunstâncias, gl Enable(GL10 .GL RESCALE N0RMAL). Acredito que o primeiro comando deva ser utilizado, pois o uso do segundo apresenta limitações e armadilhas. O problema é que normalizar ou redimensionar normais requer muito poder de processamento. A melhor solução do ponto de vista do desempenho é não dimensionar objetos iluminados.

Bem, senhores. Aprendemos bastante sobre OpenGL ultimamente, inclusive como controlar a câmera , trabalhar com texturas, e também com modelos. É hora de falar sobre algo muito mais interessante, nomeadamente a iluminação. Este tópico é interessante porque não há nada pronto para trabalhar com luz no OpenGL; tudo precisa ser escrito de forma independente usando shaders. Neste post veremos a iluminação Phong. Este é um tópico bastante amplo, então falaremos exclusivamente sobre iluminação. Em como eles são feitos sombras, teremos que descobrir isso em outra hora.

Salvando e usando normais

Antes de passarmos diretamente para a iluminação, precisamos de algo como normais.

Já sabemos que os modelos possuem vértices e coordenadas UV correspondentes a esses vértices. Para criar a iluminação necessitamos de mais algumas informações sobre os modelos, nomeadamente normais. A normal é o vetor unitário correspondente a um vértice (ou, alternativamente, a um polígono, mas este não é o nosso caso). Descobriremos exatamente qual o papel que os normais desempenham na implementação da iluminação abaixo. Por enquanto, basta dizer que os normais são realmente muito importantes. Por exemplo, graças a eles, as superfícies parecem mais lisas e você pode distinguir uma bola de um poliedro convexo regular como um icosaedro. E como as normais são tão importantes, precisamos aprender como preservá-las ao converter modelos do Blender para nosso próprio formato.

As mudanças correspondentes são bastante triviais. Obtemos as normais da mesma forma que obtivemos as coordenadas do vértice e as coordenadas UV:

//parte do corpo do procedimento importModelCreate

para (unsigned int j = 0; j< face.mNumIndices ; ++ j) {
índice int não assinado = face.mIndices[ j] ;
aiVector3D pos = malha->mVertices[índice] ;
aiVector3D uv = malha-> mTextureCoords[ 0 ] [ índice] ;
aiVector3D normal = malha-> mNormals[índice] ;

VerticesBuffer[ verticesBufferIndex++ ] = pos.x ;
verticesBuffer[ verticesBufferIndex++ ] = pos.y ;
verticesBuffer[ verticesBufferIndex++ ] = pos.z ;
verticesBuffer[ verticesBufferIndex++ ] = normal.x ;
verticesBuffer[ verticesBufferIndex++ ] = normal.y ;
verticesBuffer[ verticesBufferIndex++ ] = normal.z ;
vérticesBuffer[ vérticesBufferIndex++ ] = uv.x ;
vérticesBuffer[ vérticesBufferIndex++ ] = 1.0f - uv.y ;
}

O procedimento de otimização do modelo muda de forma semelhante. E no procedimento modelLoad, em vez de duas matrizes de atributos, agora precisamos de três:

// parte do corpo do procedimento modelLoad

GlBindVertexArray(modelVAO) ;
glEnableVertexAttribArray(0 ) ;
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);

GlBindBuffer(GL_ARRAY_BUFFER, modeloVBO) ;
glBufferData(GL_ARRAY_BUFFER, cabeçalho- > verticesDataSize, verticesPtr,
GL_STATIC_DRAW);

GLsizei passada = 8 * sizeof(GLfloat) ;
glVertexAttribPointer(0 , 3 , GL_FLOAT, GL_FALSE, passada, nullptr);
glVertexAttribPointer(1 , 3 , GL_FLOAT, GL_FALSE, passada,
(const void * ) (3 * sizeof (GLfloat) ) ) ;
glVertexAttribPointer(2 , 2 , GL_FLOAT, GL_FALSE, passada,
(const void * ) (6 * sizeof (GLfloat) ) ) ;

Também precisamos adicionalmente de uma variável uniforme com matriz M:

GLint uniformeM = getUniformLocation(programId, "M" );

// ...

GlUniformMatrix4fv(uniformM, 1, GL_FALSE, & torreM[ 0 ] [ 0 ] ) ;

... para girar corretamente o normal no espaço no sombreador de vértice:

#versão 330 núcleo

Layout(local = 0 ) em vec3 vertexPos;
layout(local = 1 ) em vec3 vertexNorm;
layout(local = 2 ) em vec2 vertexUV;

uniforme mat4 MVP;
tapete uniforme4 M;

fora do fragmento vec2UV;
fora do fragmento vec3Normal;
fora vec3 fragmentPos;

void principal() (
fragmentUV = vérticeUV;
fragmentNormal = (M * vec4 (vérticeNorm, 0 ) ) . xiz;
fragmentPos = (M * vec4 (vérticePos, 1 ) ) . xiz;

gl_Position = MVP * vec4 (vertexPos, 1);
}

Finalmente, o fragment shader assume uma normal interpolada em três vértices:

// ...

void principal() (

// ...
}

Desta forma simples obtemos as normais para fragmentos.

O que é iluminação Phong?

Conforme observado, a iluminação no OpenGL é escrita em shaders pelo próprio programador. É claro que existe mais de uma forma de implementar esta iluminação, cada uma com seu próprio grau de realismo e requisitos de recursos. E cada método ainda pode ter inúmeras implementações específicas. Pelo que entendi, a iluminação eficiente e realista em tempo real ainda é uma área de pesquisa ativa. Neste post, veremos a iluminação Phong, que é bastante realista e fácil de implementar.

É importante entender a diferença entre os seguintes conceitos:

  • O sombreamento Gouraud é quando você calcula a iluminância de cada vértice, e a iluminância dos fragmentos entre eles é interpolada;
  • Sombreamento Phong - quando a iluminação é calculada separadamente para cada fragmento;
  • A iluminação Phong ou modelo de reflexão Phong é um método de iluminação específico discutido neste artigo e que pode ser usado tanto no sombreamento Gouraud quanto no Phong;

Não é de surpreender que o sombreamento Phong e a iluminação Phong sejam frequentemente confundidos, e em alguns tutoriais você pode ler bobagens como “A ideia do sombreamento Phong é usar três componentes...” o que imediatamente faz você duvidar fortemente da autoridade do pessoa que escreveu este tutorial.

Pelo que pude entender, em aplicações modernas o sombreamento Gouraud quase não é usado; em vez disso, o sombreamento Phong é o preferido. Neste post também utilizaremos o sombreamento Phong, ou seja, a iluminação será calculada separadamente para cada fragmento. O método de iluminação específico que usaremos é a iluminação Phong. Este método é o seguinte.

Três componentes de iluminação são calculados usando fórmulas diferentes:

  • A iluminação ambiente é uma imitação da luz que atingiu um determinado ponto após ser refletida por outros objetos. Ao calcular a iluminação de fundo, nem as normais nem a posição atual da câmera são levadas em consideração;
  • A iluminação difusa é a luz de uma fonte que se espalha após atingir um determinado ponto. Dependendo do ângulo em que a luz incide, a iluminação fica mais forte ou mais fraca. Isso leva em consideração as normais, mas não a posição da câmera;
  • A iluminação especular é a luz de uma fonte que é refletida após atingir um determinado ponto. A luz refletida é visível se entrar na câmera. Portanto, tanto as normais quanto a posição da câmera são levadas em consideração aqui;

Os resultados são então somados para dar a iluminação total.

Para tornar as coisas ainda mais interessantes, existem diferentes fontes de luz. Obviamente, o sol lá fora e uma lanterna no escuro iluminam a cena de maneira muito diferente. Primeiro, veremos a fonte mais simples - a luz direcional.

Luz direcional

A luz direcional é uma imitação de uma fonte de luz infinitamente distante. Tomemos o Sol, por exemplo. O sol está muito longe da terra. Portanto, na superfície da Terra, todos os raios de luz do Sol podem ser considerados paralelos com grande precisão. A luz direcional é caracterizada por sua direção, cor, bem como alguns coeficientes que precisaremos a seguir:

estrutura DirectionalLight(
direção vec3;

cor vec3;
float intensidadeambiente;
float difusaIntensidade;
float especularIntensidade;
} ;

No código do fragment shader, definiremos um procedimento calcDirectionalLight que será usado mais ou menos assim:

em vec3 fragmentPos;
câmera vec3 uniformePos;
luz direcional uniforme luz direcional;

// ...

void principal() (
//normal deve ser corrigido após interpolação
vec3 normal = normalizar (fragmentoNormal);


direcionalLuz) ;

// ...
}

Consideremos a implementação do procedimento.

vec4 calcDirectionalLight(vec3 normal, vec3 fragmentToCamera,
Luz direcional) (
vec4 ambientColor = vec4 (cor clara, 1) * luz. intensidadeambiente;

// ...
}

Primeiro, o primeiro componente é calculado - iluminação de fundo. É simplesmente a cor da luz emitida multiplicada pela intensidade da luz de fundo. Até agora tudo é simples.

// ...

float difusorFactor = max (0.0, ponto (normal, - luz. direção));
vec4 difusaColor = vec4 (cor clara, 1) * luz. difusaIntensidade
*fator difuso;

// ...

Iluminação difusa. A variável difusaFactor representa o cosseno do ângulo entre a normal ao fragmento e o vetor direcionado do fragmento à fonte de luz. Se a luz incide perpendicularmente à superfície, o ângulo é zero. O cosseno deste ângulo é igual a um e a iluminação é máxima (veja o artigo da Wikipedia sobre a Lei de Lambert). À medida que o ângulo aumenta, o cosseno diminui e torna-se igual a zero se a luz for paralela à superfície. Se o cosseno for negativo, então a fonte de luz está em algum lugar atrás da superfície e não está iluminada, então transformamos os valores negativos em zero usando max(0.0, ...) . Além do ângulo em que a luz incide, a intensidade difusa da luz difusa também é levada em consideração.

// ...
vec3 lightReflect = normalize(reflect(light. direção, normal));
float Fator especular = pow(
max (0,0, ponto (fragmentToCamera, lightReflect) ,
materialSpecularFactor
) ;
vec4 especularColor = luz. intensidade especular * vec4(light.color, 1)
* materialSpecularIntensity * especularFactor;
// ...

Iluminação indireta. A variável lightReflect é um vetor unitário que especifica a direção da luz refletida. A variável especularFactor é calculada de forma semelhante ao difuseFactor, só que desta vez leva em consideração o cosseno do ângulo entre a direção em que a luz foi refletida e a direção do fragmento até a câmera. Se esse ângulo for zero, a luz refletida voa diretamente para a câmera e o brilho na superfície é máximo. Se o ângulo for grande, nenhum brilho deverá ser visível. Aqui materialSpecularFactor é uma variável uniforme. Quanto maior for, menor será a área de brilho na superfície do objeto. A variável materialSpecularIntensity também é usada para determinar o brilho dos destaques. Observe que todas essas são propriedades do material, não da luz. Por exemplo, o metal reflete a luz e, portanto, brilha. Mas a madeira não reflete a luz e você nunca vê brilho nas árvores (é claro, se a superfície estiver seca e assim por diante).

No código acima, a luz possui uma propriedade especularIntensity. Mas só deve ser usado para fins de depuração para destacar destaques de uma fonte de luz específica. Na versão de lançamento do código, esse coeficiente deve ser igual a um ou ser completamente removido do código.

Finalmente, os três componentes são somados e o resultado é retornado:

// ...

return corambiente + cordifusa +corespecular;
}

Não é tão difícil, certo?

Fonte de luz pontual

Uma fonte pontual de luz é, por exemplo, uma lâmpada acesa. A luz da lâmpada é direcionada em todas as direções. Portanto, uma fonte de luz pontual não é caracterizada pela direção da luz, mas sim pela posição da fonte no espaço:

estrutura PointLight(
posição vec3;

cor vec3;
float intensidadeambiente;
float difusaIntensidade;
float especularIntensidade; // para fins de depuração, deve ser definido como 1.0
} ;

A iluminação de uma fonte de luz pontual é facilmente calculada usando o procedimento calcDirectionalLight já existente:

vec4 calcPointLight(vec3 normal, vec3 fragmentToCamera,
Luz PointLight) (
vec3 lightDirection = normalize (fragmentPos - light. position );
float distância = comprimento (fragmentPos - light. posição);
float pointFactor = 1,0 / (1,0 + pow(distância, 2));

DirectionalLight tempDirectionalLight = DirectionalLight(
luzDireção,
luz. cor,
luz. ambienteIntensidade
luz. difusaIntensidade
luz. intensidade especular
) ;
return pointFactor * calcDirectionalLight(normal, fragmentToCamera,
tempDirectionalLight);
}

Tendo as coordenadas do fragmento e da fonte de luz, você pode calcular facilmente a direção da luz para um determinado fragmento através da diferença de vetores. O multiplicador pointFactor reflete o fato de que a luz atenua com o quadrado da distância à sua fonte (de acordo com a fórmula para a dependência da área da superfície de uma esfera no raio). Ao calcular pointFactor, um adicional é adicionado ao divisor para evitar a possibilidade de divisão por zero. Depois disso, tudo é calculado exatamente da mesma forma que para a luz direcional.

Holofote

Um exemplo desta fonte de luz é uma lanterna. É semelhante a uma fonte de luz pontual, só que possui adicionalmente uma direção e um ângulo de influência (corte):

estrutura SpotLight (
direção vec3;
posição vec3;
corte flutuante;

cor vec3;
float intensidadeambiente;
float difusaIntensidade;
float especularIntensidade; // para fins de depuração, deve ser definido como 1.0
} ;

Procedimento relevante:

vec4 calcSpotLight(vec3 normal, vec3 fragmentToCamera,
Luz Spotlight) (
vec3 spotLightDirection=normalize(fragmentPos-light.position);
float spotAngleCos = ponto (spotLightDirection, light. direção);
atenuação flutuante = (1,0 - 1,0 * (1,0 - spotAngleCos) /
(1,0 - corte leve ) ) ;
float spotFactor = float (spotAngleCos > light. cutoff ) * atenuação;

PointLight tempPointLight = PointLight(
luz. posição
luz. cor,
luz. ambienteIntensidade
luz. difusaIntensidade
luz. ambienteIntensidade
) ;
return spotFactor * calcPointLight(normal, fragmentToCamera,
tempPointLight);
}

A direção da luz é calculada da mesma forma que para uma fonte pontual. Em seguida, é calculado o cosseno do ângulo entre esta direção e a direção especificada nas propriedades da própria fonte de luz. Usando a expressão float(spotAngleCos > light.cutoff) a luz é rigidamente cortada no ângulo especificado. O multiplicador de atenuação adiciona suave atenuação da luz à medida que os fragmentos se afastam da direção da luz especificada nas propriedades da fonte. Depois disso, todos os cálculos são reduzidos a cálculos para uma fonte de luz pontual.

Correção de gama

Todo o procedimento principal no fragment shader é assim:

void principal() (
//normal deve ser corrigido após interpolação
vec3 normal = normalizar (fragmentoNormal);
vec3 fragmentToCamera = normalize(cameraPos - fragmentPos);

vec4 directColor = calcDirectionalLight(normal, fragmentToCamera,
direcionalLuz) ;
vec4 pointColor = calcPointLight(normal, fragmentToCamera,
pontoLuz);
vec4 spotColor = calcSpotLight(normal, fragmentToCamera, spotLight);
vec4 linearColor = textura(textureSampler, fragmentUV) *
(vec4(materialEmission, 1) + directColor +
pointColor + spotColor);

vec4 gama = vec4 (vec3 (1,0 / 2,2) , 1 ) ;
cor = pow(linearColor, gama); // cor corrigida por gama
}

Não preste muita atenção ao materialEmissão. Esta é apenas mais uma propriedade do material que adiciona seu próprio brilho. Muitos objetos brilham sozinhos. Pegue as mesmas lâmpadas que servem de fonte de luz para outros objetos. Deveríamos ser capazes de vê-los na escuridão total, mesmo que as lâmpadas não sejam iluminadas por nenhuma outra fonte de luz, certo?

O que realmente merece atenção é a correção gama, que consiste em elevar todos os componentes da luz à potência de 1/2,2. Até agora temos trabalhado no espaço de cores linear, partindo do pressuposto de que uma cor com intensidade 1,0 é duas vezes mais brilhante que uma cor com intensidade 0,5. O problema é que o olho humano não percebe o brilho de forma linear. Portanto, para obter uma iluminação realista, é necessário realizar a correção gama após todos os cálculos no espaço linear.

Deve-se levar em consideração que, ao salvar uma imagem, os editores gráficos modernos também realizam a correção gama. Portanto, antes de usar texturas, é necessário cancelar esta correção gama. Felizmente, não é difícil.

Basta substituir todas as constantes no código de carregamento de textura:

GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
GL_COMPRESSED_RGBA_S3TC_DXT5_EXT

GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT
GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT
GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT

… respectivamente. Isto indicará que uma correção gama foi aplicada à imagem e precisa ser desfeita. OpenGL cuidará do resto.

Em aplicações reais, o parâmetro gama (temos gama = 2,2) fica melhor colocado nas configurações do programa para que o usuário possa, se desejar, ajustá-lo levemente ao seu monitor.

Conclusão

É hora de ver as fotos!

Aqui vemos os diferentes componentes de iluminação. Da esquerda para a direita, de cima para baixo: fundo, difuso, refletido, todos os três juntos. Como você pode ver, um modelo de toro foi adicionado à cena. Devido ao posicionamento complexo das normais, este modelo é recomendado para testes de iluminação.

Várias fontes de luz. Da esquerda para a direita, de cima para baixo: spot light branco, spot light vermelho, spot light azul, todos os três juntos.

Deixe-me observar novamente que o mesmo método de iluminação pode ter implementações diferentes. Por exemplo, você pode definir as propriedades do material para cor ambiente, difusa e especular, o que permitirá desenhar objetos vermelhos que difundem o verde e possuem realces azuis. Em algumas implementações de iluminação Phong, vi a iluminação de fundo ser calculada uma vez, em vez de para cada luz. Também vi implementações em que a luz de uma fonte pontual desaparecia não apenas em proporção ao quadrado da distância até ela (d * d), mas de acordo com uma fórmula mais geral (no estilo A + B*d + C* d*d). Alguém faz da intensidade ambiente e da intensidade difusa uma propriedade não apenas da fonte de luz, mas também do material. Não tenho certeza do quanto tudo isso tem a ver com a iluminação realista. Mas você pode brincar com tudo isso como lição de casa.

Obras dos anos. Voloshin Maximiliano. VALOR DE UM POETA. 1. Edite o poema como o texto de um despacho estrangeiro: secura, clareza, pressão - cada palavra está em alerta.

Para recortar letra por letra numa pedra dura e apertada: Quanto mais esparsas as palavras, mais intenso é o seu poder. A carga volitiva do pensamento é igual às estrofes silenciosas.

Apague do dicionário as palavras “Beleza”, “Inspiração” - o vil jargão dos rimadores.Para o poeta - entendimentos: Verdade, design, plano, equivalência, concisão e precisão. Num ofício sóbrio e árduo há a inspiração e a honra de um poeta: aguçar a vigilância transcendental em matéria de surdos-mudos. Voloshin M.A. Biblioteca: Biblioteca Pública Universal Científica Regional de Oryol em homenagem. I A. Bunina. - M., ; Obras selecionadas: Em 2 volumes.

M., ; Fumaça Vermelha: Histórias. - M., ; Gladyshev da empresa de reconhecimento: Histórias. - M., ; Escalão; Inevitabilidade: romances. Ele fez muitas traduções de poetas Mari e Udmurt. De vez em quando eu também tentava fazer prosa. Op. Maximilian Aleksandrovich Voloshin () é um dos maiores poetas do primeiro terço do século XX. É um artista talentoso, um letrista multifacetado, que percorreu o caminho dos poemas simbolistas e esotéricos à poesia cívico-jornalística e científico-filosófica, passando pelas predileções antroposóficas - até ao “ideal da Cidade de Deus”.

A publicação proposta dá ao leitor a oportunidade de conhecer não só as melhores obras poéticas de Voloshin, mas também as suas mais interessantes obras sobre estética, prosa de memórias, jornalismo e cartas relacionadas com acontecimentos dramáticos da vida dos países. Autor. Voloshin Maximiliano. Todos os poemas do autor. Trabalhar. O valor do poeta. 2. Estrelas. Crie coleções favoritas de autores e poemas!

Converse com pessoas que pensam como você! Escreva resenhas, participe de duelos e competições de poesia! Junte-se aos melhores! Obrigado por se juntar ao Poembook! Uma carta com dados de acesso à conta foi enviada para seu e-mail!

Você deve fazer login dentro de 24 horas. Caso contrário, a conta será excluída! Os usuários registrados recebem muitos benefícios: Publique poesia - realize seu talento! Crie coleções favoritas de autores e poemas! Converse com pessoas que pensam como você! Escreva resenhas, participe de duelos e competições de poesia! Maximiliano Voloshin. Descrição. Maximilian Aleksandrovich Voloshin é um dos maiores poetas do primeiro terço do século XX.

É um artista talentoso, um letrista multifacetado, que percorreu o caminho dos poemas simbolistas e esotéricos à poesia cívico-jornalística e científico-filosófica, passando pelas predileções antroposóficas - até ao “ideal da Cidade de Deus”. A publicação proposta dá ao leitor a oportunidade de conhecer não apenas as melhores obras poéticas de Voloshin, mas também suas mais interessantes obras sobre estética, prosa de memórias, jornalismo e cartas relacionadas ao drama.

Obras e cartas selecionadas. M. A. Voloshin. Preço. esfregar. Maximilian Aleksandrovich Voloshin é um dos maiores poetas do primeiro terço do século XX. É um artista talentoso, um letrista multifacetado, que percorreu o caminho dos poemas simbolistas e esotéricos à poesia cívico-jornalística e científico-filosófica, passando pelas predileções antroposóficas - até ao “ideal da Cidade de Deus”.

Voloshin M.A., O Valor do Poeta: Obras e Cartas Selecionadas. série: Nova Biblioteca de Clássicos Russos: exemplar obrigatório Desfile, cidade, página, Descrição do livro. Maximilian Aleksandrovich Voloshin () é um dos maiores poetas do primeiro terço do século XX. É um artista talentoso, um letrista multifacetado, que percorreu o caminho dos poemas simbolistas e esotéricos à poesia cívico-jornalística e científico-filosófica, passando pelas predileções antroposóficas - até ao “ideal da Cidade de Deus”.

Categorias Pós-navegação

Sem uma fonte de luz a imagem não é visível. Para inicializar a fonte e habilitar o processador para calcular o impacto da fonte nos objetos, basta executar os comandos: glEnable(gl_lighting); // habilita o modo de análise de iluminação

GlEnable(gl_light0); //inclui uma fonte específica (zero) na cena, com suas características

Para desabilitar uma fonte, use a função Disable(). Por padrão, a fonte de luz está localizada no espaço com coordenadas (0,0,∞). Você pode criar uma fonte de luz em qualquer lugar do espaço da imagem.

A biblioteca OpenGl suporta quatro tipos de fontes de luz:

  • iluminação de fundo (iluminação ambiente),
  • fontes pontuais,
  • holofotes,
  • fontes de luz remotas (luz distante).
Cada fonte de luz possui seu próprio conjunto de características.
As características da fonte de luz correspondem aos parâmetros do modelo Phong.
Para definir parâmetros vetoriais, use a função glLightfv(), que possui o seguinte formato:

glLightfv(fonte, parâmetro, ponteiro_para_array);

Existem quatro parâmetros vetoriais que determinam a posição e direção dos raios fonte e a composição de cores de seus componentes - fundo, difuso e especular.
Para definir parâmetros escalares em OpenGL, use a função glLightf():

glLightf(fonte, parâmetro, valor);

Suponhamos, por exemplo, que você queira incluir na cena a fonte GL_LIGHT0, que deve estar localizada no ponto (1.0, 2.0, 3.0). A posição de origem é salva no programa como um ponto em coordenadas uniformes:

GLfloat light0_pos=(1,0, 2,0, 3,0, 1,0);

Se a quarta componente deste ponto for zero, então a fonte pontual se transforma em uma fonte remota, para a qual apenas a direção dos raios é significativa:

GLfloat light0_dir=(1,0, 2,0, 3,0, 0,0);

Em seguida, é determinada a composição de cores do fundo, difusão e componentes especulares da fonte. Se no exemplo em consideração a fonte tiver um componente especular de cor branca, e os componentes de fundo e difusão forem vermelhos, então o fragmento do programa que forma a fonte terá a seguinte aparência:

GLfloat difisse0= (1,0, 0,0, 0,0, 1,0);

GLfloat ambiente0=(1,0, 0,0, 0,0, 1,0);

GLfloat especular0=(1,0, 1,0, 1,0, 1,0);

GlEnable(GL_LIGHTING);

GlEnable(GL_LIGHT0);

GlLightfv(GL_LIGHT0, GL_POSITION, light0_pos);

GlLightfv(GL_LIGHT0, GL_AMBIENT, ambiente0);

GlLightfv(GL_LIGHT0, GL_DIFFUSE, difuso0);

GlLightfv(GL_LIGHT0, GL_SPECULAR, especular0);

Você também pode incluir iluminação de fundo global em sua cena, que não está associada a nenhuma fonte de luz individual. Se, por exemplo, você quiser destacar todos os objetos na cena com branco, você deve incluir o seguinte trecho de código no programa:

GLfloat global_ambient=(0,1, 0,1, 0,1, 1,0);

GlLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient);

No modelo de iluminação, o termo que leva em consideração a distância até a fonte tem a forma:

K= 1/(a+ b*d+ c*d^2)

E componentes constantes, lineares e quadráticos. Os coeficientes correspondentes para cada fonte são definidos individualmente usando a função de configuração de parâmetros escalares, por exemplo:

GlLightf(GL_LIGHT0, GL_CONSTANT_ATTENATION, a);

Para converter uma fonte pontual em um holofote, você precisa especificar a direção do feixe do holofote (GL_SPOT_DIRECTION), o indicador da função de distribuição de intensidade (GL_SPOT_EXPONENT) e o ângulo de dispersão do feixe (GL_SPOT_CUTTOF). Esses parâmetros são definidos usando as funções glLightf() e glLightfv().

Os parâmetros padrão para fontes de luz são mostrados na Tabela 3.

Configurações padrão para luzes

Tabela 3

Nome do parâmetro Valor padrão Contente
GL_AMBIENT (0.0, 0.0, 0.0, 1.0) intensidade de luz ambiente RGBA
GL_DIFFUSE (1.0, 1.0, 1.0, 1.0) intensidade de luz RGBA difusa
GL_ESPECULAR (1.0, 1.0, 1.0, 1.0) intensidade de luz especular RGBA
GL_POSIÇÃO (0.0, 0.0, 1.0, 0.0) (x, y, z, w) posição da luz
GL_SPOT_DIRECTION (0.0, 0.0, -1.0) (x, y, z) direção do foco
GL_SPOT_EXPONENT 0.0 expoente de destaque
GL_SPOT_CUTOFF 180.0 ângulo de corte do holofote
GL_CONSTANT_ATTENUATION 1.0 fator de atenuação constante
GL_LINEAR_ATTENUATION 0.0 fator de atenuação linear
GL_QUADRATIC_ATTENUATION 0.0 fator de atenuação quadrático