Buosi.org
   
Contato: cbuosi@gmail.com  
Buosi.org
Bem vindo
 

 

Primeira Lição.

Este tutorial ensinará a você como setar uma janela OpenGL. Esta janela poderá ser minimizada, redimensionada, setada como tela-cheia (fullscreen), setada em qualquer resolução, em qualquer cor e qualquer profundidade de cores. Este código é bastante flexível e pode ser usado em todos os projetos OpenGL que você fizer. Todos os tutoriais seguintes serão baseados neste código. Eu escrevi o código para ser flexível e poderoso ao mesmo tempo. Todos os erros são reportados. Não deverá haver vazamento de memória e o código é fácil de ler e modificar. Começarei o tutorial pulando diretamente para o código. A primeira coisa que você deverá fazer é criar um projeto no Visual C++ (De preferência o 6 com todos os Service Packs). Se você não sabe criar um projeto em Visual C++, você não deveria estar aprendendo OpenGL, mas sim Visual C++. Algumas versões do VC++ requerem que bool seja mudado para BOOL, true seja mudado para TRUE e false seja mudado para FALSE. Com essas mudanças, não haverá problema em compilar o código em VC++ 5.0 e VC++ 4.0. (Este código é nativo VC++ 6.0). Depois de criar um novo projeto 'Win32 Application' (NAO crie um 'console application') no VC++, você precisará linkar as bibliotecas OpenGl. No Visual C++, vá ao menu 'Project', 'Settings' e clique na tabulação LINK. No 'Object/Library Modules', no inicio da linha (antes do 'kernel32.lib'), você deverá incluir os seguintes valores: 'OpenGL32.lib, GLu32.lib, GLaux.lib' Depois de incluir estes valores, clique no Ok. Pronto, você já esta pronto para escrever seus aplicativos OpenGl. Nota: Alguns compiladores não definem DS_FULLSCREEN. Se você receber uma mensagem de erro reclamando sobre CDS_FULLSCREEN, você deverá adicionar a seguinte linha de código no inicio de seu programa: '#define CDS_FULLSCREEN 4' As primeiras 4 linhas incluem os headers para as bibliotecas que estamos usando.

 
	#include <windows.h>	// Arquivo Header do Windows
	#include <gl\gl.h>	// Arquivo Header da biblioteca OpenGL32
	#include <gl\glu.h>	// Arquivo Header da biblioteca GLu32
	#include <gl\glaux.h>	// Arquivo Header da biblioteca GLaux
	

Agora vamos setar as variáveis que usaremos no programa. Este programa criará uma janela OpenGl em branco, então não vamos precisar de muitas variáveis por enquanto. As variáveis que vamos setar a seguir são muito importantes e serão usadas em todos os programas OpenGL criados com este código. A primeira linha seta um 'Rendering Context' (Contexto de renderizacao). Todo programa OpenGl esta linkado a um 'Rendering Context'. Um 'Rendering Context' é oque liga as chamadas OpenGl ao 'Device Context' (Contexto de dispositivo). O 'Rendering Context' será definido como hRC. Para seu programa desenhar em uma janela será necessário criar um 'Device Context', isto é feito na segunda linha. O 'Device Context' da janela será definido como hDC. O DC conecta a janela ao GDI (Graphics Device Interface) do Windows. Na terceira linha, a variável Wind ira armazenar o 'andai' (manipulador) de nossa janela. E finalmente, a quarta linha criará uma instancia (ocorrência) para nosso programa.

 
	HGLRC hRC=NULL;		// 'Rendering Context' permanente
	HDC hDC=NULL;		// 'Device Context' do GDI
	HWND Wind=NULL;		// Armazena nosso manipulador (andai) da janela
	HINSTANCE hInstance;	// Armazena instancia da aplicação
	

A primeira linha abaixo seta um vetor que será usado para monitorar as teclas pressionadas no teclado. Há varias maneiras de monitorar teclas pressionadas, mas a vantagem de usar este tipo de monitoramento é a possibilidade de pegar varias teclas sendo pressionadas simultaneamente. A variável 'active' será usada para dizer ao nosso programa de quando a nossa janela for minimizada para a barra de tarefa ou não. Se a janela for minimizada, nos podemos fazer qualquer coisa, de suspender a sair do programa. Normalmente, se suspende (para) o programa. Desta maneira, ele não fica rodando quando o programa estiver minimizado. A variável 'fullscreen' é meio obvia. Se nosso programa estiver rodando no modo de tela-cheia, fullscreen será TRUE, se o programa estiver rodando no modo janela, fullscreen será FALSE. É importante setar como global para que todas as funções saibam quando o programa está rodando em tela-cheia ou não.

 
	bool keys[256];		//Vetor usado para a rotina de teclado
	bool active=TRUE;	//Flag de janela ativa setado como TRUE por padrão.
	bool fullscreen=TRUE;	//Flag de  Fullscreen setado como TRUE (Tela-cheia)
	

Agora temos que declarar WndProc(). A razão para fazer isto é que CreateGLWindow() faz uma referencia a WndProc, mas WndProc() vem depois do CreateGLWindow(). Em C, se quisermos acessar uma função ou seção de código que esta depois da função que estamos, temos que declarar a função que queremos acessar no topo de nosso programa. Então, na próxima linha, nos declaramos WndProc() para que CreateWindow() possa fazer referencia a ele.

 
	LRESULT	CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);// Declaração do WndProc
	

