Curso completo de DirectX 9 com C\C++
Gameprog - Escola de programação de jogos digitais
Contato: gameprog.br@gmail.com
Fase 07-1
07.1 Modificação de textura
1.1 Visão geral
De um ponto de vista prático a textura é apenas um recipiente
retangular de cores. A modificação da textura envolve conseguir
acesso a sua área retangular que pode ser pensada como uma tabela
quadriculada de linhas e colunas onde cada célula tem uma cor. A
modificação efetiva da textura vai consistir, em último grau, na
reconfiguração da cor de cada ponto. Aliás essa visão simplista da
textura enquadra-se naturalmente bem à tela e ao backubuffer. Em
última análise estas superfícies retangulares são apenas recipientes
de cores.
Esse projeto é baseado no projeto prj_Modelo3D do tópico 4.3 desse
curso. Fizemos pequenas modificações nesse projeto e acrescentamos
duas funções altamente semelhantes que modificam parcialmente ou
completamente a área da textura do modelo 3d com tons de cinza.
A capacidade de modificar a textura em tempo de execução permite o
efeito de texturas animadas. Um exemplo de uso de textura animada
consiste em usá-la na simulação de 'letreiros eletrônicos' de
informação ou o interior de naves espaciais com luzes que piscam.
Ainda, a edição de texturas permite a construção do seu próprio
editor de imagens visto que o directx dá um suporte fácil a gravação
da imagem no disco.
1.2 Estrutura principal da aplicação
Arquivo: motor.cpp
modificar_Textura()
Declara variáveis rgb para receber a cor da textura
Declara a variável para montar a cor temporária
Declara retângulo que receberá dados de cor da textura
Preenche retângulo com dados de cor da textura
Faz o cast dos dados recebidos para formato de cor acessível
Faz leitura da cor da textura
Produz tom de cinza com a média dos canais rgb da cor lida
Configura cor temporária com tom de cinza produzido
Escreve cor temporária na textura
Libera textura ao directx
modificar_TexturaEx()
Obtém descrição da textura
Declara retângulo que receberá dados de cor da textura
Declara retângulo-seleção que selecionará região de dados da textura
Configura retângulo-seleção com dados da descrição de textura
Preenche retângulo com dados de cor da textura com área limitada por
retângulo-seleção
Faz o cast dos dados recebidos para formato de cor acessível
Cálcula tamanho da área da textura com dados da descrição de textura
Produz tom de cinza com seleção aleatória
Configura cor temporária com tom de cinza produzido
Escreve cor temporária na textura
Libera textura ao directx
CarregarModeloTiny()
Declaração do nome completo do arquivo do modelo
Declarar um buffer para receber o material
Declaração do nome completo do arquivo da textura do modelo
Carregar modelo do disco com D3DXLoadMeshFromX()
Alocar memória para receber o material
Obter material do buffer genérico
Colocar material na variável global
Carregar textura
Liberar o buffer genérico.
desenharObjeto()
Esta função vai renderizar na tela o objeto da interface ID3DXMesh.
Isso envolve a configuração das matrizes parciais de rotação, posição
e escala; produzir a matriz final com as matrizes parciais; jogar a
matriz final no dispositivo renderizador;
configurar o material
configurar textura conforme o valor de g_texturaFlag
renderizar o subset zero(0).
initGfx()
chama configurar_cenaEstados() para preparação inicial da cena.
chama inicializar_Camera() para a configuração inicial da câmera
chama CarregarModeloTiny() para carregar o modelo 3d.
Renderizar()
Limpa a tela
Desliga filtros de textura
chama desenharObjeto() para renderizar o modelo 3d.
Liga filtros de textura
chama desenharObjeto() para renderizar o modelo 3d.
Apresenta a cena
Limpar()
Libera a interface de textura utilizada
Libera a memória alocada para o material
Libera o objeto da interface ID3DXMesh
Libera dispositivo renderizador
Libera objeto Direct3d
2.1.1 Aspectos globais - Arquivo: motor.h
//-----------------------------------------------------------------------------
// Projeto: prj_ModificarTextura - Arquivo: motor.h
// Esta aplicação mostra como modificar uma textura em tempo de execução
// By www.gameprog.com.br
//-----------------------------------------------------------------------------
#include <d3d9.h>
#include <d3dx9.h>
// Inclui as bibliotecas do Direct3D
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
// Desliga aviso de 'função insegura' devido ao uso das
// funções de string ( strcat etc... )
#pragma warning( disable:4996 )
#ifndef motor_h
#define motor_h
// Estrutura para receber dados de cor da textura
struct ARGBCOR
{
unsigned char a, r, g, b;
};
// Estrutura para controle de rotação e posição do objeto
struct Propriedades3d
{
D3DXVECTOR3 pos;
D3DXVECTOR3 rot;
};
// Esta função inicializa o Direct3D
HRESULT initGfx (HWND hJanela);
// Essa função libera os objetos utilizados
void Limpar();
// Essa função desenha a cena
void Renderizar();
// Monta a câmera da aplicação
void inicializar_Camera(void);
// Desenha o modelo 3d
void desenharObjeto ( ID3DXMesh *obj3d, Propriedades3d *props );
// Carrega o modelo tiny.x
void CarregarModeloTiny();
// Configura alguns estados da cena
void configurar_cenaEstados(void);
// Modifica parcialmente a textura para tons de cinza
void modificar_Textura (IDirect3DTexture9 *textura);
// Modifica completamente a textura para tons de cinza
void modificar_TexturaEx (IDirect3DTexture9 *textura);
// Declaração da função que atende as mensagens da janela
LRESULT CALLBACK processaJanela (HWND hJanela, UINT mensagem,
WPARAM wParam, LPARAM lParam);
#endif
#pragma warning( disable:4996 )
De acordo com os padrões atuais (2014) as antigas funções de string
da linguagem C tal como strcat() e outras não são consideradas
seguras pois não verificam a violação do espaço de buffer durante
a operação de seus processos. Ao usar estas funções padrões o
compilador da Microsoft pipoca avisos de que são inseguras; a
diretiva #pragma da forma como está configurada acima desliga estes
avisos que são inconvenientes para nossas pequenas aplicações. A
Microsoft provê para cada função string padrão da linguagem C uma
versão segura equivalente mas que preferimos não usar porque estraga
a legibilidade e a simplicidade das aplicações de exemplo.
struct ARGBCOR
{
unsigned char a, r, g, b;
};
A interface de textura quando libera acesso aos dados de cor libera
os dados com esse layout com a cor em formato argb. Esta estrutura
é utilizada então para receber os dados de cor liberados pela
interface de textura com essa formatação.
void Renderizar();
A função renderizar coordena a renderização da cena. Nesta função
testamos mais um vez os filtros de texturas que diferenciam o
aspecto visual do mesmo modelo renderizado duas vezes. Note que
o modelo renderizado à sua direita está com um melhor aspecto
visual.
void desenharObjeto ( ID3DXMesh *obj3d, Propriedades3d *props );
Essa função vai renderizar o modelo 3d aplicando de acordo com o
o valor do flag de controle a textura original (g_texturaFlag = 1) ou
a alternativa ( g_texturaFlag = -1 ) criada.
void CarregarModeloTiny();
Essa função é uma versão simplificada da função CarregarModelo() que
foi adaptada para carregar o modelo tiny.x aonde sabe-se que ele
tem apenas um subset, um material e uma textura.
void modificar_Textura (IDirect3DTexture9 *textura);
Essa função é a primeira demonstração de como modificar a textura.Ela
modifica metade da textura lendo a cor de cada texel e produzindo um
tom de cinza com o valor médio dos canais r,g,b do texel. Lembramos
que o texel é simplesmente cada ponto de cor da textura.
void modificar_TexturaEx (IDirect3DTexture9 *textura);
Essa função modifica a textura alternativa criada produzindo variados
tons da cor cinza que por final produz o efeito de 'tv fora do ar'.
LRESULT CALLBACK processaJanela ( // (...) );
Essa função no tratamento do pressionamento da tecla espaço inverte o
estado do flag de controle de texturização.
2.1.2 Aspectos globais - Arquivo: motor.cpp
// Recipientes de materiais e texturas do mesh
D3DMATERIAL9 *g_meshMtl = NULL;
IDirect3DTexture9 *g_meshTex = NULL;
IDirect3DTexture9 *g_texturaAlternativa = NULL;
// Controle de seleção da textura
int g_texturaFlag = 1;
// Interface do objeto 3d
ID3DXMesh *g_objeto3d = NULL;
D3DMATERIAL9 *g_meshMtl = NULL;
Esta variável vai receber o material do mesh.
IDirect3DTexture9 *g_meshTex = NULL;
Essa variável vai receber a textura original do modelo 3d.
IDirect3DTexture9 *g_texturaAlternativa = NULL;
Essa variável vai ser utilizada para criar uma textura alternativa
que vai ser modificada em cada frame antes da renderização do modelo
3d.
int g_texturaFlag = 1;
O valor desse flag vai controlar a textura usada no modelo 3d
selecionando a textura original (1) ou a textura alternativa (-1). A
barra de espaço dispara uma multiplicação por -1 neste flag e assim
inverte o seu estado.
ID3DXMesh *g_objeto3d = NULL;
Esta interface vai servir de recipiente do modelo 3d.
2.2 Carregando o modelo tiny.x
void CarregarModeloTiny()
{
// Nome final do arquivo do modelo 3d
char arquivo_x[] = "\\gameprog\\gdkmedia\\Modelos\\Tiny\\tiny.x";
// Buffer para o pacote de materiais e texturas do modelo 3d
ID3DXBuffer *mtlPack;
// Carrega modelo 3d com suas texturas e materiais
g_hr = D3DXLoadMeshFromX(arquivo_x, D3DXMESH_SYSTEMMEM,
g_device, NULL, &mtlPack, NULL, &g_mtlQtd, &g_objeto3d );
// Verifica falha no carregamento do modelo 3d
if(FAILED (g_hr) )
{
MessageBox (NULL,
"Falha no carregamento do modelo", "CarregarModeloTiny()", MB_OK);
return;
} // endif
// Pacote de materiais da biblioteca auxiliar d3dx
D3DXMATERIAL *xMtl = (D3DXMATERIAL *) mtlPack->GetBufferPointer();
// Inicializa a array de materiais
g_meshMtl = new D3DMATERIAL9[1];
// Copia o ponteiro do material
g_meshMtl[0] = xMtl[0].MatD3D;
// Configura a cor ambiente do material
g_meshMtl[0].Ambient = g_meshMtl[0].Diffuse;
// Nome completo da textura do modelo 3d
char *arquivo_textura = "\\gameprog\\gdkmedia\\Modelos\\Tiny\\Tiny_skin.bmp";
// Carrega a textura do disco
g_hr = D3DXCreateTextureFromFile(g_device, arquivo_textura, &g_meshTex);
// Criação da textura alternativa
g_hr = D3DXCreateTexture(g_device, 256, 256, 0,
D3DUSAGE_DYNAMIC, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT,
&g_texturaAlternativa);
// Verifica falha no carregamento ou criação das texturas
if(FAILED (g_hr) )
{
MessageBox (NULL,
"Falha na criação da textura", "CarregarModeloTiny()", MB_OK);
mtlPack->Release();
return;
} // endif
// Modifica a textura original
modificar_Textura (g_meshTex);
// Libera o buffer do pacote de materiais
mtlPack->Release();
} // CarregarModeloTiny().fim
g_hr = D3DXCreateTexture(g_device, 256, 256, 0, D3DUSAGE_DYNAMIC,
D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &g_texturaAlternativa);
Esta função cria uma textura compatível com a textura original do
modelo 3d. O tamanho da textura é 256x256. O valor zero(0) indica que
não haverá níveis superiores de mipmap. Na sequência é encontrado a
declaração do uso ( D3DUSAGE_DYNAMIC ), o formato ( D3DFMT_X8R8G8B8 )
e a classe ( D3DPOOL_DEFAULT ) de alojamento na memória da textura
criada.
modificar_Textura (g_meshTex);
Aqui a textura original é modificada parcialmente em torno de 50%
assumindo tons de cinza mais especialmente na parte que cobre a parte
superior do modelo 3d.
2.3.1 Modificação parcial da textura - modificar_Textura()
void modificar_Textura (IDirect3DTexture9 *textura)
{
// Componentes rgb para montar depois a cor final
unsigned char r, g, b;
// Cor temporária
ARGBCOR tempcor;
// Obtém a área de dados da textura
D3DLOCKED_RECT texData;
// Acessa os dados da textura
textura->LockRect(0, &texData, 0, 0);
// Acessa dados de cor
ARGBCOR *cores = (ARGBCOR *) texData.pBits;
// Escreve a média dos canais de cor na metade da textura
for (UINT linha = 0; linha < 32768; linha++)
{
// Leitura dos componentes rgb
r = cores->r;
g = cores->g;
b = cores->b;
// Média para deixar em preto e branco
unsigned char media = ( r + g + b ) / 3;
// Configura cor
tempcor.a = media;
tempcor.r = media;
tempcor.g = media;
tempcor.b = media;
// Escreve a cor na área de dados da textura
*cores = tempcor;
// Avança o ponteiro
cores++;
} // endfor linha
// Libera a textura para o sistema\placa de vídeo
textura->UnlockRect (0);
} // ModificarTextura().fim
unsigned char r, g, b;
Estas variáveis temporárias são usadas para receber os valores dos
canais rgb da cor que está na textura.
ARGBCOR tempcor;
Esta variável é usada para montar a cor final que vai ser escrita na
superfície da textura.
D3DLOCKED_RECT texData;
Esta variável texData é utilizada para receber os dados da textura
que são passados para a aplicação através do elemento pBits presente
nesta estrutura D3DLOCKED_RECT; pBits é um ponteiro do tipo void* e
por conta disso esse ponteiro recebe um cast para se conformar ao
formato de dados particular da superfície da textura.
textura->LockRect(0, &texData, 0, 0);
Esta linha com esta configuração recebe acesso ao total dos dados da
textura.
ARGBCOR *cores = (ARGBCOR *) texData.pBits;
Aqui de fato a aplicação tem acesso aos dados dentro do formato de
cor adequado para a leitura e escrita da superfície da textura.
for (UINT linha = 0; linha < 32768; linha++) {
Aqui montamos um laço para perpassar metade dos dados da textura.
O tamanho da textura original do modelo 3d carregado é 256x256 dando
uma área total de 65536 pontos de cor no formato argb.
r = cores->r;
g = cores->g;
b = cores->b;
Aqui é feito a leitura da cor do ponto sendo examinado.
unsigned char media = ( r + g + b ) / 3;
Aqui calculamos a média dos componentes rgb para produzir um tom de
cinza.
tempcor.a = media;
tempcor.r = media;
tempcor.g = media;
tempcor.b = media;
Aqui configuramos a cor temporária.
*cores = tempcor;
Aqui copiamos a cor temporária para um ponto na superfície da textura.
cores++; }
Aqui avançamos o ponteiro para o próximo ponto e fechamos o laço for.
textura->UnlockRect (0);
Aqui liberamos de volta ao directx o acesso à textura que foi
modificada.
2.3.2 Modificando completamente a textura - modificar_TexturaEx()
void modificar_TexturaEx (IDirect3DTexture9 *textura)
{
// Obtém a descrição da textura
D3DSURFACE_DESC desc;
g_hr = textura->GetLevelDesc (0, &desc);
// Retângulo para receber os dados de cor da textura
D3DLOCKED_RECT texData;
// Seleção da região a ser modificada da textura (toda a textura)
RECT texSelecao = { 0, 0, 0, 0 };
texSelecao.right = desc.Width;
texSelecao.bottom = desc.Height;
// Nível de mipmap
UINT mipNivel = 0;
// Flags de modo de acesso
DWORD tranca_flags = 0;
// Obtém os dados da textura
textura->LockRect(mipNivel, &texData, &texSelecao, tranca_flags);
// Faz o cast dos dados obtidos para o formato de cor argb
ARGBCOR *cores = (ARGBCOR *) texData.pBits;
// Cálcula o total de pontos de cor da textura
UINT nTotal = desc.Width * desc.Height;
// Escreve uma cor aleatória em cada ponto da textura
for (UINT ncx = 0; ncx < nTotal; ncx++)
{
// Seleciona um valor aleatório
unsigned char nval = (unsigned char) rand() % 255;
// Configura o valor nos canais de uma cor temporária
ARGBCOR tempcor;
tempcor.a = nval;
tempcor.r = nval;
tempcor.g = nval;
tempcor.b = nval;
// Escreve a cor na área de dados da textura
*cores = tempcor;
// Avança o ponteiro pra frente
cores++;
} // endfor linha
// Libera a textura para o sistema\placa de vídeo
textura->UnlockRect (0);
} // ModificarTexturaEx().fim
D3DSURFACE_DESC desc;
g_hr = textura->GetLevelDesc (0, &desc);
Estas linhas exemplificam a forma de obter informações sobre um
determinado nível de mipmap(0) da textura. Vamos obter dessa descrição
a largura e altura da textura. Nossa textura só tem o mipmap de nível
zero(0) sendo então este nível que representa a superfície principal
da textura.
D3DLOCKED_RECT texData;
Esta variável texData é utilizada para receber os dados da textura
que são passados para a aplicação através do elemento pBits presente
nesta estrutura D3DLOCKED_RECT; pBits é um ponteiro do tipo void* e
por conta disso esse ponteiro recebe um cast para se conformar ao
formato de dados particular da superfície da textura.
RECT texSelecao = { 0, 0, 0, 0 };
Este retângulo representa a seleção de uma região da textura.
texSelecao.right = desc.Width;
texSelecao.bottom = desc.Height;
Jogamos no retângulo que seleciona a região que queremos da textura
a descrição coletada. Com a configuração acima indicamos que queremos
a textura toda.
UINT mipNivel = 0;
DWORD tranca_flags = 0;
Aqui estão os outros dois argumentos do método LockRect() da
interface de textura. Foram nomeados aqui o nível de mipmap (mipNivel)
e a forma de acessar os dados ( tranca_flags ).
textura->LockRect(mipNivel, &texData, &texSelecao, tranca_flags);
Esta linha com esta configuração que foi montada acima recebe acesso
ao total dos dados da textura.
ARGBCOR *cores = (ARGBCOR *) texData.pBits;
Aqui de fato a aplicação tem acesso aos dados dentro do formato de
cor adequado para a leitura e escrita da superfície da textura.
UINT nTotal = desc.Width * desc.Height;
for (UINT ncx = 0; ncx < nTotal; ncx++) {
Aqui calculamos o total de pontos da textura e montamos adequadamente
o laço for com esta informação.
unsigned char nval = (unsigned char) rand() % 255;
Aqui calculamos um valor aleatório para um tom de cinza qualquer.
ARGBCOR tempcor; tempcor.a = nval;
tempcor.r = nval; tempcor.g = nval; tempcor.b = nval;
Aqui configuramos a cor temporária com o tom de cinza ( nval ).
*cores = tempcor;
Aqui copiamos a cor temporária para um ponto na superfície da textura.
cores++; }
Aqui avançamos o ponteiro para o próximo ponto e fechamos o laço for.
textura->UnlockRect (0);
Aqui liberamos de volta ao directx o acesso à textura que foi
modificada.
2.4 Renderização da cena
VOID Renderizar()
{
// Retorne se o dispositivo estiver nulo
if( g_device == NULL) return;
// Limpa o backbuffer com uma cor branca
g_device->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, branco, 1.0f, 0);
// Começa a cena
if( SUCCEEDED( g_device->BeginScene() ) )
{
// Vamos renderizar o modelo da esquerda sem filtros de textura
g_device->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_NONE);
g_device->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_NONE);
g_device->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
// Configura posição e renderiza
g_props.pos = D3DXVECTOR3 (100.0f, 0.2f, -20.0f);
g_props.rot = D3DXVECTOR3 (0.0f, 0.0f, 0.0f);
desenharObjeto( g_objeto3d, &g_props);
// Filtros para suavizar a textura do modelo da direita
g_device->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
g_device->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
// Salienta mais a visão das faces
g_device->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
// Configura posição e renderiza
g_props.pos = D3DXVECTOR3 (-150.0f, 40.0f, -20.0f);
desenharObjeto( g_objeto3d, &g_props);
// Finalizando a cena
g_device->EndScene();
} // endif
// Apresenta o conteúdo do backbuffer na tela
g_device->Present( NULL, NULL, NULL, NULL);
// Isso melhora a performance do sistema Windows
Sleep(0);
} // Renderizar().fim
g_device->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_NONE);
g_device->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_NONE);
g_device->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
g_props.pos = D3DXVECTOR3 (100.0f, 0.2f, -20.0f);
g_props.rot = D3DXVECTOR3 (0.0f, 0.0f, 0.0f);
desenharObjeto( g_objeto3d, &g_props);
Esse primeiro bloco desliga os filtros de textura e renderiza uma
instância do modelo 3d no lado esquerdo da tela.
g_device->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
g_device->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
// Salienta mais a visão das faces
g_device->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
// Configura posição e renderiza
g_props.pos = D3DXVECTOR3 (-150.0f, 40.0f, -20.0f);
desenharObjeto( g_objeto3d, &g_props);
Esse bloco liga os filtros de textura e renderiza uma instância do
modelo 3d no lado direito da tela. Repare que a textura fica mais
suave e o mesh mantém um contorno mais nítido de sua forma.
Sleep(0);
O Sleep() permite adicionar uma pausa temporária no programa. O
Sleep(0) não pausa mas permite que o Windows aplique um
fragmento de atenção aos outros processos que já estavam rodando no
sistema e assim mantém a suavidade do sistema ainda que rodando
uma aplicação 'pesada' como um jogo que devora muitos recursos.
2.5 Renderização do modelo 3d
void desenharObjeto ( ID3DXMesh *obj3d, Propriedades3d *props)
{
// Matrizes para controlar posição e rotação do objeto 3d
D3DXMATRIX obj_rot;
D3DXMATRIX obj_pos;
// Matriz para combinar todas as transformações do objeto 3d
D3DXMATRIX mtxCombinada;
// Vamos inicializar as matrizes para um valor neutro
D3DXMatrixIdentity( &obj_rot );
D3DXMatrixIdentity( &obj_pos );
D3DXMatrixIdentity( &mtxCombinada );
// Atualiza ângulo de rotação
g_angulo += 0.01f;
// Configura rotação do objeto 3d
D3DXMatrixRotationYawPitchRoll(&obj_rot,
props->rot.y,
props->rot.x + g_angulo,
props->rot.z);
// Ajusta posição do objeto 3d;
D3DXMatrixTranslation(&obj_pos,
props->pos.x, props->pos.y, props->pos.z);
// Tranfere posição e rotação para o mundo
D3DXMatrixMultiply (&mtxCombinada, &obj_rot, &obj_pos);
// Configura matriz mundo para o dispositivo renderizador
g_device->SetTransform( D3DTS_WORLD, &mtxCombinada );
// Renderiza o mesh
g_device->SetMaterial(&g_meshMtl[0] );
// Seleciona a textura de acordo com o flag g_texturaFlag
// Renderiza com textura original modificada
if (g_texturaFlag == 1) g_device->SetTexture(0, g_meshTex);
// Renderiza com textura alternativa
else
{
modificar_TexturaEx ( g_texturaAlternativa );
g_device->SetTexture(0, g_texturaAlternativa );
} // endif
// Renderiza o mesh
obj3d->DrawSubset(0);
} // desenharObjeto().fim
if (g_texturaFlag == 1) g_device->SetTexture(0, g_meshTex);
else
{
modificar_TexturaEx ( g_texturaAlternativa );
g_device->SetTexture(0, g_texturaAlternativa );
} // endif
obj3d->DrawSubset(0);
Esse bloco renderiza finalmente o modelo aplicando a textura original
ou a textura alternativa conforme o valor presente em g_texturaFlag.
Note que a textura é modificada por modificar_TexturaEx() antes de
ser aplicada no dispositivo renderizador.
3. Código fonte do projeto de exemplo: prj_ModificarTextura

