Curso completo de DirectX 9 com C\C++
Gameprog - Escola de programação de jogos digitais
Contato: gameprog.br@gmail.com
Fase 10-2

index << >>


10.2 Renderização simples

1.1 Visão geral
Neste tópico vamos mostrar como estruturar a aplicação para a utilização de HLSL e vamos explicar a estrutura básica de um programa simples escrito com esta linguagem. Esta aplicação foi baseada no projeto prj_Buffers do tópico 01.09 e vai mostrar um quadrado girando no eixo z, com uma pequena animação de cor e escala, renderizado com HLSL. Inicialização do motor gráfico O uso de HLSL implica que vai ser realizado diretamente no hardware o processamento dos vértices. Então para utilizar HLSL na aplicação é necessário criar o dispositivo renderizador com essa opção: DWORD create_flags = D3DCREATE_HARDWARE_VERTEXPROCESSING; Uso obrigatório do vertexbuffer A aplicação faz uso do buffer de vértices g_vbVertices inicializado pelas funções inicializar_Buffers() e montar_Geometria(). A presença do buffer de vértices (vertexbuffer) é obrigatória na utilização da linguagem HLSL para renderizar a cena. Declaração de vértice Com o uso de HLSL é necessário criar um objeto VertexDeclaration que vai levar para a placa de vídeo a informação de cada elemento da configuração dos vértices que vão seguir para a linha programável da placa de vídeo. A criação dessa declaração de vértice, que é um objeto da interface IDirect3DVertexDeclaration9, é criada de maneira rápida e fácil na função alternativa DeclararVertices_Auto() e também mostramos como criar do zero e passo a passo a mesma declaração de vértice na função DeclararVertices_Manual(). A declaração de vértice para a placa de vídeo é um processo equivalente ao processo de estabelecer e declarar o formato de vértice para o dispositivo renderizador do directx. Câmera A instalação da câmera no dispositivo renderizador não é mais feita com o método SetTransform(). As matrizes parciais de câmera são configuradas normalmente em inicializar_Camera() e antes do processo de renderização a matriz final é produzida e introjetada na linha programável pela função AtualizarCamera(). Os efeitos produzidos com HLSL A aplicação e gerenciamento dos efeitos do HLSL na aplicação são feitos pelo objeto da interface ID3DXEffect que carrega, compila e transfere o programa HLSL para a placa de vídeo. Esse objeto é inicializado na função inicializarEfeito() que carrega o arquivo simples.fx que contém o código hlsl. Os efeitos possuem módulos selecionáveis chamados técnicas (Technique) que aplicam processos particulares nos vértices e nos pixels. 1.2 Estrutura principal da aplicação
Arquivo: motor.cpp
Aspectos globais Declaração de ponteiros globais para: o objeto vertexbuffer o objeto efeito produzido pela interface ID3DXEffect ( g_efeito ) o objeto declaração de vértice ( IDirect3DVertexDeclaration9 ) inicializarEfeito() inicializa os argumentos da função que produz o objeto efeito produz o objeto efeito com a função D3DXCreateEffectFromFile() que carrega o arquivo com o código hlsl, compila-o e produz o objeto efeito da interface ID3DXEffect. seleciona a técnica a ser utilizada na renderização initGfx() Configura a ocorrência do processamento de vértices no hardware. chama inicializar_Buffers() \ montar_Geometria() para configurar o buffer de vértices. chama inicializar_Camera() \ atualizarCamera() para configuração inicial da câmera. chama DeclararVertices_Manual() ou DeclararVertices_Auto() para produzir a declaração de vértice. chama inicializarEfeito() para carregar o programa hlsl do disco e inicializar o objeto efeito ( g_efeito ). atualizarCamera() Atualiza as variáveis para produzir variação de movimento e cor Produz a matriz final de câmera Introjeta as variáveis atualizadas na linha programável do dispositivo gráfico. renderizar_Geometria() Declara ao dispositivo o vertexbuffer que será utilizado Declara ao dispositivo o formato de vértice que será utilizado. Configura no dispositivo a declaração de vértice produzida. chama atualizarCamera() para introjetar informações no shader Renderiza a cena utilizando o objeto efeito ( g_efeito ) com o apoio de g_device->DrawIndexedPrimitive().
2.1.1 Aspectos globais - Arquivo: motor.h
// ----------------------------------------------------------------------------- // Projeto: prj_HLSL01 - Arquivo: motor.h // Esta aplicação mostra como renderizar um // quadrado colorido com HLSL. // By www.gameprog.com.br // ----------------------------------------------------------------------------- #pragma comment(lib, "d3d9.lib") #pragma comment(lib, "d3dx9.lib") #ifndef motor_h #define motor_h // Definição do formato de vértice utilizado por esta aplicação #define CustomVertex_PositionOnly_Format(D3DFVF_XYZ) // Estrutura de vértice customizado com posição apenas. struct CustomVertex_PositionOnly { float x, y, z; // Construtor default CustomVertex_PositionOnly() {} CustomVertex_PositionOnly( float _x, float _y, float _z) { x = _x; y = _y; z = _z; } }; // fim da estrutura CustomVertex_PositionOnly // Esta função inicializa o Direct3D HRESULT initGfx (HWND hJanela); // Essa função libera os objetos utilizados void Limpar(); // Essa função coordena a renderização a cena void Renderizar(); // Essa função inicializa o buffer de vértices void inicializar_Buffers(void); // Essa função monta as formas geométricas void montar_Geometria (void); // Renderiza as formas geométricas void renderizar_Geometria (void); // Faz a configuração inicial da câmera void inicializar_Camera(void); // Elabora a declaração de vértices void DeclararVertices_Auto(void); void DeclararVertices_Manual(void); // Inicializa o objeto efeito void inicializarEfeito(void); // Atualiza a câmera void atualizarCamera(void); // Declaração da função que atende as mensagens da janela LRESULT CALLBACK processaJanela ( HWND hJanela, UINT mensagem, WPARAM wParam, LPARAM lParam); #endif
// Definição do formato de vértice utilizado por esta aplicação #define CustomVertex_PositionOnly_Format(D3DFVF_XYZ) O directx apresenta a função D3DXDeclaratorFromFVF() que transforma a definição de formato de vértice em uma array de elementos informativos que vai dar origem à declaração de vértice. Neste processo surge o conceito de semântica que consiste em esclarecer a finalidade de uso do elemento, por exemplo, a semântica de D3DFVF_XYZ é posicionamento. HRESULT initGfx (HWND hJanela); Nas aplicações que fazem uso de renderização com HLSL o flag de criação do dispositivo renderizador deve trazer a opção D3DCREATE_HARDWARE_VERTEXPROCESSING que indica que o processamento dos vértices vai ocorrer no hardware. void inicializar_Camera(void); O destaque dessa função é a ausência do método SetTransform() na instalação final das matrizes da câmera no dispositivo renderizador. Essa configuração final da câmera vai ser realizada na função atualizarCamera() com funções que transferem dados para a placa de vídeo. void DeclararVertices_Auto(void); Essa função vai mostrar uma forma simples de elaborar a declaração de vértice. void DeclararVertices_Manual(void); Essa função vai detalhar o processo de elaboração da declaração de vértice. As funções DeclararVertices_Auto() e DeclararVertices_Manual() são iguais no resultado final e apenas uma deve ser usada para produzir a declaração de vértice. Teste uma e depois teste a outra. void inicializarEfeito(void); Essa função inicializa a interface de uso do objeto efeito que permite utilizar HLSL para processar os vértices e pixels de composição da imagem final. Isso envolve carregar um arquivo com código escrito em HLSL e produzir o objeto da interface ID3DXEffect. void atualizarCamera(void); Essa função vai atualizar as variáveis que produzem uma pequena animação de movimento e cor no quadrado renderizado. Isso inclui produzir as matrizes finais de câmera e introjetar estas informações na linha programável da placa de vídeo. void renderizar_Geometria (void); Esta função vai renderizar o quadrado utilizando o código escrito em HLSL. 2.1.2 Aspectos globais - Arquivo: motor.cpp
// Representa o buffer de vértices IDirect3DVertexBuffer9 *g_vbVertices = NULL; // Representa o objeto efeito ID3DXEffect *g_efeito = NULL; // Interface para a declaração de vértices IDirect3DVertexDeclaration9 *g_VertexDeclaration; // Variáveis para provocar mudanças de cor e movimento float angulo = 0.01f; float nMovimento = 0.0f; float nVelocidade = 0.01f;
IDirect3DVertexBuffer9 *g_vbVertices = NULL; O uso de buffer de vértices é obrigatório na utilização de HLSL para renderização da cena. ID3DXEffect *g_efeito = NULL; Este é o objeto efeito que faz a interface entre a aplicação e a placa de vídeo permitindo a utilização de código escrito em HLSL na renderização da cena final. IDirect3DVertexDeclaration9 *g_VertexDeclaration; Esta é a interface recipiente da declaração de vértice. float angulo = 0.01f; float nMovimento = 0.0f; float nVelocidade = 0.01f; A modificação destas variáveis produzem a pequena animação de cor e rotação do quadrado. 2.2.1 Declaração de vértices - Automatizada
void DeclararVertices_Auto(void) { // Declaração coletiva dos elementos D3DVERTEXELEMENT9 Elementos[ sizeof(D3DVERTEXELEMENT9) * 2 ]; // Preenche a array dos elementos D3DXDeclaratorFromFVF(CustomVertex_PositionOnly_Format, Elementos ); // Criação da declaração de vértices g_hr = g_device->CreateVertexDeclaration(Elementos, &g_VertexDeclaration); } // DeclararVertices(void);
D3DVERTEXELEMENT9 Elementos[ sizeof(D3DVERTEXELEMENT9) * 2 ]; A declaração de vértice é uma coleção de elementos do tipo D3DVERTEXELEMENT9. Cada elemento dessa coleção tem uma correspondência com cada componente do formato de vértice. Por exemplo, o conjunto de 3 floats ( x, y, z ) do formato de vértice constitui-se em um elemento da declaração e sua semântica é posicionamento. Aqui neste caso essa array terá dois elementos: um para posição e outro para o elemento finalizador que indica o fim da array. D3DXDeclaratorFromFVF(CustomVertex_PositionOnly_Format, Elementos ); Essa função produz uma array de elementos a partir da definição de um formato de vértice. g_hr = g_device->CreateVertexDeclaration(Elementos, &g_VertexDeclaration); O método CreateVertexDeclaration() do dispositivo renderizador produz a declaração de vértice a partir de uma array de elementos do tipo da estrutura D3DVERTEXELEMENT9. 2.2.2 Declaração de vértices - Manual
void DeclararVertices_Manual(void) { // Argumentos para preenchimento da array D3DVERTEXELEMENT9 WORD nStream = 0; WORD nDeslocamento = 0; BYTE nTipo = D3DDECLTYPE_FLOAT3; BYTE nMetodo = D3DDECLMETHOD_DEFAULT; BYTE nUso = D3DDECLUSAGE_POSITION; BYTE nIndiceUso = 0; // Declaração do primeiro elemento D3DVERTEXELEMENT9 position = {nStream, nDeslocamento, nTipo, nMetodo, nUso, nIndiceUso }; // Declaração do elemento final D3DVERTEXELEMENT9 finalizador = D3DDECL_END(); // Declaração coletiva dos elementos D3DVERTEXELEMENT9 Elementos[] = { position, finalizador }; // Criação da declaração de vértices g_hr = g_device->CreateVertexDeclaration(Elementos, &g_VertexDeclaration); } // DeclararVertices(void);
Descrevemos abaixo os elementos da estrutura D3DVERTEXELEMENT9 que são utilizados para descrever cada componente de um formato de vértice e formar assim um elemento descritivo da declaração de vértice. WORD nStream = 0; Aqui é a indicação do stream que contém o elemento. Em uma aplicação com vários streams é normal cada stream conter apenas um componente do formato de vértice ( só coordenada de textura, só posição de vértice, etc...). Uma aplicação com apenas um stream vai colocar todos os elementos no stream zero (0). WORD nDeslocamento = 0; Esse elemento representa a distância em bytes do elemento dentro da constituição dos elementos. Por exemplo, o elemento posição (x, y, z) ocupa 12 bytes porque cada float que representa um eixo ocupa 4 bytes. Sendo, assim o deslocamento do próximo elemento seria indicado pelo valor 12 em consideração ao espaço já ocupado pelo elemento posição. BYTE nTipo = D3DDECLTYPE_FLOAT3; Esse elemento indica o tipo de constituição do elemento. D3DDECLTYPE_FLOAT3 quer dizer um conjunto de 3 floats. As coordenadas de textura seriam representadas pelo tipo D3DDECLTYPE_FLOAT2 da enumeração D3DDECLTYPE contida no arquivo d3d9types.h. BYTE nMetodo = D3DDECLMETHOD_DEFAULT; Este item indica o tipo de processamento que a placa deve dar a este elemento da declaração. Este é o valor padrão mais usado. BYTE nUso = D3DDECLUSAGE_POSITION; Este item é a semântica que indica a finalidade de uso do elemento, no caso aqui é uma indicação de posição. Em outro exemplo, D3DDECLUSAGE_TEXCOORD é a semântica para coordenadas de textura. BYTE nIndiceUso = 0; Esse item geralmente é configurado como zero (0) e sua modificação para outro valor altera ou agrega detalhes na semântica. // Declaração do primeiro elemento D3DVERTEXELEMENT9 position = { nStream, nDeslocamento, nTipo, nMetodo, nUso, nIndiceUso }; Esse linha mostra a produção de um único elemento da declaração com os argumentos que já foram discutidos. D3DVERTEXELEMENT9 finalizador = D3DDECL_END(); Este elemento é obrigatório e indica o final de uma coleção de elementos na array de elementos ( Elementos ). // Declaração coletiva dos elementos D3DVERTEXELEMENT9 Elementos[] = { position, finalizador }; Aqui a array de elementos é criada com todos os seus elementos individuais. g_hr = g_device->CreateVertexDeclaration(Elementos, &g_VertexDeclaration); O método CreateVertexDeclaration() do dispositivo renderizador produz a declaração de vértice a partir de uma array de elementos do tipo da estrutura D3DVERTEXELEMENT9. 2.3 Inicialização do objeto efeito
void inicializarEfeito() { // Nome do arquivo de efeito char fx_arquivo[] = "\\gameprog\\gdkmedia\\shader\\simples.fx"; // Flag de configuração DWORD create_flags = D3DXSHADER_DEBUG; // Buffer para receber informação em caso de erro ID3DXBuffer *txtErro = NULL; // Gera o efeito a partir do arquivo carregado g_hr = D3DXCreateEffectFromFile( g_device, fx_arquivo, 0, 0, create_flags, 0, &g_efeito, &txtErro); if (FAILED(g_hr)) { MessageBox(NULL, "falha: D3DXCreateEffectFromFile()", "inicializarEfeito", MB_OK); MessageBox(NULL, (char*) txtErro->GetBufferPointer(), "inicializarEfeito", MB_OK); txtErro->Release (); return; } // endif // Seleciona a técnica de renderização D3DXHANDLE htech_MovimentoCor; htech_MovimentoCor = g_efeito->GetTechniqueByName ("MovimentoCor"); g_efeito->SetTechnique(htech_MovimentoCor); // Libera o buffer de erros if (txtErro != NULL) txtErro->Release(); } // inicializarEfeito().fim
char fx_arquivo[] = "\\gameprog\\gdkmedia\\shader\\simples.fx"; Esta variável representa o nome do arquivo de efeito com código hlsl que será carregado. Lembramos que no arquivo de efeito a parte do código que processa os vértices é chamado de vertexshader e a parte do código que processa os pixels é chamado de pixelshader. DWORD create_flags = D3DXSHADER_DEBUG; Este flag indica que o efeito será compilado com informações de debug. Essa configuração é interessante pois produz informações que ajudam nas correções em possíveis erros no código hlsl. ID3DXBuffer *txtErro = NULL; Esse buffer recebe uma string com informação no caso de erros no código do efeito. No caso de erro, para acessar a string é necessário um cast para o tipo char* no ponteiro retornado pelo método GetBufferPointer() dessa interface. No código de verificação de erro isso é feito dessa forma em destaque: MessageBox(NULL, (char*) txtErro->GetBufferPointer(), // (...) g_hr = D3DXCreateEffectFromFile( g_device, fx_arquivo, 0, 0, create_flags, 0, &g_efeito, &txtErro); A função D3DXCreateEffectFromFile() carrega o arquivo de efeito do disco, compila-o e gera o objeto efeito da interface ID3DXEffect que fará a renderização da cena. D3DXHANDLE htech_MovimentoCor; Esta variável representa um handle para uma technique ( técnica ). A technique é uma entidade dentro do código hlsl que aplica um efeito particular na cena envolvendo uma função de vertexshader e pixelshader. htech_MovimentoCor = g_efeito->GetTechniqueByName ("MovimentoCor"); Aqui a técnica MovimentoCor é carregada para o respectivo handle. g_efeito->SetTechnique(htech_MovimentoCor); Aqui a técnica MovimentoCor é configurada no dispositivo renderizador. Depois dessa configuração toda renderização será feita pelo código hlsl apontado por essa técnica. É comum haver no código hlsl várias técnicas para produzir efeitos visuais diversos na cena. 2.4 Atualização da câmera
void atualizarCamera(void) { // Atualiza movimento\cor através da variação de nMovimento nMovimento += nVelocidade; if (nMovimento >= 1.0f) nVelocidade *= -1; if (nMovimento <= 0.0f) nVelocidade *= -1; // Atualiza a câmera angulo += 0.05f; D3DXMatrixRotationZ (&g_mtxMundo, angulo); D3DXMATRIX camera = g_mtxMundo * g_mtxVisao * g_mtxProj; // Handle para a variável do shader 'nMovimento' D3DXHANDLE hMov = NULL; // Handle para a variável do shader 'camera' D3DXHANDLE hCam = NULL; // Obtém acesso às variáveis do shader hMov = g_efeito->GetParameterByName (0, "nMovimento"); hCam = g_efeito->GetParameterByName (0, "Camera"); // Atualiza variáveis no shader g_efeito->SetValue (hMov, &nMovimento, sizeof(nMovimento) ); g_efeito->SetValue (hCam, &camera, sizeof(camera) ); } // atualizarCamera(void);
nMovimento += nVelocidade; A alteração da variável nMovimento na taxa dada por nVelocidade é responsável pela pequena animação de cor e escala do quadrado renderizado. Essa variável ( nMovimento ) é utilizada dessa forma no código hlsl: Pos.z += nMovimento * 2; cor.r = nMovimento; cor.b = 1 - nMovimento; Em resumo, a alteração periódica de nMovimento provoca um vai e vem no eixo z e na variação de cor do quadrado. if (nMovimento >= 1.0f) nVelocidade *= -1; if (nMovimento <= 0.0f) nVelocidade *= -1; Esse código mantém a variável nMovimento na faixa de valor do intervalo [0.0f, 1.0f] com inversão da direção do incremento no encontro com esses limites extremos. angulo += 0.05f; D3DXMatrixRotationZ (&g_mtxMundo, angulo); Aqui ocorre a preparação da rotação do quadrado. D3DXMATRIX camera = g_mtxMundo * g_mtxVisao * g_mtxProj; Aqui é a preparação da matriz final da câmera. D3DXHANDLE hMov = NULL; Este é um handle para a troca de uma informação entre a aplicação e a placa de vídeo. Neste caso aqui é o handle para transferir o valor da variável nMovimento da aplicação para a variável nMovimento do shader. D3DXHANDLE hCam = NULL; Este aqui é o handle para transferir o valor da variável camera da aplicação para a variável Camera do shader. hMov = g_efeito->GetParameterByName (0, "nMovimento"); O método GetParameterByName() obtém o endereço de acesso de uma variável global do shader, neste caso aqui a variável nMovimento do shader. hCam = g_efeito->GetParameterByName (0, "Camera"); Aqui é obtido o endereço de acesso da variável Camera do shader. g_efeito->SetValue (hMov, &nMovimento, sizeof(nMovimento) ); O método SetValue() assinala o valor de uma variável global do shader. Neste caso aqui estamos configurando o valor da variável nMovimento do shader com o valor da variável nMovimento da aplicação. g_efeito->SetValue (hCam, &camera, sizeof(camera) ); Aqui estamos configurando o valor da variável Camera do shader com o valor da variável camera da aplicação. É um movimento de transferência de informação da aplicação para a placa de vídeo e de maneira complementar existem funções que fazem o caminho inverso recebendo informação processada pela placa de vídeo. 2.5 Inicialização do motor gráfico
// initGfx() - Inicializa o Direct3D HRESULT initGfx( HWND hJanela ) { // Cria o objeto Direct3D que é necessário para criar o dispositivo gráfico g_Direct3d = Direct3DCreate9( D3D_SDK_VERSION); // Verifica se o objeto Direct3D foi criado if(g_Direct3d == NULL) { MessageBox (NULL, "Falha na inialização do Direct3D", "InitGfx()", MB_OK); return E_FAIL; } // endif // Declara a variável para os parâmetros de apresentação D3DPRESENT_PARAMETERS pps; // Limpa a estrutura ZeroMemory( &pps, sizeof(pps) ); // Configura os parâmetros de apresentação // A aplicação vai ter janela pps.Windowed = TRUE; // Esse método transfere rapidamente o backbuffer para a tela imediata pps.SwapEffect = D3DSWAPEFFECT_DISCARD; // Esse formato vai procurar se encaixar no modo de video corrente pps.BackBufferFormat = D3DFMT_UNKNOWN; // Configuração do renderizador a ser criado // Adaptador default (0) int nAdaptador = D3DADAPTER_DEFAULT; // Tipo de dispositivo Hardware ou emulador de referência (software) D3DDEVTYPE dispositivo_tipo = D3DDEVTYPE_HAL; // Flags de configuração do dispositivo DWORD create_flags = D3DCREATE_HARDWARE_VERTEXPROCESSING; // Criamos aqui o dispositivo renderizador g_hr = g_Direct3d->CreateDevice( nAdaptador, dispositivo_tipo, hJanela, create_flags, &pps, &g_device ); // Verifica se houve falha no processo if( FAILED( g_hr ) ) { MessageBox (NULL, "Falha na criação: g_device", "initGfx()", MB_OK); return E_FAIL; } // endif // Inicializa o vertexbuffer e configura os vértices inicializar_Buffers(); montar_Geometria(); // Elabora a declaração dos vértices DeclararVertices_Manual(); // Inicializa um objeto efeito inicializarEfeito(); // Inicializa e atualiza a configuração de câmera inicializar_Camera(); atualizarCamera(); return S_OK; } // initGfx().fim
Destacamos abaixo as mudanças e adaptações de initGfx() para esta primeira aplicação com renderização feita pelo shader escrito em hlsl: // O processamento dos vértices será feito pelo hardware gráfico! DWORD create_flags = D3DCREATE_HARDWARE_VERTEXPROCESSING; // Elabora a declaração dos vértices DeclararVertices_Manual(); (*) Esta função pode ser substituida pela função alternativa DeclararVertices_Auto(). // Inicializa um objeto efeito inicializarEfeito(); // Inicializa e atualiza a configuração de câmera inicializar_Camera(); atualizarCamera(); 2.6 Inicialização do vertexbuffer Segue abaixo a listagem da função inicializar_Buffers() que não sofreu alterações especiais para esta aplicação, entretanto, o uso do vertexbuffer é obrigatório para habilitar o uso de hlsl. Esta mesma consideração se aplica à função montar_Geometria().
void inicializar_Buffers(void) { // Quantidade de vértices do vertexbuffer UINT nVertices = 6; // Tamanho do vertexbuffer em bytes UINT vbTamanho = sizeof(CustomVertex_PositionOnly) * nVertices; // Criação efetiva do buffer de vértices (vertexbuffer) g_hr = g_device->CreateVertexBuffer(vbTamanho, D3DUSAGE_WRITEONLY, CustomVertex_PositionOnly_Format, D3DPOOL_MANAGED, &g_vbVertices, NULL); // Verifica falha na criação if(FAILED (g_hr) ) { MessageBox (NULL, "Falha na criação do buffer de vértices", "inicializar_Buffers()", MB_OK); return; } // endif } // inicializar_Buffers().fim
Montagem do quadrado
void montar_Geometria(void) { // Posicionamento de profundidade float zpos = 1.0f; // Ponteiro de acesso aos dados do buffer de vértices CustomVertex_PositionOnly *pVerts; // Aqui a aplicação ganha acesso à memória do buffer de vértices g_vbVertices->Lock( 0, 0, (void**) &pVerts, 0); // Primeiro triângulo pVerts[0] = CustomVertex_PositionOnly( -1.0f, 1.0f, zpos ); pVerts[1] = CustomVertex_PositionOnly( -1.0f, -1.0f, zpos ); pVerts[2] = CustomVertex_PositionOnly( 1.0f, -1.0f, zpos ); // Segundo triângulo pVerts[3] = CustomVertex_PositionOnly( -1.0f, 1.0f, zpos ); pVerts[4] = CustomVertex_PositionOnly( 1.0f, -1.0f, zpos ); pVerts[5] = CustomVertex_PositionOnly( 1.0f, 1.0f, zpos ); // Liberação do vertexbuffer g_vbVertices->Unlock(); } // montar_Geometria().fim
2.7 Renderização da geometria com HLSL
void renderizar_Geometria() { // O VertexShader é informado da estruturação dos vértices g_device->SetVertexDeclaration( g_VertexDeclaration); // Informação do buffer de vértices utilizado g_device->SetStreamSource( 0, g_vbVertices, 0, sizeof(CustomVertex_PositionOnly) ); // Atualiza a câmera atualizarCamera(); // Número de passos do efeito UINT nPassosQtd = 0; // Renderiza o quadrado usando o efeito g_efeito->Begin(&nPassosQtd, 0); for(UINT nPasso = 0; nPasso < nPassosQtd; nPasso++) { // Uma cena pode ser renderizada com muitos passos g_efeito->BeginPass (nPasso); // Renderização efetiva dos vértices g_device->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 2); // Fim do passo g_efeito->EndPass (); } // endfor // Fim do efeito g_efeito->End (); } // renderizar_Geometria().fim
g_device->SetVertexDeclaration( g_VertexDeclaration); Nesta linha é informado ao shader a configuração dos vértices através da declaração produzida. g_device->SetStreamSource( 0, g_vbVertices, 0, sizeof(CustomVertex_PositionOnly) ); Temos aqui a configuração normal do vertexbuffer no dispositivo renderizador. atualizarCamera(); Essa função aqui faz o trabalho chave de atualizar valores e introjetá-los na linha programável do dispositivo gráfico. UINT nPassosQtd = 0; Esta variável é utilizada para coletar a quantidade de passos que o shader precisa para renderizar completamente a cena. O passo em si é um ciclo de aplicação de funções do shader. g_efeito->Begin(&nPassosQtd, 0); Aqui começa o processo de renderização com o código hlsl. A variável nPassosQtd é preenchida nessa chamada. O zero (0) indica que os estados modificados durante a aplicação do efeito serão restaurados no final desse processo sinalizado por g_efeito->EndPass(). Ao invés do zero (0) no segundo argumento pode ser utilizado a definição D3DXFX_DONOTSAVESAMPLERSTATE para não ocorrer esse processo de gravação e restauração dos estados entre os efeitos aplicados.
for (UINT nPasso = 0; nPasso < nPassosQtd; nPasso++) { g_efeito->BeginPass (nPasso); g_device->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 2); g_efeito->EndPass (); } // endfor
Esse bloco mostra a renderização da mesma geometria em cada ciclo dos diferentes passos aplicados. g_efeito->End (); } // renderizar_Geometria().fim A renderização pelo código hlsl do shader acaba aqui. 2.8 Renderização da cena Segue abaixo a listagem das funções Renderizar() e inicializar_Camera() lembrando que o destaque desta última é a ausência do método SetTransform() cujo trabalho equivalente foi feito pela sintaxe do hlsl.
VOID Renderizar() { // Retorne se o dispositivo estiver nulo if( g_device == NULL) return; // Limpa o backbuffer com uma cor verde g_device->Clear( 0, NULL, D3DCLEAR_TARGET, verde, 1.0f, 0); // Começa a cena if( SUCCEEDED( g_device->BeginScene() ) ) { // Vamos renderizar a geometria renderizar_Geometria(); // Finalizando a cena g_device->EndScene(); } // endif // Apresenta o conteúdo do backbuffer na tela g_device->Present( NULL, NULL, NULL, NULL); } // Renderizar().fim
Inicialização da câmera
void inicializar_Camera(void) { // *************************************************************************** // Inicializa todas as matrizes para elemento neutro D3DXMatrixIdentity( &g_mtxMundo ); D3DXMatrixIdentity( &g_mtxVisao ); D3DXMatrixIdentity( &g_mtxProj ); // *************************************************************************** // Dados para a configuração da matriz de visualização // Aonde está a câmera? - posição da câmera D3DXVECTOR3 cam_pos (0.0f, 0.0f, 5.0f); // Para aonde a câmera está apontada ou olhando? Alvo da câmera D3DXVECTOR3 cam_alvo (0.0f, 0.0f, 0.0f); // A câmera está de cabeça pra baixo? - orientação da câmera D3DXVECTOR3 cam_vetorcima (0.0f, 1.0f, 0.0f); // Configura a matriz de visão D3DXMatrixLookAtLH( &g_mtxVisao, &cam_pos, &cam_alvo, &cam_vetorcima ); // *************************************************************************** // Argumentos de configuração da matriz de projeção // Pega o tamanho da área cliente RECT area_cliente; GetWindowRect (hJanela, &area_cliente); g_xtela = area_cliente.right; g_ytela = area_cliente.bottom; // aspecto dos gráficos float aspecto = (float) g_xtela / g_ytela; // campo de visão float campo_visao = D3DX_PI / 4; // Trapézio de visualização da câmera ( Frustrum ) float corte_perto = 1.0f; float corte_longe = 1000.0f; // Configura a matriz de projeção D3DXMatrixPerspectiveFovLH( &g_mtxProj, campo_visao, aspecto, corte_perto, corte_longe); } // inicializar_Camera().fim
3.0 O primeiro programa em HLSL
// Gameprog: Introdução a programação em HLSL // Arquivo: simples.fx - prj_HSL01 // Matriz de projeção e visualização float4x4 Camera : WORLDVIEWPROJECTION; float nMovimento; // Transforma coordenadas 3d em espaço de tela float4 vs_Main( float4 Pos : POSITION) : POSITION { // Declara variável de saída float4 saida_pos; // Transforma posição Pos.z += nMovimento * 2; saida_pos = mul(Pos, Camera); // Retorna posição transformada return saida_pos; } // vs_Main().fim float4 ps_Main ( ) : COLOR0 { // Declara uma variável para cor no padrão RGBA float4 cor = float4 (0.0f, 0.0f, 0.0f, 0.0f); // Configura individualmente os canais de cor cor.r = nMovimento; cor.b = 1 - nMovimento; cor.ga = 1.0f; return cor; } // ps_Main().fim technique MovimentoCor { pass P0 { // Configura alguns estados do dispositivo CullMode = None; FillMode = Solid; // Compila o VertexShader e o PixelShader VertexShader = compile vs_1_1 vs_Main(); PixelShader = compile ps_2_0 ps_Main(); } // fim do passo P0 } // fim da técnica
3.1 Variáveis globais
float4x4 Camera : WORLDVIEWPROJECTION; float nMovimento;
float4x4 Camera : WORLDVIEWPROJECTION; Esta é uma variável global que representa a camera. Esse tipo de float float4x4 é um conjunto de 16 floats na forma de uma array 4x4. Depois dos dois pontos (:) vem a semântica da variável global. A semântica indica o significado e o uso particular de uma variável global para o Shader. Neste caso, o Shader fica sabendo que Camera é uma matriz de mundo-visualização-projeção indicado por WORLDVIEWPROJECTION. O uso da semântica é obrigatório nas variáveis globais de entrada e saída. float nMovimento; Esta é outra variável global modificável a partir de nossa aplicação. 3.2 A função de entrada do VertexShader: vs_Main()
float4 vs_Main( float4 Pos : POSITION) : POSITION { // Declara variável de saída float4 saida_pos; // Transforma a posição do vértice Pos.z += nMovimento * 2; saida_pos = mul(Pos, Camera); // Retorna a posição transformada return saida_pos; } // vs_Main().fim
Assinatura da função vs_Main() float4 vs_Main( float4 Pos : POSITION) : POSITION Essa é a assinatura da função de entrada do nosso VertexShader. Ela recebe um argumento float4 que representa cada vértice do modelo sendo renderizado, trabalha sobre esse argumento, e o retorna modificado e transformado para a aplicação. Da mesma forma que float4x4 quer dizer float[4][4], float4 quer dizer float[4] para guardar as coordenadas [x, y, z, w]. O w é um truque matemático chamado 'coordenada homogenada' para permitir o movimento de translação do modelo com uma operação de multiplicação no final do ciclo das transformações. Esse argumento de entrada tem ligação direta com o processo de declaração de vértice feito pela aplicação. // Declara variável de saída float4 saida_pos; Essa é a variável de saida que vai receber o resultado final do trabalho realizado individualmente em cada vértice. Pos.z += nMovimento * 2; Essa expressão matemática foi adicionada apenas para mostrar a a liberdade de realizar qualquer manipulação sobre os vértices que entram no VertexShader. Aqui os vértices vão pra frente ou para trás em seu eixo Z conforme o valor de nMovimento. Outro destaque do Pos.z aqui é que os elementos [x, y, z, w] podem ser acessados naturalmente como se fossem elementos de uma estrutura. saida_pos = mul(Pos, Camera); Esta linha realiza a multiplicação da Posição de entrada que já sofreu uma modificação no meio do caminho pela Camera para produzir a posição final transformada para a aplicação. É apenas uma linha de de código mas realiza nos bastidores uma extensa multiplicação com 20 elementos do tipo float ao multiplicar um float4 por um float4x4. return saida_pos; Aqui ocorre o retorno da posição transformada via saida_pos. Tirando algumas coisas como a semântica e os tipos específicos é como um programa escrito em linguagem C++. 3.3 A função de entrada do VertexShader: ps_Main()
float4 ps_Main ( ) : COLOR0 { // Declara uma variável para cor no padrão RGBA float4 cor = float4 (0.0f, 0.0f, 0.0f, 0.0f); // Configura individualmente os canais de cor cor.r = nMovimento; cor.b = 1 - nMovimento; cor.ga = 1.0f; return cor; } // ps_Main().fim
float4 ps_Main ( ) : COLOR0 Esta é a assinatura da função ps_Main() do nosso pixelshader particular. O valor de retorno é um float4 que representa uma cor no formato rgba com estes canais da cor sendo acessados individualmente por estas letras. COLOR0 é a semântica de cor. // Declara uma variável para cor no padrão RGBA float4 cor = float4 (0.0f, 0.0f, 0.0f, 0.0f); Esta é uma forma alternativa de inicializar os componentes individuais das variáveis. // Configura individualmente os canais de cor cor.r = nMovimento; cor.b = 1 - nMovimento; Aqui nestas linhas os canais vermelho (r) e azul (b) ganham uma pequena animação. cor.ga = 1.0f; Preste atenção atenção nesta linha. Aqui está outro recurso exclusivo da linguagem do shader. Configuramos simultaneamente o canal verde (g) e alfa (a) da cor na mesma instrução. Esse formato de assinalação de valores é chamado de swizzle. Através da oportunidade do swizzle os canais verde e alfa foram configurados cada um com o valor 1.0f com uma única instrução. return cor; A função retorna a cor para o dispositivo. 3.4 Estabelecendo uma técnica
technique MovimentoCor { pass P0 { // Configura alguns estados do dispositivo CullMode = None; FillMode = Solid; // Compila o VertexShader e o PixelShader VertexShader = compile vs_1_1 vs_Main(); PixelShader = compile ps_2_0 ps_Main(); } // fim do passo P0 } // fim da técnica
technique MovimentoCor { // (...) } O bloco da técnica declarado pela instrução do shader technique pode enquadrar vários blocos de passos identificados pela instrução pass. pass P0 { // (...) } Em cada passo pode ser aplicado um pixelshader e um vertexshader particular no modelo sendo renderizado. Em cada passo os estados do dispositivo também podem ser alterados conforme mostrado neste exemplo. // Configura alguns estados do dispositivo CullMode = None; FillMode = Solid; Aqui foi alterado os estados do dispositivo renderizador apenas para mostrar essa possibilidade dentro do shader. // Compila o VertexShader e o PixelShader VertexShader = compile vs_1_1 vs_Main(); PixelShader = compile ps_2_0 ps_Main(); Este é o código padrão que finaliza um passo. Tanto o VertexShader como o PixelShader são compilados nas versões indicadas respectivamente pelas instruções compile, vs_1_1 e ps_2_0. Uma coisa para esclarecer agora é que o nome de qualquer função dentro do shader não possui padrão de nomeação obrigatório. Escolhemos livremente vs_Main() para indicar a função principal de nosso VertexShader e o mesmo ocorrendo com ps_Main() em relação ao PixelShader. 4. Código fonte do projeto de exemplo: prj_HLSL01
// ----------------------------------------------------------------------------- // Projeto: prj_HLSL01 - Arquivo: motor.h // Esta aplicação mostra como renderizar um // quadrado colorido com HLSL. // By www.gameprog.com.br // ----------------------------------------------------------------------------- #pragma comment(lib, "d3d9.lib") #pragma comment(lib, "d3dx9.lib") #ifndef motor_h #define motor_h // Definição do formato de vértice utilizado por esta aplicação #define CustomVertex_PositionOnly_Format(D3DFVF_XYZ) // Estrutura de vértice customizado com posição apenas. struct CustomVertex_PositionOnly { float x, y, z; // Construtor default CustomVertex_PositionOnly() {} CustomVertex_PositionOnly( float _x, float _y, float _z) { x = _x; y = _y; z = _z; } }; // fim da estrutura CustomVertex_PositionOnly // Esta função inicializa o Direct3D HRESULT initGfx (HWND hJanela); // Essa função libera os objetos utilizados void Limpar(); // Essa função coordena a renderização a cena void Renderizar(); // Essa função inicializa o buffer de vértices void inicializar_Buffers(void); // Essa função monta as formas geométricas void montar_Geometria (void); // Renderiza as formas geométricas void renderizar_Geometria (void); // Faz a configuração inicial da câmera void inicializar_Camera(void); // Elabora a declaração de vértices void DeclararVertices_Auto(void); void DeclararVertices_Manual(void); // Inicializa o objeto efeito void inicializarEfeito(void); // Atualiza a câmera void atualizarCamera(void); // Declaração da função que atende as mensagens da janela LRESULT CALLBACK processaJanela ( HWND hJanela, UINT mensagem, WPARAM wParam, LPARAM lParam); #endif
// ----------------------------------------------------------------------------- // Projeto: prj_HLSL01 - arquivo: motor.cpp // Esta aplicação mostra como renderizar um // quadrado colorido com HLSL. // By www.gameprog.com.br // ----------------------------------------------------------------------------- #include <windows.h> #include <d3d9.h> #include <d3dx9.h> #include <stdio.h> #include "motor.h" // Variáveis globais // Representa o dispositivo Direct3D LPDIRECT3D9 g_Direct3d = NULL; // Representa o dispositivo Renderizador IDirect3DDevice9 *g_device = NULL; // Representa o buffer de vértices IDirect3DVertexBuffer9 *g_vbVertices = NULL; // Representa o objeto efeito ID3DXEffect *g_efeito = NULL; // Interface para a declaração de vértices IDirect3DVertexDeclaration9 *g_VertexDeclaration; // Variáveis para provocar mudanças de cor e movimento float angulo = 0.01f; float nMovimento = 0.0f; float nVelocidade = 0.01f; // Essa variável recebe informação de erro do Directx HRESULT g_hr = 0; // Tamanho da janela extern int g_xtela; extern int g_ytela; extern HWND hJanela; // Constante para cores const DWORD branco = 0xFFFFFFFF; const DWORD verde = 0xFF008000; // Matrizes de configuração da câmera D3DXMATRIX g_mtxMundo; D3DXMATRIX g_mtxVisao; D3DXMATRIX g_mtxProj; void DeclararVertices_Auto(void) { // Declaração coletiva dos elementos D3DVERTEXELEMENT9 Elementos[ sizeof(D3DVERTEXELEMENT9) * 2 ]; // Preenche a array dos elementos D3DXDeclaratorFromFVF(CustomVertex_PositionOnly_Format, Elementos ); // Criação da declaração de vértices g_hr = g_device->CreateVertexDeclaration(Elementos, &g_VertexDeclaration); } // DeclararVertices(void); void DeclararVertices_Manual(void) { // Argumentos para preenchimento da array D3DVERTEXELEMENT9 WORD nStream = 0; WORD nDeslocamento = 0; BYTE nTipo = D3DDECLTYPE_FLOAT3; BYTE nMetodo = D3DDECLMETHOD_DEFAULT; BYTE nUso = D3DDECLUSAGE_POSITION; BYTE nIndiceUso = 0; // Declaração do primeiro elemento D3DVERTEXELEMENT9 position = {nStream, nDeslocamento, nTipo, nMetodo, nUso, nIndiceUso }; // Declaração do elemento final D3DVERTEXELEMENT9 finalizador = D3DDECL_END(); // Declaração coletiva dos elementos D3DVERTEXELEMENT9 Elementos[] = { position, finalizador }; // Criação da declaração de vértices g_hr = g_device->CreateVertexDeclaration(Elementos, &g_VertexDeclaration); } // DeclararVertices(void); void inicializarEfeito() { // Nome do arquivo de efeito char fx_arquivo[] = "\\gameprog\\gdkmedia\\shader\\simples.fx"; // Flag de configuração DWORD create_flags = D3DXSHADER_DEBUG; // Buffer para receber informação em caso de erro ID3DXBuffer *txtErro = NULL; // Gera o efeito a partir do arquivo carregado g_hr = D3DXCreateEffectFromFile( g_device, fx_arquivo, 0, 0, create_flags, 0, &g_efeito, &txtErro); if (FAILED(g_hr)) { MessageBox(NULL, "falha: D3DXCreateEffectFromFile()", "inicializarEfeito", MB_OK); MessageBox(NULL, (char*) txtErro->GetBufferPointer(), "inicializarEfeito", MB_OK); txtErro->Release (); return; } // endif // Seleciona a técnica de renderização D3DXHANDLE htech_MovimentoCor; htech_MovimentoCor = g_efeito->GetTechniqueByName ("MovimentoCor"); g_efeito->SetTechnique(htech_MovimentoCor); // Libera o buffer de erros if (txtErro != NULL) txtErro->Release(); } // inicializarEfeito().fim void atualizarCamera(void) { // Atualiza movimento\cor através da variação de nMovimento nMovimento += nVelocidade; if (nMovimento >= 1.0f) nVelocidade *= -1; if (nMovimento <= 0.0f) nVelocidade *= -1; // Atualiza a câmera angulo += 0.05f; D3DXMatrixRotationZ (&g_mtxMundo, angulo); D3DXMATRIX camera = g_mtxMundo * g_mtxVisao * g_mtxProj; // Handle para a variável do shader 'nMovimento' D3DXHANDLE hMov = NULL; // Handle para a variável do shader 'camera' D3DXHANDLE hCam = NULL; // Obtém acesso às variáveis do shader hMov = g_efeito->GetParameterByName (0, "nMovimento"); hCam = g_efeito->GetParameterByName (0, "Camera"); // Atualiza variáveis no shader g_efeito->SetValue (hMov, &nMovimento, sizeof(nMovimento) ); g_efeito->SetValue (hCam, &camera, sizeof(camera) ); } // atualizarCamera(void); // initGfx() - Inicializa o Direct3D HRESULT initGfx( HWND hJanela ) { // Cria o objeto Direct3D que é necessário para criar o dispositivo gráfico g_Direct3d = Direct3DCreate9( D3D_SDK_VERSION); // Verifica se o objeto Direct3D foi criado if(g_Direct3d == NULL) { MessageBox (NULL, "Falha na inialização do Direct3D", "InitGfx()", MB_OK); return E_FAIL; } // endif // Declara a variável para os parâmetros de apresentação D3DPRESENT_PARAMETERS pps; // Limpa a estrutura ZeroMemory( &pps, sizeof(pps) ); // Configura os parâmetros de apresentação // A aplicação vai ter janela pps.Windowed = TRUE; // Esse método transfere rapidamente o backbuffer para a tela imediata pps.SwapEffect = D3DSWAPEFFECT_DISCARD; // Esse formato vai procurar se encaixar no modo de video corrente pps.BackBufferFormat = D3DFMT_UNKNOWN; // Configuração do renderizador a ser criado // Adaptador default (0) int nAdaptador = D3DADAPTER_DEFAULT; // Tipo de dispositivo Hardware ou emulador de referência (software) D3DDEVTYPE dispositivo_tipo = D3DDEVTYPE_HAL; // Flags de configuração do dispositivo DWORD create_flags = D3DCREATE_HARDWARE_VERTEXPROCESSING; // Criamos aqui o dispositivo renderizador g_hr = g_Direct3d->CreateDevice( nAdaptador, dispositivo_tipo, hJanela, create_flags, &pps, &g_device ); // Verifica se houve falha no processo if( FAILED( g_hr ) ) { MessageBox (NULL, "Falha na criação: g_device", "initGfx()", MB_OK); return E_FAIL; } // endif // Inicializa o vertexbuffer e configura os vértices inicializar_Buffers(); montar_Geometria(); // Elabora a declaração dos vértices DeclararVertices_Manual(); // Inicializa um objeto efeito inicializarEfeito(); // Inicializa e atualiza a configuração de câmera inicializar_Camera(); atualizarCamera(); return S_OK; } // initGfx().fim void inicializar_Buffers(void) { // Quantidade de vértices do vertexbuffer UINT nVertices = 6; // Tamanho do vertexbuffer em bytes UINT vbTamanho = sizeof(CustomVertex_PositionOnly) * nVertices; // Criação efetiva do buffer de vértices (vertexbuffer) g_hr = g_device->CreateVertexBuffer(vbTamanho, D3DUSAGE_WRITEONLY, CustomVertex_PositionOnly_Format, D3DPOOL_MANAGED, &g_vbVertices, NULL); // Verifica falha na criação if(FAILED (g_hr) ) { MessageBox (NULL, "Falha na criação do buffer de vértices", "inicializar_Buffers()", MB_OK); return; } // endif } // inicializar_Buffers().fim void montar_Geometria(void) { // Posicionamento de profundidade float zpos = 1.0f; // Ponteiro de acesso aos dados do buffer de vértices CustomVertex_PositionOnly *pVerts; // Aqui a aplicação ganha acesso à memória do buffer de vértices g_vbVertices->Lock( 0, 0, (void**) &pVerts, 0); // Primeiro triângulo pVerts[0] = CustomVertex_PositionOnly( -1.0f, 1.0f, zpos ); pVerts[1] = CustomVertex_PositionOnly( -1.0f, -1.0f, zpos ); pVerts[2] = CustomVertex_PositionOnly( 1.0f, -1.0f, zpos ); // Segundo triângulo pVerts[3] = CustomVertex_PositionOnly( -1.0f, 1.0f, zpos ); pVerts[4] = CustomVertex_PositionOnly( 1.0f, -1.0f, zpos ); pVerts[5] = CustomVertex_PositionOnly( 1.0f, 1.0f, zpos ); // Liberação do vertexbuffer g_vbVertices->Unlock(); } // montar_Geometria().fim void renderizar_Geometria() { // O VertexShader é informado da estruturação dos vértices g_device->SetVertexDeclaration( g_VertexDeclaration); // Informação do buffer de vértices utilizado g_device->SetStreamSource( 0, g_vbVertices, 0, sizeof(CustomVertex_PositionOnly) ); // Atualiza a câmera atualizarCamera(); // Número de passos do efeito UINT nPassosQtd = 0; // Renderiza o quadrado usando o efeito g_efeito->Begin(&nPassosQtd, 0); for(UINT nPasso = 0; nPasso < nPassosQtd; nPasso++) { // Uma cena pode ser renderizada com muitos passos g_efeito->BeginPass (nPasso); // Renderização efetiva dos vértices g_device->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 2); // Fim do passo g_efeito->EndPass (); } // endfor // Fim do efeito g_efeito->End (); } // renderizar_Geometria().fim // Esta função é chamada por DispatchMessage() LRESULT CALLBACK processaJanela (HWND hJanela, UINT mensagem, WPARAM wParam, LPARAM lParam) { switch (mensagem) { case WM_DESTROY: // Coloca uma mensagem WM_QUIT na fila de mensagem Limpar(); PostQuitMessage (0); break; case WM_KEYDOWN: if (wParam == VK_ESCAPE) { Limpar(); PostQuitMessage( 0); } // endif break; // Essa mensagem vai ocorrer a todo momento case WM_PAINT: // Renderiza a cena Renderizar(); // Invalida a tela para chamar WM_PAINT novamente InvalidateRect( hJanela, NULL, false); break; // Processamento default de mensagens não tratada pela aplicação default: return DefWindowProc (hJanela, mensagem, wParam, lParam); } // endswitch return 0; } // processaJanela().fim // Limpar() - Libera todos os objetos previamente inicializados // ----------------------------------------------------------------------------- VOID Limpar() { // Libera o buffer de vértices if( g_vbVertices != NULL) g_vbVertices->Release(); if (g_efeito != NULL) g_efeito->Release(); if (g_VertexDeclaration != NULL) g_VertexDeclaration->Release(); // Libera o dispositivo gráfico if( g_device != NULL) g_device->Release(); // Libera o motor do Direct3D if( g_Direct3d != NULL) g_Direct3d->Release(); // Anulação dos ponteiros g_device = NULL; g_Direct3d = NULL; g_vbVertices = NULL; g_efeito = NULL; g_VertexDeclaration = NULL; } // Limpar().fim // ----------------------------------------------------------------------------- // Renderizar() - Desenha a cena // ----------------------------------------------------------------------------- VOID Renderizar() { // Retorne se o dispositivo estiver nulo if( g_device == NULL) return; // Limpa o backbuffer com uma cor verde g_device->Clear( 0, NULL, D3DCLEAR_TARGET, verde, 1.0f, 0); // Começa a cena if( SUCCEEDED( g_device->BeginScene() ) ) { // Vamos renderizar a geometria renderizar_Geometria(); // Finalizando a cena g_device->EndScene(); } // endif // Apresenta o conteúdo do backbuffer na tela g_device->Present( NULL, NULL, NULL, NULL); } // Renderizar().fim void inicializar_Camera(void) { // *************************************************************************** // Inicializa todas as matrizes para elemento neutro D3DXMatrixIdentity( &g_mtxMundo ); D3DXMatrixIdentity( &g_mtxVisao ); D3DXMatrixIdentity( &g_mtxProj ); // *************************************************************************** // Dados para a configuração da matriz de visualização // Aonde está a câmera? - posição da câmera D3DXVECTOR3 cam_pos (0.0f, 0.0f, 5.0f); // Para aonde a câmera está apontada ou olhando? Alvo da câmera D3DXVECTOR3 cam_alvo (0.0f, 0.0f, 0.0f); // A câmera está de cabeça pra baixo? - orientação da câmera D3DXVECTOR3 cam_vetorcima (0.0f, 1.0f, 0.0f); // Configura a matriz de visão D3DXMatrixLookAtLH( &g_mtxVisao, &cam_pos, &cam_alvo, &cam_vetorcima ); // *************************************************************************** // Argumentos de configuração da matriz de projeção // Pega o tamanho da área cliente RECT area_cliente; GetWindowRect (hJanela, &area_cliente); g_xtela = area_cliente.right; g_ytela = area_cliente.bottom; // aspecto dos gráficos float aspecto = (float) g_xtela / g_ytela; // campo de visão float campo_visao = D3DX_PI / 4; // Trapézio de visualização da câmera ( Frustrum ) float corte_perto = 1.0f; float corte_longe = 1000.0f; // Configura a matriz de projeção D3DXMatrixPerspectiveFovLH( &g_mtxProj, campo_visao, aspecto, corte_perto, corte_longe); } // inicializar_Camera().fim
//----------------------------------------------------------------------------- // Projeto: prj_HLSL01 - arquivo: entrada.cpp // Esta aplicação mostra como renderizar um // quadrado colorido com HLSL. // By www.gameprog.com.br // ----------------------------------------------------------------------------- #include <windows.h> #include <d3d9.h> #include <d3dx9.h> #include "motor.h" // Variável global da classe da janela char sclasseJanela[ ] = "cls_directx"; // Dimensões da janela int g_xtela = 640; int g_ytela = 480; // alça da janela HWND hJanela; int WINAPI WinMain (HINSTANCE app_instancia, HINSTANCE app_anterior, LPSTR sComando,int nExibir) { // Estrutura de recepção das mensagens MSG mensagem; // Estrutura de descrição da janela WNDCLASSEX wcls; // Estrutura que descreve a janela wcls.hInstance = app_instancia; wcls.lpszClassName = sclasseJanela; wcls.lpfnWndProc = processaJanela; wcls.style = CS_HREDRAW | CS_VREDRAW; wcls.cbSize = sizeof (WNDCLASSEX); // O cursor e os ícones da aplicação são default wcls.hIcon = LoadIcon (NULL, IDI_APPLICATION); wcls.hIconSm = LoadIcon (NULL, IDI_APPLICATION); wcls.hCursor = LoadCursor (NULL, IDC_ARROW); // Aplicação sem menu wcls.lpszMenuName = NULL; // Nada de espaço extra atrelado a classe da janela (wcls) wcls.cbClsExtra = 0; // Nada de espaço extra atrelado a janela wcls.cbWndExtra = 0; // Cor default da janela wcls.hbrBackground = ( HBRUSH) COLOR_BACKGROUND; // Registra a janela e retorna se esta operação falhar int status = RegisterClassEx (&wcls); if(status == 0) { MessageBox(NULL, "Registro falhou!", "WinMain()", MB_OK); return 0; } // endif // Com a classe criada pode-se criar a janela DWORD estiloExtra = 0; const char janelaTitulo[] = "prj_HLSL01"; DWORD controleEstilo = WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX; int xpos = 160; int ypos = 120; HWND hjanelaPai = HWND_DESKTOP; HMENU sem_menu = NULL; LPVOID dadoExtra = NULL; // Cria a janela hJanela = CreateWindowEx(estiloExtra, sclasseJanela, janelaTitulo, controleEstilo, xpos, xpos, g_xtela, g_ytela, hjanelaPai, sem_menu, app_instancia, dadoExtra ); // Verifica se janela foi criada if(hJanela == NULL) { MessageBox(NULL, "Falha na criação da janela!", "WinMain()", MB_OK); return 0; } // endif // Essa variável recebe informação de erro do Directx HRESULT hr; // Inicia o Direct3D hr = initGfx ( hJanela ); // Encerre a aplicação se houve falha if(FAILED (hr) ) { MessageBox (hJanela, "Direct3D: falha na inicialização", "WinMain()", MB_OK); UnregisterClass( sclasseJanela, wcls.hInstance); return E_FAIL; } // endif // Mostra a janela ShowWindow(hJanela, nExibir); UpdateWindow(hJanela ); // Rode a bombeamento de mensagens até GetMessage() retornar 0 while (GetMessage(&mensagem, NULL, 0, 0)) { // Traduz mensagem de tecla virtual em mensagem de caracteres TranslateMessage( &mensagem ); // Despacha a mensagem para a função processaJanela */ DispatchMessage( &mensagem ); } // endwhile // O valor de retorno é zero(0) passado por PostQuitMessage() UnregisterClass( sclasseJanela, wcls.hInstance); return mensagem.wParam; } // WinMain().fim

index << >>

Produzido por Gameprog: Jair Pereira - Agosto/2014 © gameprog.br@gmail.com http://www.gameprog.com.br