A finalidade da próxima seção de código é de redimensionar a cena OpenGL toda vez que a janela (assumindo que o programa esta no modo janela e não no modo tela-cheia) for redimensionada. Até se você não for capaz de redimensionar a janela (se estiver no modo tela-cheia), esta rotina será chamada pelo menos uma vez, quando o programa for iniciado. A cena OpenGL será redimensionada de acordo com a altura/largura da janela em que ela estiver sendo exibida.

 
	GLvoid ReSizeGLScene(GLsizei width, GLsizei height)	//Redimensiona e inicializa a janela GL
{
	if (height==0)// Previne divisão por zero
	{
	height=1;// Marca altura igual a um
	}

	glViewport(0, 0, width, height);// Reseta a Viewport corrente
	

As linhas seguintes setam a tela para uma visão de perspectiva. Significando que os objetos ficam menores. Isto cria uma cena mais realística. A perspectiva é calculada com uma visão de 45 graus baseada na altura e largura da janela. O 0.1f e 100.0f é o ponto inicial e final de quanto profundo podemos desenhar na tela. O glMatrixMode(GL_PROJECTION) indica que as 2 próximas linhas vão afetar a matriz de projeção. A matriz de projeção é responsável em adicionar perspectiva a nossa cena. O glLoadIdentity() é similar a um reset. Ele volta a matriz selecionada ao seu estado original. Depois que glLoadIdentity() for chamada, nos setamos nossa visão de perspectiva da cena. glMatrixMode(GL_MODELVIEW) indica que qualquer nova transformação irá afetar a matriz de modelagem. A matriz de modelagem é onde as informações de nossos objetos serão armazenadas. Não se preocupe se você não entender muito bem essas coisas, isto será mais bem explicados nos tutoriais seguintes. Apenas saiba que isso PRECISA ser feito se você quer uma bela cena de perspectiva.

 
	glMatrixMode(GL_PROJECTION);	// Seleciona a matriz de projeção
	glLoadIdentity();		// Reseta a matriz de projeção
	// calcula o aspecto da janela
	gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
	glMatrixMode(GL_MODELVIEW);	// Seleciona a matriz de modelagem
	glLoadIdentity();		// Reseta a matriz de modelagem
}
	

Na próxima seção, faremos toda a inicialização do OpenGL. Setaremos qual cor será usado para limpar a tela, ligaremos o buffer de profundidade, ligaremos a suavisação de cores, e etc. Esta função não será chamada até que a janela OpenGL seja criada. O retorno desta função não será utilizado por enquanto.

 
int InitGL(GLvoid)// Todas configurações do OpenGL vão aqui
{
	

A próxima linha liga a suavizacao de cores (smooth shading). Isto será explicado com maiores detalhes em outro tutorial.

 
	glShadeModel(GL_SMOOTH); // Liga a suavizacao de cores(Smooth Shading)
	

A linha seguinte seta a cor da tela quando esta for limpa. Se você não sabe como as cores funcionam, eu vou explicar rapidamente. Os valores podem variar de 0.0f a 1.0f. 0.0f sendo o mais escuro (ausente de cor) e o 1.0f sendo o mais claro. O primeiro parametro do glClearColor é a intensidade de vermelho, o segundo parametro verde e o terceiro azul (famoso RGB - Red/Green/Blue). Quanto maior (até 1.0f) for o numero, o mais claro a cor será. O quarto valor é o alpha. Por enquanto deixe-o como 0.0f, que ele será explicado em futuro tutoriais. Voce pode criar cores diferentes misturando estas tres cores primarias (RGB). Espero que voce tenha aprendido cores primarias na escola. Então voce sabe que para limpar a tela uzando a cor azul, voce usa usar o comando glClearColor(0.0f,0.0f,1.0f,0.0f) ou para limpar a tela usando um vermelho médio: glClearColor(0.5f,0.0f,0.0f,0.0f).

 
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);// Black Background
	

Estas proximas tres linhas tem a ver com buffer de profundiade (Depth Buffer). Pense em buffer de profundidade como camadas na tela. O buffer de profundidade manipula o quão profundo os objetos estão na tela. Nós não usaremos o buffer de profundidade neste exemplo, mas quase todos os programsa OpenGL que desenha em 3D irá usar o buffer de profundidade. Ele ordena qual objeto desenhar primeiro (na frente) e os demais objetos (a traz). O buffer de profundidade é uma parte muito importante do OpenGL.

 
	glClearDepth(1.0f);		// Depth Buffer Setup
	glEnable(GL_DEPTH_TEST);	// Enables Depth Testing
	glDepthFunc(GL_LEQUAL);		// The Type Of Depth Test To Do
	

Agora diremos para o OpenGL que queremos usar a melhor correção de perspectiva. Isso traz uma pequena perca de performance, mas faz a visão de perspectiva ficar bem mais bonita.

 
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);// Calculo de Perspectiva muito boa
	

Finalmente retornamos TRUE. Voce pode adicionar algum codigo aqui para retornar FALSE se ocorrer algum erro, mas por enquanto, nao vamos nos preocupar com esses detalhes.

 
	return TRUE;	// Inicializacao ocorreu OK
}
	

Esta secao é onde irão todos seus desenhos. Tudo que voce planeja mostrar na tela vai nesta parte. Todos os tutoriais irão adicionar código nesta parte. Por enquanto, tudo que vamos fazer é limar a tela, o buffer de profundidade, e resetar a cena. Nao vamos desenhar nada. O return TRUE diz ao seu programa que não houveram problemas. Se você quiser que o programa pare por algum motivo, adicione um return FALSE em algum lugar antes do return TRUE.

 
int DrawGLScene(GLvoid)// Aqui fazemos todo desenho
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Limpa a tela e buffer de profundidade.
	glLoadIdentity();// Reseta a matriz de modelo atual
	return TRUE;// Tudo OK
}
	