//-----------------------------------------------------------------------------
// Projeto: prj_ModificarTextura - Arquivo: motor.h
// Esta aplicação mostra como modificar uma textura em tempo de execução
// By www.gameprog.com.br
//-----------------------------------------------------------------------------
#include <d3d9.h>
#include <d3dx9.h>
// Inclui as bibliotecas do Direct3D
#pragma comment(lib,
"d3d9.lib")
#pragma comment(lib,
"d3dx9.lib")
// Desliga aviso de 'função insegura' devido ao uso das
// funções de string ( strcat etc... )
#pragma warning( disable:4996 )
#ifndef motor_h
#define motor_h
// Estrutura para receber dados de cor da textura
struct ARGBCOR
{
unsigned char a, r, g, b;
};
// Estrutura para controle de rotação e posição do objeto
struct Propriedades3d
{
D3DXVECTOR3 pos;
D3DXVECTOR3 rot;
};
// Esta função inicializa o Direct3D
HRESULT initGfx (
HWND hJanela);
// Essa função libera os objetos utilizados
void Limpar();
// Essa função desenha a cena
void Renderizar();
// Monta a câmera da aplicação
void inicializar_Camera(
void);
// Desenha o modelo 3d
void desenharObjeto (
ID3DXMesh *obj3d,
Propriedades3d *props );
// Carrega o modelo tiny.x
void CarregarModeloTiny();
// Configura alguns estados da cena
void configurar_cenaEstados(
void);
// Modifica parcialmente a textura para tons de cinza
void modificar_Textura (
IDirect3DTexture9 *textura);
// Modifica completamente a textura para tons de cinza
void modificar_TexturaEx (
IDirect3DTexture9 *textura);
// 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_ModificarTextura - arquivo: motor.cpp
// Esta aplicação mostra como modificar uma textura em tempo de execução
// By www.gameprog.com.br
// -----------------------------------------------------------------------------
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include "motor.h"
// Variáveis globais
// Representa o dispositivo Direct3D
LPDIRECT3D9 g_Direct3d = NULL;
// Representa o dispositivo Renderizador
IDirect3DDevice9* g_device = NULL;
// Recipiente de materiais e texturas do mesh
D3DMATERIAL9 *g_meshMtl = NULL;
IDirect3DTexture9 *g_meshTex = NULL;
IDirect3DTexture9 *g_texturaAlternativa = NULL;
// Controle de seleção da textura
int g_texturaFlag = 1;
// Interface do objeto 3d
ID3DXMesh *g_objeto3d = NULL;
// Quantidade de materiais no objeto 3d
DWORD g_mtlQtd = 0;
// Recipientes de cor, rotação e posição do objeto
Propriedades3d g_props;
// Controla o ângulo de rotação do objeto 3d
float g_angulo = 0.0f;
// 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;
// Matrizes de configuração da câmera
D3DXMATRIX g_mtxMundo;
D3DXMATRIX g_mtxVisao;
D3DXMATRIX g_mtxProj;
void CarregarModeloTiny()
{
// Nome final do arquivo do modelo 3d
char arquivo_x[] = "\\gameprog\\gdkmedia\\Modelos\\Tiny\\tiny.x";
// Buffer para o pacote de materiais e texturas do modelo 3d
ID3DXBuffer *mtlPack;
// Carrega modelo 3d com suas texturas e materiais
g_hr = D3DXLoadMeshFromX(arquivo_x, D3DXMESH_SYSTEMMEM,
g_device, NULL, &mtlPack, NULL, &g_mtlQtd, &g_objeto3d );
// Verifica falha no carregamento do modelo 3d
if(FAILED (g_hr) )
{
MessageBox (NULL,
"Falha no carregamento do modelo", "CarregarModeloTiny()", MB_OK);
return;
} // endif
// Pacote de materiais da biblioteca auxiliar d3dx
D3DXMATERIAL *xMtl = (D3DXMATERIAL *) mtlPack->GetBufferPointer();
// Inicializa a array de materiais
g_meshMtl = new D3DMATERIAL9[1];
// Copia o ponteiro do material
g_meshMtl[0] = xMtl[0].MatD3D;
// Configura a cor ambiente do material
g_meshMtl[0].Ambient = g_meshMtl[0].Diffuse;
// Nome completo da textura do modelo 3d
char *arquivo_textura = "\\gameprog\\gdkmedia\\Modelos\\Tiny\\Tiny_skin.bmp";
// Carrega a textura do disco
g_hr = D3DXCreateTextureFromFile(g_device, arquivo_textura, &g_meshTex);
// Criação da textura alternativa
g_hr = D3DXCreateTexture(g_device, 256, 256, 0,
D3DUSAGE_DYNAMIC, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT,
&g_texturaAlternativa);
// Verifica falha no carregamento ou criação das texturas
if(FAILED (g_hr) )
{
MessageBox (NULL,
"Falha na criação da textura", "CarregarModeloTiny()", MB_OK);
mtlPack->Release();
return;
} // endif
// Modifica a textura original
modificar_Textura (g_meshTex);
// Libera o buffer do pacote de materiais
mtlPack->Release();
} // CarregarModeloTiny().fim
void modificar_Textura (IDirect3DTexture9 *textura)
{
// Componentes rgb para montar depois a cor final
unsigned char r, g, b;
// Cor temporária
ARGBCOR tempcor;
// Obtém a área de dados da textura
D3DLOCKED_RECT texData;
// Acessa os dados da textura
textura->LockRect(0, &texData, 0, 0);
// Acessa dados de cor
ARGBCOR *cores = (ARGBCOR *) texData.pBits;
// Escreve a média dos canais de cor na metade da textura
for (UINT linha = 0; linha < 32768; linha++)
{
// Leitura dos componentes rgb
r = cores->r;
g = cores->g;
b = cores->b;
// Média para deixar em preto e branco
unsigned char media = ( r + g + b ) / 3;
// Configura cor
tempcor.a = media;
tempcor.r = media;
tempcor.g = media;
tempcor.b = media;
// Escreve a cor na área de dados da textura
*cores = tempcor;
// Avança o ponteiro
cores++;
} // endfor linha
// Libera a textura para o sistema\placa de vídeo
textura->UnlockRect (0);
} // ModificarTextura().fim
void modificar_TexturaEx (IDirect3DTexture9 *textura)
{
// Obtém a descrição da textura
D3DSURFACE_DESC desc;
g_hr = textura->GetLevelDesc (0, &desc);
// Retângulo para receber os dados de cor da textura
D3DLOCKED_RECT texData;
// Seleção da região a ser modificada da textura (toda a textura)
RECT texSelecao = { 0, 0, 0, 0 };
texSelecao.right = desc.Width;
texSelecao.bottom = desc.Height;
// Nível de mipmap
UINT mipNivel = 0;
// Flags de modo de acesso
DWORD tranca_flags = 0;
// Obtém os dados da textura
textura->LockRect(mipNivel, &texData, &texSelecao, tranca_flags);
// Faz o cast dos dados obtidos para o formato de cor argb
ARGBCOR *cores = (ARGBCOR *) texData.pBits;
// Cálcula o total de pontos de cor da textura
UINT nTotal = desc.Width * desc.Height;
// Escreve uma cor aleatória em cada ponto da textura
for (UINT ncx = 0; ncx < nTotal; ncx++)
{
// Seleciona um valor aleatório
unsigned char nval = (unsigned char) rand() % 255;
// Configura o valor nos canais de uma cor temporária
ARGBCOR tempcor;
tempcor.a = nval;
tempcor.r = nval;
tempcor.g = nval;
tempcor.b = nval;
// Escreve a cor na área de dados da textura
*cores = tempcor;
// Avança o ponteiro pra frente
cores++;
} // endfor linha
// Libera a textura para o sistema\placa de vídeo
textura->UnlockRect (0);
} // ModificarTexturaEx().fim
// 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) );
// Vamos ativar o buffer de profundidade
pps.EnableAutoDepthStencil = true;
pps.AutoDepthStencilFormat = D3DFMT_D16;
// 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_SOFTWARE_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 gerador de números aleatórios
srand( clock() );
// Configura uma câmera para a aplicação
inicializar_Camera();
// Configura alguns estados da cena
configurar_cenaEstados();
// Carrega o modelo tiny.x
CarregarModeloTiny();
return S_OK;
} // initGfx().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:
// Inverte a textura aplicada
if (wParam == VK_SPACE) g_texturaFlag *= -1;
// Encerra a aplicação
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 a memória de materiais e textura
if (g_meshTex != NULL) g_meshTex->Release();
if (g_meshMtl != NULL) delete g_meshMtl;
if (g_texturaAlternativa != NULL) g_texturaAlternativa->Release();
// Libera o objeto 3d, o renderizador e o Direct3d
if( g_objeto3d != NULL) g_objeto3d->Release();
if( g_device != NULL) g_device->Release();
if( g_Direct3d != NULL) g_Direct3d->Release();
// Anula os ponteiros
g_objeto3d = NULL;
g_device = NULL;
g_Direct3d = NULL;
g_meshTex = NULL;
g_meshMtl = NULL;
g_texturaAlternativa = 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 branca
g_device->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, branco, 1.0f, 0);
// Começa a cena
if( SUCCEEDED( g_device->BeginScene() ) )
{
// Vamos renderizar o modelo da esquerda sem filtros de textura
g_device->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_NONE);
g_device->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_NONE);
g_device->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
// Configura posição e renderiza
g_props.pos = D3DXVECTOR3 (100.0f, 0.2f, -20.0f);
g_props.rot = D3DXVECTOR3 (0.0f, 0.0f, 0.0f);
desenharObjeto( g_objeto3d, &g_props);
// Filtros para suavizar a textura do modelo da direita
g_device->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
g_device->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
// Salienta mais a visão das faces
g_device->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
// Configura posição e renderiza
g_props.pos = D3DXVECTOR3 (-150.0f, 40.0f, -20.0f);
desenharObjeto( g_objeto3d, &g_props);
// Finalizando a cena
g_device->EndScene();
} // endif
// Apresenta o conteúdo do backbuffer na tela
g_device->Present( NULL, NULL, NULL, NULL);
// Isso melhora a performance do sistema Windows
Sleep(0);
} // Renderizar().fim
void inicializar_Camera(void)
{
// ***************************************************************************
// Inicializa todas as matrizes para elemento neutro
D3DXMatrixIdentity( &g_mtxMundo );
D3DXMatrixIdentity( &g_mtxVisao );
D3DXMatrixIdentity( &g_mtxProj );
// ***************************************************************************
// Configura a matriz de mundo no dispositivo renderizador
g_device->SetTransform( D3DTS_WORLD, &g_mtxMundo );
// ***************************************************************************
// 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, 700.0f);
// Para aonde a câmera está apontada ou olhando? Alvo da câmera
D3DXVECTOR3 cam_alvo (0.0f, 0.0f, 0);
// 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 );
// Configura a matriz de visão no dispositivo renderizador
g_device->SetTransform( D3DTS_VIEW, &g_mtxVisao );
// ***************************************************************************
// 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);
// Configura a matriz de projeção no dispositivo renderizador
g_device->SetTransform( D3DTS_PROJECTION, &g_mtxProj );
} // inicializar_Camera().fim
void desenharObjeto ( ID3DXMesh *obj3d, Propriedades3d *props)
{
// Matrizes para controlar posição e rotação do objeto 3d
D3DXMATRIX obj_rot;
D3DXMATRIX obj_pos;
// Matriz para combinar todas as transformações do objeto 3d
D3DXMATRIX mtxCombinada;
// Vamos inicializar as matrizes para um valor neutro
D3DXMatrixIdentity( &obj_rot );
D3DXMatrixIdentity( &obj_pos );
D3DXMatrixIdentity( &mtxCombinada );
// Atualiza ângulo de rotação
g_angulo += 0.01f;
// Configura rotação do objeto 3d
D3DXMatrixRotationYawPitchRoll(&obj_rot,
props->rot.y,
props->rot.x + g_angulo,
props->rot.z);
// Ajusta posição do objeto 3d;
D3DXMatrixTranslation(&obj_pos,
props->pos.x, props->pos.y, props->pos.z);
// Tranfere posição e rotação para o mundo
D3DXMatrixMultiply (&mtxCombinada, &obj_rot, &obj_pos);
// Configura matriz mundo para o dispositivo renderizador
g_device->SetTransform( D3DTS_WORLD, &mtxCombinada );
// Renderiza o mesh
g_device->SetMaterial(&g_meshMtl[0] );
// Seleciona a textura de acordo com o flag g_texturaFlag
// Renderiza com textura original modificada
if (g_texturaFlag == 1) g_device->SetTexture(0, g_meshTex);
// Renderiza com textura alternativa
else
{
modificar_TexturaEx ( g_texturaAlternativa );
g_device->SetTexture(0, g_texturaAlternativa );
} // endif
// Renderiza o mesh
obj3d->DrawSubset(0);
} // desenharObjeto().fim
void configurar_cenaEstados(void)
{
// Configuração de iluminação
g_device->SetRenderState( D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255) );
g_device->SetRenderState (D3DRS_LIGHTING, true);
// Configura modo de shading
g_device->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
// Habilita uso de buffer de profundidade
g_device->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE);
// Desliga o culling
g_device->SetRenderState (D3DRS_CULLMODE, D3DCULL_NONE);
} // configurar_cenaEstados().fim
//-----------------------------------------------------------------------------
// Projeto: prj_ModificarTextura - arquivo: entrada.cpp
// Esta aplicação mostra como modificar uma textura em tempo de execução
// 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_ModificarTextura - Pressione a barra de espaços";
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