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