A proxima seção é chamada logo antes do programa ser abortado. O trabalho de KiilGLWindow() é liberar o contexto de renderização (Rendering Context), o contexto de dispositivo (Device Context) e finalmente o manupulador de janela (Window Handle). Foi adicionado varias checagems de erro. Se o programa for incapaz de destruir qualquer parte da janela, uma caixa de mensagem ira aparecer, lhe dizendo oque ocorreu. Isso torma mais fácil achar problemas no seu codigo.

 
GLvoid KillGLWindow(GLvoid)// Mata a janela de maneira correta
{
	

A primeira coisa a fazer e checar se estamos no modo de tela-cheia. Se estiver, nós mudaremos devolta para o desktop. Nós deveriamos destruir a janela antes de desabilitar o modo tela-cheia, mas em algumas placas de video, se destruirmos a janela ANTES de desabilitar a modo tela-cheia. o desktop vai ficar todo bagunçado. Portanto, sempre desabilatamos o modo tela-cheia antes. Assim evitamos maiores problemas e garantimos a compatibilidade com placas antigas (3dfx).

 
	if (fullscreen)// Esta em modo tela-cheia?
	{
	

Nós usamos ChangeDisplaySettings(NULL,0) para retornar para nosso desktop original. Passando NULL como primeiro parametro e 0 como segundo, forçãmos o Windows a usar os valores padrões (atuais) de resolução, restaurando a configuracao original do desktop. Depois disso, faremos o cursor ficar visivel novamente.

 
	ChangeDisplaySettings(NULL,0);// Se estiver, volta para o desktop
		ShowCursor(TRUE);// Mostra o ponteiro do mouse
	}
	

O Codigo abaixo checa se temos um "Rendering Context" (hRC).

 
	if (hRC)// Temos um "Rendering Context"?
	{
	

Se tivermos um "Rendering Context", o codigo abaixo vai checar se somos capazes de libera-lo (desacoplar o hRC do rDC). Note como estou checando por erros. Estou pedindo ao programa para tentar libera-lo (com wglMakeCurrent(NULL,NULL), depois eu checo se a liberação foi feita com sucesso ou não. Combinando algumas linhas de código em uma só.

 
	if (!wglMakeCurrent(NULL,NULL))// Fomos capazes de liberar o DC e RC?
		{
	

Se não fomos capazes de liberar o DC e RC, MessageBox() vai mostrar uma mensagem de erro, nos avisando do erro ocorrido. NULL significa que a mensagem não tem janela parente. O texto após o NULL é o texto que será apresentado na mensagem. "ERRO TERMINO" é o texto que aparecera em cima da mensagem (titulo). Depois temos o MB_OK, que siginifica que teremos um botão de OK em nossa mensagem. MB_ICONINFORMATION mostra um i minusculo em um circulo, dentro da mensagem.

 
	MessageBox(NULL,"Falha na liberação do DC e RC." , "ERRO TERMINO",MB_OK | 
MB_ICONINFORMATION); }

Agora tentamos apagar o "Rendering Context" (hRC). Se não tivermos sucesso, uma mensagem de erro aparecerá.

 
	if (!wglDeleteContext(hRC))// Fomos capazes de apagar o RC?
		{
	

Se nao apagou o "Rendering Context", mostra a mensagem de erro e seta hRC como NULL.

 
	MessageBox(NULL,"Falha na liberação do Rendering Context.","ERRO TERMINO",MB_OK | 
MB_ICONINFORMATION); } hRC=NULL;// Seta RC como NULL }

Agora checamos se o programa tem um Device Context e se tiver, tentamos libera-lo. Se não formos capazes de libera-lo, aparecerá uma mensagem de erro.

 
	if (hDC && !ReleaseDC(hWnd,hDC))// Fomos capazes de liberar o DC
	{
		MessageBox(NULL,"Falha na liberação do Device Context.","ERRO TERMINO",MB_OK | 
MB_ICONINFORMATION); hDC=NULL;// Seta DC como NULL }

Aqui checamos se há um handle de janela e se hjouver, tentamos destruir esta janela usando DestroyWindow(hWnd). Se não for possivel destruir a janela, aparecerá uma mensagem de erro.

 
	if (hWnd && !DestroyWindow(hWnd))// Janela destruida?
	{
		MessageBox(NULL,"Falha ao liberar hWnd.","ERRO TERMINO",MB_OK | 
MB_ICONINFORMATION); hWnd=NULL;// Seta hWnd como NULL }

A ultima coisa á fazer é de-registrar nosssa Classe Windows. Isso nos permite matar de forma correta a janela, e depois abrir outra janela sem receber a mensagem "Classe de janela já registrada".

 
	if (!UnregisterClass("OpenGL",hInstance))// Fomos capazes de de-registrar a classe janela?
	{
		MessageBox(NULL,"Falha ao de-registrar Classe.","ERRO TERMINO",MB_OK | 
MB_ICONINFORMATION); hInstance=NULL;// Seta hInstance como NULL } }

A proxima seção cria nossa janela OpenGL. The next section of code creates our OpenGL Window. Como voce pode ver, a função retorna BOOL (TRUE ou FALSE), ela tambem requer 5 parametros: Titulo da janela, largura da janela, altura da janela, bits (16/24/32), e finalmente "fullscreenflag" (TRUE para tela-cheia ou FALSE para modo janela). Retornamos um valor boorleano que nos dirá se a janela foi criada com sucesso.

 
	BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
	

Quando pedimos ao windows para achar um formato de pixel que bata com o que queremos, o numero do modo que o windows achar para nós será guardado na variavel PixelFormat.

 
	GLuint		PixelFormat;	// Mantem o resultado depois de procurar por resultado
	

wc será usado para guardar nossa Estrutura Classe Windows. Esta estrutura mantem informações sobre nossa janela. Alterando campos diferentes desta classe podemos mudar como o a janela se parece e se comporta. Toda janela pertence a uma Classe Janela. Antes de criar uma janela, voce PRECISA registrar uma classe para a janela.

 
	WNDCLASS	wc;		// Estrutura Classe Janela
	

dwExStyle e dwStyle vão guardar as informações de estilos normais e extendidos da janela. Usamos estas variaveis para mudar os estilos da janela dependendo de que modo esla estiver. (Uma janela popup para tela-cheia ou uma janela com borda para o modo-janela)

 
	DWORD		dwExStyle;	// Estilo Janela Extendido
	DWORD		dwStyle;	// Estilo Janela Normal
	

As proximas 5 linhas quardam os valors dos cantos do retangulo da janela. Usaremos este valores para para ajustar a janela de um modo em que a area de desenho seja igual a resolução que desejamos. Normalmente se criarmos uma janela 640x480, as bordas da janela ocupam um pouco da resolução

 
	RECT WindowRect;		// Grabs Rectangle Upper Left / Lower Right Values
	WindowRect.left=(long)0;	// Set Left Value To 0
	WindowRect.right=(long)width;	// Set Right Value To Requested Width
	WindowRect.top=(long)0;		// Set Top Value To 0
	WindowRect.bottom=(long)height;	// Set Bottom Value To Requested Height
	

Na proxima linha, nós fazemos a variavel fullscreen igual a fullscreenflag.

 
	fullscreen=fullscreenflag;// Seta a flag global Fullscreen
	

Na proxima seção de codigos, vamos apanhar a instancia da nossa janela, entao nós declaramos nossa classe janela. O estilo CS_HREDRAW e CS_VREDRAW força a janela a ser redesenhada toda vez que for redimensionada. CS_OWNDC cria um DC privado para a janela. Significando que o DC nao é compartilhado entre as aplicações. WndProc é o procedimento que vigia por mensagems no nosso programa. Nenhum dado extra de janela é usado entao zeramos os 2 proximos campos. Entao setamos a instancia. Depois setamos o hIcon a NULL significando que não queremos um ICONE na janela, e para cursor de mouse usamos o ponteiro padrão. A cor de fundo não importa (setamos isso no GL). Não queremos menu nessa janela então setamos como NULL, e o nome da classe pode ser oque voce quiser. Usaremos "OpenGL" para simplificar.

 
	// Apanhamos a instancia para nossa janela
	hInstance		= GetModuleHandle(NULL);
	// Redesenha ao mover
	wc.style		= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	// WndProc manipula as Mesagems	
	wc.lpfnWndProc		= (WNDPROC) WndProc;
	// Sem dado adicional de janela
	wc.cbClsExtra		= 0;
	// Sem dado adicional de janela
	wc.cbWndExtra		= 0;
	// Seta a instancia
	wc.hInstance		= hInstance;
	// Carrega o Icone padrão
	wc.hIcon		= LoadIcon(NULL, IDI_WINLOGO);
	// Carrega o ponteiro padrao do mouse.
	wc.hCursor		= LoadCursor(NULL, IDC_ARROW);
	// Fundo desnecessario para o OpenGl
	wc.hbrBackground	= NULL;
	// Não queremos um menu
	wc.lpszMenuName		= NULL;
	// Seta o nome da Classe
	wc.lpszClassName	= "OpenGL";
	

Agora registraremos a classe. Se alguma coisa der errado, aparecerá uma mensagem de erro. Clicando OK na mensagem abortará o programa.

 
	if (!RegisterClass(&wc))	//Tentativa de registrar a classe da janela
	{
		MessageBox(NULL,"Falha ao registrar a classe da janela.","ERRO",
MB_OK|MB_ICONEXCLAMATION); return FALSE; // Sai e retorna FALSE }

Agora checaremos se o progrma deve rodar no modo tela-cheia ou em modo-janela.

 
	if (fullscreen)	// Tela-cheia?
	{
	

Esta proxima parte é um lugar onde as pessoas costumam ter algum problema... alternando para o modo tela-cheia. Há algumas coisas que voce deve ter em mente quando for alternar para a tela-cheia. Tenha certeza que a altura e largura que voce usara no modo tela-cheia é o mesmo da altura e largura que voce planeja usar na sua janela, e mais importante, sete o modo tela-cheia ANTES de criar a janela. Neste código, voce não precisa se preocupar com a altura/largura, o modo-janela e tela-cheia estao ambos setados com os mesmo valores.

 
	//Device Mode
	DEVMODE dmScreenSettings;				
		//Cetifica que a memoria esta limpa
		memset(&dmScreenSettings,0,sizeof(dmScreenSettings));	
		//tamanho da estrutura DEVMODE
		dmScreenSettings.dmSize=sizeof(dmScreenSettings);	
		//largura selecionada
		dmScreenSettings.dmPelsWidth	= width;		
		//altura selecionada
		dmScreenSettings.dmPelsHeight	= height;		
		//BPP selecionada
		dmScreenSettings.dmBitsPerPel	= bits;			
		//BPP selecionada
		dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
	

No codigo acima limparemos os campos para armazenar as configuracoes de video. Setaremos a altura, largura e bits que a tela terá. No codigo abaixo tentaremos efetivar a tela-cheia requisitada. Guardamos todas informações sobre altura, largura e bits no dmScreenSettings. A linha abaixo de ChangeDisplaySettings tenta alternar para o modo que seja igual ao que alimentamos no dmScreenSettings. Usaremos o parametro CDS_FULLSCREEN quando alternarmos os modos, porque este deve remover a barra de inicio em baixo da tela, e tambem não move ou redimensiona as janelas do desktop quando se alterna entre modos tela-cheia e modo-janela.

 
	//tenta setar o modo selecionado e guarda resultado
		//Nota: CDS_FULLSCREEN se livra da Barra iniciar
		if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
		{
	

Se o modo não pôde ser setado, o codigo abaixo será rodado. Se um modo tela-cheia compativel não existie, uma mensagem será apresentada com duas opções: Rodar em modo-janela ou sair do programa.

 
	//Se o modo falhar, oferecer duas opcoes. Sair ou rodar em uma janela.
			if (MessageBox(NULL,"O modo tela-cheia desejado não é
			                    suportado pela\nsua placa de video. Usar modo janela?",
			                    "OpenGl",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
			{
	

Se o usuario decidir usar o modo-janela, a variavel fullscreen se torna FALSE, e o programa continua rodando.

 
	fullscreen=FALSE;//Seleciona modo-janela (Fullscreen=FALSE)
			}
			else
			{
	

Se o usuario decidir sair, uma mensagem aparecerá dizendo que o programa irá terminar. Será retornado FALSE, dizendo ao programa que a janela nao foi criada com sucesso. O programa então irá terminar.

 
	//Mensagem dizendo ao usuario que o programa terminará
			MessageBox(NULL,"O programa será fechado.","ERROR",MB_OK|MB_ICONSTOP);
			return FALSE;//Sai e retorna FALSE
			}
		}
	}
	

Porque o código de tela-cheia acima pode ter falhado (e o usuario ter escolhido rodar o programa em modo-janela) checamos novamente se fullscreen é TRUE/FALSE antes de setar o tipo de tela/janela.

 
	if (fullscreen)//Ainda estamos no modo tela-cheia?
	{
	

Se ainda estamos em modo tela-cheia, setaremos o extilo extendido para WS_EX_APPWINDOW, que forçará uma janela de nivel alto. Setaremos o estilo janela como WS_POPUP. Este tipo de janela não tem bordas ao redor dela, criando a tela-cheia perfeita. Finalmente, desabilitaremos o ponteiro do mouse. Se seu programa não for interativo, geralmente é bom desligar o ponteiro do mouse quando estiver em modo-cheio. Porem cabe a voce decidir.

 
	dwExStyle=WS_EX_APPWINDOW;//Estilo extendido
		dwStyle=WS_POPUP;//Estilo de janela
		ShowCursor(FALSE);//Esconde o cursor do mouse.
	}
	else
	{
	

Se estivermos usando uma janela ao invés de tela-cheia, adicionaremos WS_EX_WINDOWEDGE ao estilo extendido. Isto dará a janela uma aparencia mais 3D. Para o estilo usaremos o WS_OVERLAPPEDWINDOW em vez do WS_POPUP. WS_OVERLAPPEDWINDOW cria uma janela com uma barra de titulo, borda redimensionável, menu de janela e botoes de minimizar/maximizar.

 
	dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;//Estilo extendido
		dwStyle=WS_OVERLAPPEDWINDOW;//estilo de janela
	}
	

A linha abaixo ajusta nossa janela em qual estilo estivermos criando. Este ajuste irá fazer nossa janela exatamente na resolução que pedimos. Normalmente as bordas da janela sobrepõem partes de nossa janela. Usando o comando AdjustWindowRectEx, nenhuma parte de nossa cena OpenGl será ocupada pela borda, ao invés disso, a janela se tornará mais larga para aceitar os pixels necessarios para desenhar as bordas da janela. No modo tela-cheia, este comando não tem efeito.

 
	//Ajusta a janela para o jamanho requisitado
	AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);
	

Na proxima secao de codigo, vamos criar nossa janela e checar se for criada corretamente. Vamos passar a CreateWindowEx() todos os parametros que ele necessita. O estilo extendido que decidimos usar. O nome da classe (que tem que ser o mesmo ao nome usado quando registramos a Classe da Janela). O titulo da janela. O estilo da janela. A posicão de cima/esquerda da janela (0,0 é um valor seguro). A altura/largura da janela. Não temos uma janela parente e não queremos um menu então setamos estes dois parametros como NULL. Passamos a instancia de nossa janela e finalmente NULL como ultimo parametro. Note que usamos os estilos WS_CLIPSIBLINGS e WS_CLIPCHILDREN junto com o estilo de janela que decidimos usar. Estes estilos são REQUERIDOS para o funcionamento correto do OpenGL. Estes estilos previnem que outras janelas desenhem sobre ou dentro de nossas janela OpenGL.

 
	if (!(hWnd=CreateWindowEx(	
		dwExStyle,				// Extilo extendido da janela
		"OpenGL",				// Nome da classe
		title,					// Titulo da janela
		WS_CLIPSIBLINGS 			// Estilo requerido de janela
		WS_CLIPCHILDREN | 			// Estilo requerido de janela
		dwStyle,				// Estilo selecionado de janela
		0, 0,					// Posicao da janela
		WindowRect.right-WindowRect.left,	// Calcula largura ajustada da janela
		WindowRect.bottom-WindowRect.top,	// Calcula altura ajustada da janela
		NULL,					// Sem janela parente
		NULL,					// Sem menu
		hInstance,				// Instancia
		NULL)))					// Nao passa nada para o WM_CREATE
	

Agora vamos checar se nossa janela foi criada corretamente. Se a janela foi criada, hWnd irá conter um 'handle' (ou manipulador). Se a janela não foi criada o código abaixo irá gerar uma mensage de erro e o programa irá ser cancelado.

 
	{
		KillGLWindow();// Reseta o display
		MessageBox(NULL,"Erro na criacao da janela.","ERRO",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;// Retorna FALSE
	}
	

O proximo boloco descreve um 'Pixel Format' (maneira na qual vai ser mostrada os pixels na tela). Vamos escolher um formato que suporta o OpenGL e double buffering, incluindo RGBA (canais vermelgo/verde/azul/alpha). Vamos tentar achar um 'pixel format' que suporta os bits que passamos como parametro (16bit,24bit,32bit). E então setamos um 16bit Z-Buffer. Os parametros a mais ou não são usados ou não são importantes.

 
	static	PIXELFORMATDESCRIPTOR pfd=	//pfd dirá ao Windows como queremos as coisas.
	{
		sizeof(PIXELFORMATDESCRIPTOR),	// Tamanho do descritor Pixel Format
		1,				// Versão
		PFD_DRAW_TO_WINDOW |		// Deve suportar Window
		PFD_SUPPORT_OPENGL |		// Deve suportar OpenGL
		PFD_DOUBLEBUFFER,		// Deve suportar  Double Buffering
		PFD_TYPE_RGBA,			// Deve suportar formato RGBA
		bits,				// Seleciona profundidade de cor (BPP)
		0, 0, 0, 0, 0, 0,		// Bits de cores ignorados
		0,				// Sem Alpha Buffer
		0,				// Sem Shift Bit
		0,				// Sem buffer de acumulação
		0, 0, 0, 0,			// Buffer de acumulação ignorado
		16,				// 16Bit Z-Buffer (Depth Buffer)
		0,				// Sem Stencil Buffer
		0,				// Sem buffer Auxiliary
		PFD_MAIN_PLANE,			// Camada de desenho principalLayer
		0,				// Reservado
		0, 0, 0				// Mascara de camadas ignorado
	};
	

Se não houver erros enquanto criar a janela, tentaremos obter um 'OpenGL Device Context' (Contexto de dispositivo OpenGL). Se não conseguirmos o DC, uma mensagem de erro irá aparecer e o programa vai terminar (retornar FALSE).

 
	if (!(hDC=GetDC(hWnd)))		// Conseguimos o Device Context?
	{
	KillGLWindow();			// Reseta o Display
	MessageBox(NULL,"Erro ao criar o GL Device Context.","ERRO",MB_OK|MB_ICONEXCLAMATION);
	return FALSE;			// Return FALSE
	}
	

Se conseguimos o Device Context para nossa janela OpenGL, tentaremos achar um formato de pixel que suporta os bits que descrevemos logo acima. Se o Windows não conseguir um formato de pixel compativel, aparecerá uma mensagem de erro e o programa vai terminar (retornar FALSE)

 
	if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd)))	// Achou um Pixel Format?
	{
		KillGLWindow();				// Reseta o Display
		MessageBox(NULL,"Erro ao achar PixelFormat.","ERRO",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;				// Return FALSE
	}
	

Se o Windows achou um pixel format, tentaremos setar este pixel format. Se este não poder ser setado, uma mensagem de erro aparecerá e o programa vai terminar (retornar FALSE).

 
	if(!SetPixelFormat(hDC,PixelFormat,&pfd))	// Fomos capazes de setar o Pixel Format?
	{
		KillGLWindow();					// Reseta o Display
		MessageBox(NULL,"Impossivel setar o PixelFormat.","ERRO",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;					// Retorna FALSE
	}
	

Agora vamos obter um 'Rendering Context' (Contexto de renderização). Se este não poder ser obtido, uma mensagem de erro aparecerá e o programa vai terminar (retornar FALSE).

 
	if (!(hRC=wglCreateContext(hDC)))	// Fomos capazes de obter o Rendering Context?
	{
		KillGLWindow();				// Reseta o Display
		MessageBox(NULL,"Erro ao obter o Rendering Context.","ERRO",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;				// Retorna FALSE
	}
	

Se não houve nenhum erro até agora, e conseguimos criar tanto um 'Device COntext' e um 'Rendering Context' tudo que temos que fazer agora é fazer o 'Renderinf Context' ativo. Se este não poder ser setado como ativo, uma mensagem de erro aparecerá e o programa vai terminar (retornar FALSE).

 
	if(!wglMakeCurrent(hDC,hRC))	// Tenta ativar o Rendering Context
	{
		KillGLWindow();			// Reset The Display
		MessageBox(NULL,"Erro ao ativar o GL Rendering Context.",
						"ERRO",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;			// Return FALSE
	}
	

Se foi tudo bem, e nossa janela OpenGL foi criada, vamos mostrar a janela, setar ela no plano de frente (dando-a mais prioridade) e então setar o foco nesta janela. Então vamos chamar ReSizeGLScene passando o largura e altura para setar a perspectiva da cena OpenGL.

 
	ShowWindow(hWnd,SW_SHOW);	// Mostra a janela
	SetForegroundWindow(hWnd);	// Aumenta um pouco a prioridade
	SetFocus(hWnd);			// Seta o foco do teclado na janela
	ReSizeGLScene(width, height);	// Seta a perspectiva
	

Finalmente pulamos para o InitGL() onde setamos as luzes, texturas e tudo mais que prcisa ser setado. Agora voce pode incluir sua propria checagem de erro no InitGL(), e retornar TRUE (tudo ok) ou FALSE (quando houver algum erro) . Por exemplo, se voce tiver carregando texturas no InitGL() e ocorrer um erro, voce pode desejar cancelar o programa.

 
	if (!InitGL())// Inicializa nossa janela OpenGL
	{
		KillGLWindow();		// Reseta o Display
		MessageBox(NULL,"Falha na Inicialização","ERRO",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;		// Return FALSE
	}
	

Se chegamos até aqui, é correto assumir que a criação da janela teve sucesso. Podemos retornar TRUE para o WinMain(), dizendo que tudo ocorreu sem problemas

 
	return TRUE;	// Successo!
}
	

Aqui é onde são manipuladas as mensagems do Windows. Quando registramios a Classe Janela, dizemos para passar aqui para processar as mensagems.

 
	LRESULT CALLBACK WndProc(HWND	hWnd,	// Handle da Window
			UINT	uMsg,	// Mensagem da Window
			WPARAM	wParam,	// Informação adicional da mensagem
			LPARAM	lParam)	// Informação adicional da mensagem
{
	

O codigo abaixo seta uMsg como o valor que todos os CASES vao ser comparados. uMsg terá o numero da mensagem que queremos manipular.

 
	switch (uMsg)//Checa por mensagens do Windows
	{
	

se uMsg for WM_ACTIVATE nós checamos se nossa janela ainda está ativa. Se foi minimizada, a variavel 'active' será FALSE. Se a janela estiver ativa, a variavel 'active' será TRUE.

 
	case WM_ACTIVATE:		//Espera por mensagem de ativação
		{
			if (!HIWORD(wParam))	//Checa estado de minimização
			{
			active=TRUE;		// Programa esta ativo
			}
			else
			{
			active=FALSE;		//Programa não esta mais ativo
			}
			return 0;		// Retorna ao loop de mensagens
		}
	

Se a mensagem for um WM_SYSCOMMAND (comando do sistema) vamos comparar wParam no switch. Se wParam for SC_SCREENSAVE ou SC_MONITORPOWER, ou a proteção de tela está tentando iniciar ou monitor esta tentando hibernar. Retornando 0, previnimos as duas coisas de acontecer.

 
	case WM_SYSCOMMAND:		// Intercepta comandos do sistema
		{
		switch (wParam)			//Checa chamadas do sistema
			{
			case SC_SCREENSAVE:	// Screensaver tentando iniciar?
			case SC_MONITORPOWER:	// Monitor tentando hibernar?
			return 0;		// Previne acontecer
			}
			break;			// Sai
		}
	

Se o uMsg for WM_CLOSE, a janela foi fechada. Mandaremos uma mensagem de saida que o loop principal irá interceptar. A variavel 'done' será setada comoTRUE, o loop WinMain() vai parar e o programa será fechado.

 
	case WM_CLOSE:			// Recebemos uma mensagem de fechar?
		{
			PostQuitMessage(0);	//Manda uma mensagem de sair.
			return 0;		// Sai
		}
	

Se uma tecla for pressionada podemos ver qual é a tecla lendo wParam. Marcamos esta tecla no vetor 'keys[]' como TRUE. Deste modo podemos ler o vetor adiante e verificar qual tecla esta sendo pressionada. Isto possibilita a leitura de varias teclas sendo pressionadas simultaneamente.

 
	case WM_KEYDOWN:		//Uma tecla esta sendo pressionada?
		{
			keys[wParam] = TRUE;	//Se estiver, marque-a como TRUE
			return 0;		//Sai.
		}
	

Se uma tecla foi solta podemos ver qual é a tecla lendo o wParam. Marcamos esta tecla no vetor 'keys[]' como FALSE. Deste modo, podemos ler o vetor e saber se as teclas estao sendo pressionadas ou nao. Cada tecla do teclado pode ser representado por um numero de 0 a 255. Quando pressionamos a tecla que corresponde ao numero 40, por exemplo, 'keys[40]' irá se tornar TRUE. Quando soltamos esta mesma tecla, o 'keys[40]' irá voltar a ser FALSE. É assim que usamos o vetors 'keys' para monitorar o teclado.

 
	case WM_KEYUP:			// Uma tecla foi solta?
		{
			keys[wParam] = FALSE;	// Marque-a como FALSE
			return 0;		// Sai.
		}
	

Toda vez que redimensionarmos a janela, o Windows irá disparar uma mensagem e uMsg irá se tornar WM_SIZE. Temos que ler os valoes LOWORD e HIWORD de lParam para obtermos os novos valores de altura e largura da janela. Temos que passar a nova largura e altura para a função ReSizeGLScene(). Então nossa cena OpenGL será redimensionadas para a nova largura/altura.

 
	case WM_SIZE:					//Janela foi redimensionada?
		{
		ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));	//LoWord=Largura, HiWord=Altura
		return 0;					// Sai
		}
	}
	

Qualquer outra mensagem qua não nos interessar será passada para o DefWindowProc para que o Windows posa manipula-las.

 
	// Passa todas as outras mensagens  para DefWindowProc
	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
	

Esta é o ponto de entrada de nossa aplicação Windows. Aqui chamamos a a função de criação de janela, gerenciamento de mensagens e manipulamos a interação humana.

 
	int WINAPI WinMain(HINSTANCE	hInstance,	// Instancia
		HINSTANCE	hPrevInstance,	// Instancia anterior 
		LPSTR		lpCmdLine,	// Parametros de linha de comando
		int		nCmdShow)	// Estado da janela
{
	

Setaremos duas variaveis. 'msg' será usado para checar se existe alguma mensagem pendente que precisamos analisar. A variavel 'done' começa sendo FALSE. Isto significa que nosso programa não terminou de rodar. Enquanto 'done' for FALSE, o programa continuará rodando. Quando 'done' for TRUE, o programa terminará.

 
	MSG	msg;		// Estrutura de mensagens do Windows
	BOOL	done=FALSE;	// Variavel boorleana para sair do loop
	

Esta parte do código é opcional. Ela apresenta uma mensagem que pergunta ao usuario se ele quer iniciar o programa no modo tela-cheia. Se o usuario clicar no botão 'Não', a variavel 'fullscreen' muda de TRUE para FALSE e o programa roda no modo janela em vez do modo tela-cheia (padrão).

 
	// Pergunta ao usuario qual modo de tela ele prefere.
	if (MessageBox(NULL,"Gostaria de rodar no modo tela-cheia?", 
			"Iniciar em tela-cheia?",MB_YESNO|MB_ICONQUESTION)==IDNO)
	{
	fullscreen=FALSE;	// Modo janela.
	}
	

Agora vamos criar nossa janela OpenGL. Passamos o titulo, a largura, a altura, a profundidade de cor (BPP) e TRUE (tela-cheia) ou FALSE (modo janela) para a função CreateGLWindow. Se a janela não for criada por alguma razão, será retornado FALSE e nosso programa irá terminar.

 
	//Cria nossa janela OpenGL
	if (!CreateGLWindow("Programa OpenGL",640,480,16,fullscreen))
	{
	return 0;	// sai se a janela nao foi criada
	}
	

Aqui iniciamos o loop. Enquanto 'done' for FALSE, o loop vai se repetir.

 
	while(!done)	//Roda até que done==TRUE
	{
	

A primeira coisa a fazer é checar se alguma mensagem de janela está na fila. Usando PeekMessage() podemos checar as mensagens sem travar nosso programa. Varios programas usam GetMessage(), mas esta função bloqueia o fluxo do programa até receber alguma mensagem.

 
		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))	//Há mensagem pendente?
		{
	

Agora vamos checar se a mensagem de saida foi envocada. Se a mensagem atual for WM_QUIT (causada pelo PostQuitMessage(0)), setamos a variavel 'done' para TRUE, causando o final do programa.

 
			if (msg.message==WM_QUIT)//Mensagem de saida?
			{
				done=TRUE;// Se for, done=TRUE
			}
			else//Se nao, analiza a mensagem
			{
	

Se a mensagem nao for de saida, traduzimos a mensagem depois a despachamos para que WndProc() ou o Windows possa manipula-la.

 
				TranslateMessage(&msg);	//Traduz a mensagem
				DispatchMessage(&msg);  //Despacha a mensagem
			}
		}
							//Nao ha nenhuma mensagem.
		{
	

Se não ha mensagens, vamos desenhar a cena OpenGL. A primeira linha abaixo checa se a janela esta ativa. Se a tecla ESC for ressionada a variavel 'done' é setada como TRUE, causando a saida do programa.

 
			if (active)			// Programa ativo?
			{
				if (keys[VK_ESCAPE])	// ESC foi pressionado?
				{
					done=TRUE;	// seta saida
				}
				else 			// Nao é hora de sair, atualiza tela
				{
	

Se o programa estiver ativo e ESC não for pressioanda, então renderizamos a cena e fazemos a troca de buffer (buffer swap) (Usando a tecnica de double buffering obtemos uma animação mais suave). Usando a troca de buffer, desenhamos tudo na tela oculta (qua não podemos ver) e quando trocamos os buffer, a tela que vemos se torna oculta e a tela que acabamos de desenhar se torna visivel. Desta maneira nós não vemos a tela sendo desenhada, ela apenas aparece instantaneamente.

 
					DrawGLScene();		// Desenha a cena
					SwapBuffers(hDC);	// Troca os Buffers (Double Buffering)
				}
			}
	

A proxima parte de cóidigo nos permite alternar entre o modo janela e o modo tela-cheia toda vez que o usuario apertar a tecla F1.

 
			if (keys[VK_F1])		// F1 sendo pressionada?
			{
				keys[VK_F1]=FALSE;	// Se estiver marca a tecla como FALSE
				KillGLWindow();		// Mata a janela atual
				fullscreen=!fullscreen;	// Alterna modo janela/tela-cheia 
				//Recria a janela OpenGL
				if (!CreateGLWindow("Programa OpenGL",640,480,16,fullscreen))
				{
				return 0;		// Sai se não criar com sucesso.
				}
			}
		}
	}
	

Se a variavel 'done' não for mais FALSE, o programa acaba. Matamos a janela OpenGL de maneira correta para que tudo seja liberado e então saimos do programa.

 
	KillGLWindow();		// Mata a janela
	return (msg.wParam);	// Sai do programa.
}
	

Neste tutorial, tentei explicar com mais detelhes possiveis todos os passos para criar um programa OpenGL básico. Qua vai sair toda vez que for pressionado ESC e monitora se o programa esta ativo ou não.
- Deixe seu recado/dúvida ® 2011 - www.buosi.org   
Se estão dando, pegue. Se vierem buscar, corra. -- Provérbio russo