{
Bem-vindo à lição nove. Até agora, você deve ter um bom entendimento do OpenGL.
“CKER: Se não, deve ser culpa do meu tradutor…”.
(Myling acrescentou: Meu pecado é ainda maior, haha)
Você aprendeu todos os detalhes da configuração de uma janela OpenGL.
Aprenda a mapear e adicionar mistura de luz e cores (transparência) a objetos giratórios.
Esta lição deve ser considerada um tutorial intermediário.
Você aprenderá como mover um bitmap em uma cena 3D e remover pixels pretos do bitmap (usando mistura de cores).
A seguir, pinte as texturas em preto e branco e, finalmente, você aprenderá a criar cores ricas,
E misture texturas com cores diferentes entre si para obter um efeito de animação simples.
Faremos modificações com base no código da primeira lição. Primeiro adicione algumas variáveis no início do código-fonte do programa.
Reescrevi todo o código para maior clareza.
}
Var
h_RC: HGLRC; // Contexto de renderização (tabela de descrição de sombreamento).
h_DC: HDC; // Contexto do dispositivo (tabela de descrição do dispositivo)
h_Wnd: HWND; // identificador da janela
h_Instance: HINST; // Instância do programa (instância).
teclas: Array[0..255] Of Boolean; // Array para rotinas de teclado
{As linhas a seguir foram adicionadas recentemente.
twinkle e tp são variáveis booleanas, o que significa que só podem ser definidas como TRUE ou FALSE.
brilho é usado para rastrear se o efeito de cintilação está ativado.
tp é usado para verificar se a tecla 'T' está pressionada ou liberada.
(tp=TRUE quando pressionado, tp=FALSE quando liberado).}
twinkle : Boolean; // Estrelas cintilantes (novas)
tp : Boolean; // 'T' está pressionado?
{Agora vamos criar uma estrutura.
A palavra estrutura parece assustadora, mas não é. (Este é o tipo de registro do Delphi)
Uma estrutura usa um conjunto de tipos simples de dados (e variáveis, etc.) para expressar uma combinação maior de dados semelhantes.
Sabemos que estamos acompanhando as estrelas.
Você pode ver as estrelas abaixo;
Cada estrela possui três valores inteiros de cores. Um vermelho (r), um verde (g) e um azul (b).
Além disso, cada estrela está a uma distância diferente do centro da tela,
E pode ser qualquer ângulo de 360 graus tendo o centro da tela como origem.
Um número de ponto flutuante de dist para controlar a distância.
O número de ponto flutuante do ângulo acompanha o valor do ângulo da estrela.
Então usamos um conjunto de dados para descrever a cor, a distância e o ângulo das estrelas na tela.
Infelizmente estamos rastreando mais de uma estrela.
Mas não há necessidade de criar 50 valores vermelhos, 50 valores verdes, 50 valores azuis, 50 valores de distância
e 50 valores de ângulo, e apenas crie uma estrela de matriz. }
Tipo
stars = Record // Cria uma estrutura para estrelas, chamada stars
r, g, b: inteiro; // cor das estrelas
dist: GLfloat; //A distância da estrela ao centro
ângulo: GLfloat; //O ângulo atual da estrela
Fim;
Var
star : Array[0..49] Of stars; // Use a estrutura 'stars' para gerar um array 'star' contendo 50 elementos;
{Em seguida, configuramos diversas variáveis de rastreamento:
A variável de distância (zoom) da estrela ao observador,
O ângulo (inclinação) em que vemos as estrelas,
e o giro variável que faz com que a estrela cintilante gire em torno do eixo Z.
A variável loop é usada para desenhar 50 estrelas.
Texture[1] é usado para armazenar uma textura em preto e branco.
Se você precisar de mais texturas,
Você deve aumentar o tamanho da matriz de texturas para o número de texturas que decidir usar.
}
zoom : GLfloat = -15.0; // A distância da estrela ao observador
tilt : GLfloat = 90.0; // A inclinação da estrela
spin : GLfloat; // A rotação da estrela cintilante
loop : GLuint; // Variável de loop global
textura: Array[0..1] Of GLuint; // Armazena uma textura
Procedimento glGenTextures(n: GLsizei; Var texturas: GLuint externo stdcall);
opengl32;
Procedimento glBindTexture(destino: GLenum; textura: GLuint externo);
opengl32;
{
O código imediatamente acima é o código que usamos para carregar a textura.
Não vou explicar esse código em detalhes.
Este é exatamente o mesmo código que usamos nas Lições 6, 7 e 8.
O bitmap carregado desta vez é chamado star.bmp.
Aqui usamos glGenTextures(1, &texture[0]),
para gerar uma textura. A textura usa filtragem linear.
}
Function LoadTexture: boolean; //Carrega o bitmap e converte-o em uma textura
Var
Status: booleano; // Indicador de status
TextureImage: Array[0..1] Of PTAUX_RGBImageRec; // Cria espaço de armazenamento de textura
Começar
Estado := falso;
ZeroMemory(@TextureImage, sizeof(TextureImage)); // Define o ponteiro para NULL
TextureImage[0] := LoadBMP('Star.bmp');
Se TextureImage[0] <> Nulo Então
Começar
Status := VERDADEIRO; // Define o status como VERDADEIRO;
glGenTextures(1, textura[0]); // Cria textura
// Cria o mapa de filtro mais próximo
glBindTexture(GL_TEXTURE_2D, textura[0]);
//Gera textura
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0].tamanhoX,
TexturaImagem[0].tamanhoY, 0, GL_RGB, GL_UNSIGNED_BYTE,
TexturaImage[0].data);
Fim;
Se atribuído(TextureImage[0]) Then // Se a textura existe
Se atribuído(TextureImage[0].data) Then // Se a imagem de textura existe
TextureImage[0].data := Nil; // Libera a memória ocupada pela imagem de textura
TextureImage[0] := Nil; // Libera a estrutura da imagem
resultado := Status; // Status de retorno
Fim;
{Defina o modo de renderização OpenGL em glInit(). Não vamos usar testes de profundidade aqui,
Se você usar o código da Lição 1,
Confirme se glDepthFunc(GL_LEQUAL) e glEnable(GL_DEPTH_TEST) foram removidos.
Caso contrário, os resultados que você verá serão uma bagunça.
Aqui usamos mapeamento de textura,
Portanto, certifique-se de adicionar qualquer código que não tenha sido incluído na primeira lição.
Você notará que habilitamos o mapeamento de textura misturando cores.
}
Procedimento glInit();
Começar
If (Not LoadTexture) Then // Chama a sub-rotina de carregamento de textura (novo)
exit; // Se falhar ao carregar, saia (novo)
glEnable(GL_TEXTURE_2D); // Habilita mapeamento de textura
glShadeModel(GL_SMOOTH); // Habilita suavização de sombra
glClearColor(0,0, 0,0, 0,0, 0,5);
glClearDepth(1.0); //Definir o buffer de profundidade
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Correção de perspectiva muito boa
glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Define a função de mistura de cores para obter um efeito translúcido
glEnable(GL_BLEND); // Habilita a mistura de cores
{A seguir está o novo código.
O ângulo inicial, a distância e a cor de cada estrela são definidos.
Você notará como é fácil modificar as propriedades de uma estrutura.
Todas as 50 estrelas serão cicladas.
Para alterar o ângulo de star[1] tudo o que precisamos fazer é star[1].angle=um determinado valor;
É tão simples! }
For loop := 0 To 49 Do // Cria um loop para definir todas as estrelas
Começar
star[loop].angle := 0.0; // Todas as estrelas começam do ângulo zero
{A distância do loop-ésima estrela ao centro é dividida pelo valor do loop pelo número total de estrelas e depois multiplicada por 5,0.
Basicamente, isso faz com que a última estrela fique um pouco mais distante do centro do que a estrela anterior.
Desta forma, quando o loop é 50 (a última estrela), o loop dividido por num é exatamente 1,0.
A razão pela qual precisamos multiplicar por 5,0 é porque 1,0*5,0 é 5,0.
『CKER: Bobagem, bobagem! Por que esse estrangeiro se parece com Kong Yiji! :)』
5.0 já está bem próximo da borda da tela. Não quero que as estrelas voem para fora da tela, então 5.0 é a melhor escolha.
Claro, se você definir a cena mais profundamente na tela,
Talvez você pudesse usar um valor maior que 5,0, mas as estrelas pareceriam menores (tudo por causa da perspectiva).
Você também notará que a cor de cada estrela é um número aleatório de 0 a 255.
Você pode estar se perguntando por que o intervalo de valores de cores aqui não é o intervalo normal do OpenGL de 0,0 a 1,0.
A função de configuração de cores que usamos aqui é glColor4ub em vez da glColor4f anterior.
ub significa que o parâmetro é do tipo Unsigned Byte.
A faixa de valores de um byte é de 0 a 255.
Parece mais fácil usar o valor de byte para obter um número inteiro aleatório do que um número aleatório de ponto flutuante.
}
star[loop].dist := (Trunc(loop) / 50) * 5.0; // Calcula a distância da estrela ao centro
star[loop].r := random(256); // Define um componente vermelho aleatório para star[loop]
star[loop].g := random(256); // Define um componente vermelho aleatório para star[loop]
star[loop].b := random(256); // Define um componente vermelho aleatório para star[loop]
Fim;
Fim;
{
Agora nos voltamos para o código de desenho glDraw().
Se você estiver usando o código da Lição 1, exclua o código antigo do DrawGLScene e simplesmente copie o código abaixo.
Na verdade, o código da primeira lição tem apenas duas linhas, então não há muito o que cortar.
}
Procedimento glDraw();
Começar
glClear(GL_COLOR_BUFFER_BIT Ou GL_DEPTH_BUFFER_BIT); // Limpa a tela e o buffer de profundidade;
glBindTexture(GL_TEXTURE_2D, textura[0]); //Selecione a textura
For loop:= 0 To 49 Do // Loop para definir todas as estrelas
Começar
glLoadIdentity(); // Antes de desenhar cada estrela, redefine a matriz de observação do modelo
glTranslatef(0.0, 0.0, zoom); // Aprofunde-se na tela (usando o valor de 'zoom')
glRotatef(tilt, 1.0, 0.0, 0.0); // Ângulo de visão de inclinação (usando o valor de 'tilt')
{
Agora vamos mover as estrelas.
A estrela começa no centro da tela.
A primeira coisa que precisamos fazer é girar a cena ao longo do eixo Y.
Se girarmos 90 graus, o eixo X não fica mais da esquerda para a direita, ele sai da tela de dentro para fora.
Para ficar mais claro, vamos dar um exemplo. Imagine que você está parado no meio de uma casa.
Agora imagine que a parede à sua esquerda diz -x, e a parede à sua frente diz -z.
A parede à direita é +x, e a parede atrás de você é +z.
Se a casa inteira estiver virada 90 graus para a direita, mas você não se mover, a parede da frente será -x em vez de -z.
Todas as outras paredes também se movem. -z aparece à direita, +z aparece à esquerda e +x aparece atrás de você.
Você está louco? Ao girar a cena, alteramos a orientação dos planos x e z.
A segunda linha de código move um valor positivo ao longo do eixo x.
Normalmente, um valor positivo no eixo x significa mover-se para o lado direito da tela (ou seja, a direção positiva usual do eixo x).
Mas aqui, como giramos o sistema de coordenadas em torno do eixo y, a direção positiva do eixo x pode ser em qualquer direção.
Se girarmos 180 graus, os lados esquerdo e direito da tela serão espelhados.
Portanto, quando nos movemos ao longo do eixo x positivo, este pode ser para a esquerda, para a direita, para a frente ou para trás.
}
glRotatef(star[loop].angle, 0.0, 1.0, 0.0); //Gira para o ângulo da estrela atualmente desenhada;
glTranslatef(star[loop].dist, 0.0, 0.0); // Avança ao longo do eixo X
{
O código a seguir tem um pequeno truque.
As estrelas são na verdade uma textura plana.
Agora você desenha um quadrado plano no centro da tela e aplica uma textura, e fica bem.
Tudo está como você imaginou. Mas quando você gira 90 graus ao longo do eixo y,
Os únicos dois lados da textura na tela voltados para você são os lados direito e esquerdo. Parece apenas uma linha tênue.
Não é isso que queremos. Queremos que as estrelas estejam sempre voltadas para nós, não importa como a tela seja girada ou inclinada.
Conseguimos isso cancelando quaisquer rotações feitas na estrela antes de desenhá-la.
Você pode reverter a rotação para neutralizá-la. Quando inclinamos a tela, na verdade giramos a estrela em seu ângulo atual.
Ao inverter a ordem, "anti-rotamos" a estrela em seu ângulo atual. Ou seja, a estrela é girada pelo valor negativo do ângulo atual.
aquilo é,
Se girarmos a estrela 10 graus, nós a giraremos -10 graus para que a estrela fique voltada para a tela novamente naquele eixo.
A primeira linha abaixo cancela a rotação ao longo do eixo y. Então, também precisamos compensar a inclinação da tela ao longo do eixo x.
Para fazer isso, só precisamos girar a tela novamente -inclinar.
Depois de cancelar a rotação nos eixos x e y, a estrela está agora completamente voltada para nós novamente.
}
glRotatef(-star[loop].angle, 0.0, 1.0, 0.0); // Cancela o ângulo da estrela atual
glRotatef(-tilt, 1.0, 0.0, 0.0); // Cancela a inclinação da tela
{Se o brilho for VERDADEIRO, primeiro desenhamos uma estrela não girada na tela:
Subtraia o número atual de estrelas (loop) do número total de estrelas (num) e depois subtraia 1.
para extrair as diferentes cores de cada estrela (isso é feito porque o intervalo do loop é de 0 a num-1).
Por exemplo, quando o resultado é 10, usamos a cor da estrela nº 10.
Desta forma as cores das estrelas adjacentes são sempre diferentes. Não é uma boa ideia, mas funciona.
O último valor é o componente do canal alfa. Quanto menor o valor, mais escura é a estrela.
Como o brilho está ativado, cada estrela acabará sendo desenhada duas vezes.
O programa será executado mais lentamente, dependendo do desempenho da sua máquina.
Mas as cores das estrelas desenhadas em duas passagens misturam-se e produzem um grande efeito.
Ao mesmo tempo, como as estrelas na primeira passagem não giraram, as estrelas depois de ativarem o brilho parecem um efeito de animação.
(Se você não entendeu aqui, vá e veja você mesmo o efeito de execução do programa.)
É importante notar que colorir texturas é fácil.
Mesmo que a textura em si seja preta e branca, ela mudará para a cor que selecionamos antes de pintá-la.
Além disso, também é importante notar que o valor da cor que usamos aqui é do tipo byte,
em vez dos habituais números de ponto flutuante. Até o componente do canal alfa é assim. }
If (brilho) Then // Ativa o efeito de brilho
Começar
// Especifique uma cor usando um valor de byte
glColor4ub(estrela[(50 - loop) - 1].r, estrela[(50 - loop) - 1].g,
estrela[(50 - loop) - 1].b, 255);
glBegin(GL_QUADS); // Começa a desenhar quadrantes mapeados com textura
glTexCoord2f(0,0, 0,0);
glVertex3f(-1,0, -1,0, 0,0);
glTexCoord2f(1,0, 0,0);
glVertex3f(1,0, -1,0, 0,0);
glTexCoord2f(1,0, 1,0);
glVertex3f(1,0, 1,0, 0,0);
glTexCoord2f(0,0, 1,0);
glVertex3f(-1,0, 1,0, 0,0);
glEnd(); // Fim do desenho do quadrilátero
Fim;
{
Agora desenhe a segunda passagem das estrelas.
A única diferença do código anterior é que desta vez as estrelas serão definitivamente desenhadas e, desta vez, as estrelas girarão em torno do eixo z.
}
glRotatef(spin, 0.0, 0.0, 1.0); // Gira a estrela em torno do eixo z
// Especifique uma cor usando um valor de byte
glColor4ub(estrela[loop].r, estrela[loop].g, estrela[loop].b, 255);
glBegin(GL_QUADS); // Começa a desenhar quadrantes mapeados com textura
glTexCoord2f(0,0, 0,0);
glVertex3f(-1,0, -1,0, 0,0);
glTexCoord2f(1,0, 0,0);
glVertex3f(1,0, -1,0, 0,0);
glTexCoord2f(1,0, 1,0);
glVertex3f(1,0, 1,0, 0,0);
glTexCoord2f(0,0, 1,0);
glVertex3f(-1,0, 1,0, 0,0);
glEnd(); // Fim do desenho do quadrilátero
{O código abaixo representa o movimento das estrelas.
Aumentamos o valor do spin para girar todas as estrelas (revolução).
Em seguida, aumente o ângulo de rotação de cada estrela em loop/num.
Isso faz com que estrelas mais distantes do centro girem mais rápido. Por fim, reduza a distância de cada estrela ao centro da tela.
Parece que as estrelas são constantemente sugadas para o centro da tela. }
spin := spin + 0.01; // A revolução da estrela
star[loop].angle := star[loop].angle + Trunc(loop) / 50;
star[loop].dist := star[loop].dist - 0.01; // Altera a distância da estrela ao centro
{As próximas linhas verificam se a estrela tocou o centro da tela.
Quando a estrela atinge o centro da tela, damos a ela uma nova cor e a movemos 5 unidades para fora.
A estrela embarcará em sua jornada de volta ao centro da tela. }
If (star[loop].dist < 0.0) Then // A estrela atingiu o centro?
Começar
star[loop].dist := star[loop].dist + 5.0; // Move 5 unidades para fora
star[loop].r := random(256); // Atribuir um novo componente vermelho
star[loop].g := random(256); // Atribuir um novo componente verde
star[loop].b := random(256); // Atribuir um novo componente azul
Fim;
Fim;
Fim;
{
Agora adicionamos o código para monitorar o teclado.
Vá para WinMain(). Encontre a linha SwapBuffers(hDC).
Adicionaremos o código de monitoramento do teclado após esta linha.
O código verificará se a tecla T foi pressionada.
Se a tecla T for pressionada e liberada, o código do bloco if será executado.
Se o brilho for FALSO, ele se tornará VERDADEIRO.
vice-versa. Enquanto a tecla T for pressionada, tp se tornará TRUE.
Isso evita que o código dentro do bloco seja executado repetidamente se você continuar pressionando a tecla T.
}
If (keys[ord('T')] And Not tp) Then // Se a tecla T foi pressionada e o valor tp é FALSE
Começar
tp := TRUE; // Se sim, defina tp como TRUE;
twinkle := Não twinkle; // Inverte o valor de twinkle;
Fim;
{
O código abaixo verifica se a tecla T foi liberada.
Nesse caso, defina tp=FALSE.
A menos que o valor de tp seja FALSO,
Caso contrário, nada acontecerá enquanto mantiver pressionada a tecla T. Portanto, esta linha de código é importante.
}
If (Not keys[Ord('T')]) Then // A tecla T foi liberada?
Começar
tp := FALSE; // Se sim, tp é FALSO
Fim;
{O código restante verifica se as teclas de seta para cima e para baixo, tecla de página para cima ou tecla de página para baixo estão pressionadas. }
If (keys[VK_UP]) Then // A tecla de seta para cima está pressionada?
tilt := tilt - 0.5; // Inclina a tela para cima
If (keys[VK_DOWN]) Then // A tecla de seta para baixo está pressionada?
tilt := tilt + 0.5; // Inclina a tela para baixo
If (keys[VK_PRIOR]) Then // A tecla page up está pressionada?
zoom := zoom - 0,2; // Menos zoom
If (keys[VK_NEXT]) Then // A tecla page down está pressionada?
zoom := zoom + 0,2; // Aumentar o zoom
{
Nesta lição, tento explicar como carregar uma textura de bitmap em tons de cinza,
Após remover a cor de fundo (usando cores misturadas), pinte-o novamente e, finalmente, faça-o se mover na cena 3D.
Eu mostrei como criar lindos efeitos de cores e animação.
O princípio de implementação é sobrepor uma cópia do bitmap ao bitmap original.
Até agora, contanto que você tenha uma boa compreensão de tudo o que lhe ensinei,
Você poderá criar sua própria demonstração 3D sem nenhum problema.
Todos os princípios básicos cobertos! }
//========myling:
//As aulas 1 a 9 foram traduzidas. Como disse NEHE, o conhecimento básico foi basicamente explicado.
//Eu olhei os tutoriais a seguir e eles parecem ter sido escritos por outras pessoas. Se houver bons exemplos, irei segui-los seletivamente.
//Renovado, estou tão cansado, tire uma soneca :), até a próxima