Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Esta página irá mostrar-lhe como gerar e usar um efeito. Os tópicos abordados incluem como:
- Criar um efeito
- Renderizar um efeito
- Usar semântica para encontrar parâmetros de efeito
- Usar Manipuladores para Obter e Definir Parâmetros de Forma Eficiente
- Adicionar informações de parâmetro com anotações
- Parâmetros de efeito de compartilhamento
- compilar um efeito offline
- Melhore o desempenho com pré-sombreadoras
- usar blocos de parâmetros para gerenciar parâmetros de efeito
Criar um efeito
Aqui está um exemplo de criação de um efeito retirado do BasicHLSL Sample. O código de criação de efeito para criar um sombreador de depuração é de OnCreateDevice:
ID3DXEffect* g_pEffect = NULL;
DWORD dwShaderFlags = 0;
dwShaderFlags |= D3DXSHADER_FORCE_VS_SOFTWARE_NOOPT;
dwShaderFlags |= D3DXSHADER_FORCE_PS_SOFTWARE_NOOPT;
dwShaderFlags |= D3DXSHADER_NO_PRESHADER;
// Read the D3DX effect file
WCHAR str[MAX_PATH];
DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"BasicHLSL.fx" );
D3DXCreateEffectFromFile(
pd3dDevice,
str,
NULL, // CONST D3DXMACRO* pDefines,
NULL, // LPD3DXINCLUDE pInclude,
dwShaderFlags,
NULL, // LPD3DXEFFECTPOOL pPool,
&g_pEffect,
NULL );
Esta função utiliza estes argumentos:
- O dispositivo.
- O nome do arquivo de efeito.
- Um ponteiro para uma lista de #defines terminada por NULL, a ser usada durante a análise do sombreador.
- Um ponteiro opcional para um manipulador de inclusão escrito pelo utilizador. O manipulador é chamado pelo processador sempre que precisa resolver um #include.
- Uma flag de compilação de sombreadores que dá ao compilador dicas acerca de como o sombreador será utilizado. As opções incluem:
- Ignorando a validação, caso estejam a ser compilados sombreadores considerados bons.
- Ignorar a otimização (às vezes usado quando as otimizações dificultam a depuração).
- Solicitando a inclusão de informações de depuração no shader para que possa ser depurado.
- O pool de efeitos. Se mais de um efeito usar o mesmo ponteiro de pool de memória, as variáveis globais nos efeitos serão compartilhadas entre si. Se não houver necessidade de compartilhar variáveis de efeito, o pool de memória pode ser definido como NULL.
- Um ponteiro para o novo efeito.
- Um ponteiro para um buffer onde os erros de validação podem ser enviados. Neste exemplo, o parâmetro foi definido como NULL e não usado.
Observação
A partir do SDK de dezembro de 2006, o compilador HLSL do DirectX 10 agora é o compilador padrão no DirectX 9 e no DirectX 10. Consulte Effect-Compiler Tool para obter detalhes.
Gerar um efeito
A sequência de chamadas para aplicar o estado de efeito a um dispositivo é:
- ID3DXEffect::Begin define a técnica ativa.
- ID3DXEffect::BeginPass define o passo ativo.
- ID3DXEffect::CommitChanges atualiza as alterações em todas as chamadas definidas no passe. Isso deve ser chamado antes de qualquer chamada de sorteio.
- ID3DXEffect::EndPass termina um passe.
- ID3DXEffect::End finaliza a técnica ativa.
O código de renderização de efeito também é mais simples do que o código de renderização correspondente sem um efeito. Aqui está o código de renderização com um efeito:
// Apply the technique contained in the effect
g_pEffect->Begin(&cPasses, 0);
for (iPass = 0; iPass < cPasses; iPass++)
{
g_pEffect->BeginPass(iPass);
// Only call CommitChanges if any state changes have happened
// after BeginPass is called
g_pEffect->CommitChanges();
// Render the mesh with the applied technique
g_pMesh->DrawSubset(0);
g_pEffect->EndPass();
}
g_pEffect->End();
O ciclo de renderização consiste em consultar o efeito para ver quantas etapas ele contém e, em seguida, chamar todas as etapas para uma técnica. O loop de renderização pode ser expandido para chamar várias técnicas, cada uma com várias passagens.
Usar semântica para localizar parâmetros de efeito
Uma semântica é um identificador anexado a um parâmetro de efeito para permitir que um aplicativo pesquise o parâmetro. Um parâmetro pode ter no máximo uma semântica. A semântica está localizada após dois pontos (:) após o nome do parâmetro. Por exemplo:
float4x4 matWorldViewProj : WORLDVIEWPROJ;
Se você declarasse a variável global de efeito sem usar uma semântica, ela ficaria assim:
float4x4 matWorldViewProj;
A interface de efeito pode usar uma semântica para obter um identificador para um parâmetro de efeito específico. Por exemplo, o seguinte retorna o identificador da matriz:
D3DHANDLE handle =
m_pEffect->GetParameterBySemantic(NULL, "WORLDVIEWPROJ");
Além de pesquisar por nome semântico, a interface de efeitos tem muitos outros métodos para procurar parâmetros.
Use manipuladores para obter e definir parâmetros de forma eficiente
As alças fornecem um meio eficiente para referenciar parâmetros de efeito, técnicas, passes e anotações com um efeito. As alças (que são do tipo D3DXHANDLE) são ponteiros de cadeia de caracteres. Os manípulos que são passados para funções como GetParameterxxx ou GetAnnotationxxx podem estar em uma de três formas:
- Um identificador retornado por uma função como GetParameterxxx.
- Uma cadeia de caracteres que contém o nome do parâmetro, técnica, passagem ou anotação.
- Um identificador definido como NULL.
Este exemplo retorna um identificador para o parâmetro que tem a semântica WORLDVIEWPROJ anexada a ele:
D3DHANDLE handle =
m_pEffect->GetParameterBySemantic(NULL, "WORLDVIEWPROJ");
Adicionar informações de parâmetro com anotações
As anotações são dados específicos do usuário que podem ser anexados a qualquer técnica, passagem ou parâmetro. Uma anotação é uma maneira flexível de adicionar informações a parâmetros individuais. As informações podem ser lidas de volta e usadas da maneira que o aplicativo escolher. Uma anotação pode ser de qualquer tipo de dados e pode ser adicionada dinamicamente. As declarações de anotação são delimitadas por colchetes angulares. Uma anotação contém:
- Um tipo de dados.
- Um nome de variável.
- Um sinal de igual (=).
- O valor dos dados.
- Um ponto-e-vírgula final (;).
Por exemplo, ambos os exemplos anteriores neste artigo contêm esta anotação:
texture Tex0 < string name = "tiger.bmp"; >;
A anotação é anexada ao objeto de textura e especifica o arquivo de textura que deve ser usado para inicializar o objeto de textura. A anotação não inicializa o objeto de textura, é simplesmente uma parte da informação do usuário que é anexada à variável. Um aplicativo pode ler a anotação com ID3DXBaseEffect::GetAnnotation ou ID3DXBaseEffect::GetAnnotationByName para retornar a cadeia de caracteres. Anotações também podem ser adicionadas pelo aplicativo.
Cada anotação:
- Deve ser ou numérico ou texto.
- Deve sempre ser inicializado com um valor padrão.
- Pode ser associado a Técnicas e Passes (Direct3D 9) e a Parâmetros de Efeito de nível superior.
- Pode ser escrito e lido com ID3DXEffect ou ID3DXEffectCompiler.
- Pode ser adicionado com ID3DXEffect.
- Não pode ser referenciado dentro do efeito.
- Não pode ter subsemântica ou sub-anotações.
Parâmetros de efeito de compartilhamento
Os parâmetros de efeito são todas as variáveis não estáticas declaradas em um efeito. Isso pode incluir variáveis globais e anotações. Os parâmetros de efeito podem ser compartilhados entre diferentes efeitos declarando parâmetros com a palavra-chave "compartilhado" e, em seguida, criando o efeito com um pool de efeitos.
Um pool de efeitos contém parâmetros de efeito compartilhados. O pool é criado chamando D3DXCreateEffectPool, que retorna um ID3DXEffectPool interface. A interface pode ser fornecida como uma entrada para qualquer uma das funções D3DXCreateEffectxxx quando um efeito é criado. Para que um parâmetro seja compartilhado entre vários efeitos, o parâmetro deve ter o mesmo nome, tipo e semântica em cada um dos efeitos compartilhados.
ID3DXEffectPool* g_pEffectPool = NULL; // Effect pool for sharing parameters
D3DXCreateEffectPool( &g_pEffectPool );
Os efeitos que partilham parâmetros devem utilizar o mesmo dispositivo. Isso é aplicado para impedir o compartilhamento de parâmetros dependentes do dispositivo (como sombreadores ou texturas) entre diferentes dispositivos. Os parâmetros são excluídos do pool sempre que os efeitos que contêm os parâmetros compartilhados são liberados. Se os parâmetros de compartilhamento não forem necessários, forneça NULL para o pool de efeitos quando um efeito for criado.
Os efeitos clonados usam o mesmo conjunto de efeitos do qual são clonados. A clonagem de um efeito faz uma cópia exata de um efeito, incluindo variáveis globais, técnicas, passes e anotações.
Compilar um efeito offline
Você pode compilar um efeito em tempo de execução com D3DXCreateEffectou pode compilar um efeito offline usando a ferramenta de compilador de linha de comando fxc.exe. O efeito no CompiledEffect Sample contém um sombreador de vértice, um sombreador de pixel e uma técnica:
// File: CompiledEffect.fx
// Global variables
float4 g_MaterialAmbientColor; // Material's ambient color
...
// Texture samplers
sampler RenderTargetSampler =
...
// Type: Vertex shader
VS_OUTPUT RenderSceneVS( float4 vPos : POSITION,
float3 vNormal : NORMAL,
float2 vTexCoord0 : TEXCOORD0 )
{
...
};
// Type: Pixel shader
PS_OUTPUT RenderScenePS( VS_OUTPUT In )
{
...
}
// Type: Technique
technique RenderScene
{
pass P0
{
ZENABLE = true;
VertexShader = compile vs_1_1 RenderSceneVS();
PixelShader = compile ps_1_1 RenderScenePS();
}
}
Usando Effect-Compiler Ferramenta para compilar o sombreador para vs_1_1, gerou as seguintes instruções de sombreador em assembler:
//
// Generated by Microsoft (R) D3DX9 Shader Compiler 4.09.02.1188
//
// fxc /T vs_1_1 /E RenderSceneVS /Fc CompiledEffect.txt CompiledEffect.fx
//
//
// Parameters:
//
// float4 g_LightAmbient;
// float4 g_LightDiffuse;
// float3 g_LightDir;
// float4 g_MaterialAmbientColor;
// float4 g_MaterialDiffuseColor;
// float g_fTime;
// float4x4 g_mWorld;
// float4x4 g_mWorldViewProjection;
//
//
// Registers:
//
// Name Reg Size
// ---------------------- ----- ----
// g_mWorldViewProjection c0 4
// g_mWorld c4 3
// g_MaterialAmbientColor c7 1
// g_MaterialDiffuseColor c8 1
// g_LightDir c9 1
// g_LightAmbient c10 1
// g_LightDiffuse c11 1
// g_fTime c12 1
//
//
// Default values:
//
// g_LightDir
// c9 = { 0.57735, 0.57735, 0.57735, 0 };
//
// g_LightAmbient
// c10 = { 1, 1, 1, 1 };
//
// g_LightDiffuse
// c11 = { 1, 1, 1, 1 };
//
vs_1_1
def c13, 0.159154937, 0.25, 6.28318548, -3.14159274
def c14, -2.52398507e-007, 2.47609005e-005, -0.00138883968, 0.0416666418
def c15, -0.5, 1, 0.5, 0
dcl_position v0
dcl_normal v1
dcl_texcoord v2
mov r0.w, c12.x
mad r0.w, r0.w, c13.x, c13.y
expp r3.y, r0.w
mov r0.w, r3.y
mad r0.w, r0.w, c13.z, c13.w
mul r0.w, r0.w, r0.w
mad r1.w, r0.w, c14.x, c14.y
mad r1.w, r0.w, r1.w, c14.z
mad r1.w, r0.w, r1.w, c14.w
mad r1.w, r0.w, r1.w, c15.x
mad r0.w, r0.w, r1.w, c15.y
mul r0.w, r0.w, v0.x
mul r0.x, r0.w, c15.z
dp3 r1.x, v1, c4
dp3 r1.y, v1, c5
dp3 r1.z, v1, c6
mov r0.yzw, c15.w
dp3 r2.x, r1, r1
add r0, r0, v0
rsq r1.w, r2.x
dp4 oPos.x, r0, c0
mul r1.xyz, r1, r1.w
dp4 oPos.y, r0, c1
dp3 r1.x, r1, c9
dp4 oPos.z, r0, c2
max r1.w, r1.x, c15.w
mov r1.xyz, c8
mul r1.xyz, r1, c11
mov r2.xyz, c7
mul r2.xyz, r2, c10
dp4 oPos.w, r0, c3
mad oD0.xyz, r1, r1.w, r2
mov oD0.w, c15.y
mov oT0.xy, v2
// approximately 34 instruction slots used
Melhore o desempenho com pré-sombreadores
Um pré-sombreador é uma técnica para aumentar a eficiência do sombreador através do pré-cálculo de expressões de sombreador constantes. O compilador de efeitos extrai automaticamente os cálculos de sombreador do corpo de um sombreador e os executa na CPU antes da execução do sombreador. Consequentemente, os preshaders funcionam apenas com efeitos. Por exemplo, estas duas expressões podem ser avaliadas fora do sombreador antes que ele seja executado.
mul(World,mul(View, Projection));
sin(time)
Os cálculos de sombreamento que podem ser movidos são aqueles associados a parâmetros uniformes; ou seja, os cálculos não mudam para cada vértice ou pixel. Se você estiver usando efeitos, o compilador de efeitos gera e executa automaticamente um pré-sombreador para você; não há bandeiras para habilitar. Os pré-sombreadores podem reduzir o número de instruções por sombreador e também podem reduzir o número de registros constantes que um sombreador consome.
Pense no compilador de efeitos como uma espécie de compilador multiprocessador porque ele compila código de sombreador para dois tipos de processadores: uma CPU e uma GPU. Além disso, o compilador de efeitos é projetado para mover o código da GPU para a CPU e, portanto, melhorar o desempenho do sombreador. Isso é muito semelhante a extrair uma expressão estática de um loop. Um sombreador que transforma a posição do espaço mundial para o espaço de projeção e copia as coordenadas de textura ficaria assim no HLSL:
float4x4 g_mWorldViewProjection; // World * View * Projection matrix
float4x4 g_mWorldInverse; // Inverse World matrix
float3 g_LightDir; // Light direction in world space
float4 g_LightDiffuse; // Diffuse color of the light
struct VS_OUTPUT
{
float4 Position : POSITION; // vertex position
float2 TextureUV : TEXCOORD0; // vertex texture coords
float4 Diffuse : COLOR0; // vertex diffuse color
};
VS_OUTPUT RenderSceneVS( float4 vPos : POSITION,
float3 vNormal : NORMAL,
float2 vTexCoord0 : TEXCOORD0)
{
VS_OUTPUT Output;
// Transform the position from object space to projection space
Output.Position = mul(vPos, g_mWorldViewProjection);
// Transform the light from world space to object space
float3 vLightObjectSpace = normalize(mul(g_LightDir, (float3x3)g_mWorldInverse));
// N dot L lighting
Output.Diffuse = max(0,dot(vNormal, vLightObjectSpace));
// Copy the texture coordinate
Output.TextureUV = vTexCoord0;
return Output;
}
technique RenderVS
{
pass P0
{
VertexShader = compile vs_1_1 RenderSceneVS();
}
}
Usando a ferramenta Effect-Compiler para compilar o sombreador para vs_1_1, são geradas as seguintes instruções de montagem:
technique RenderVS
{
pass P0
{
vertexshader =
asm {
//
// Generated by Microsoft (R) D3DX9 Shader Compiler 9.15.779.0000
//
// Parameters:
//
// float3 g_LightDir;
// float4x4 g_mWorldInverse;
// float4x4 g_mWorldViewProjection;
//
//
// Registers:
//
// Name Reg Size
// ---------------------- ----- ----
// g_mWorldViewProjection c0 4
// g_mWorldInverse c4 3
// g_LightDir c7 1
//
vs_1_1
def c8, 0, 0, 0, 0
dcl_position v0
dcl_normal v1
dcl_texcoord v2
mov r1.xyz, c7
dp3 r0.x, r1, c4
dp3 r0.y, r1, c5
dp3 r0.z, r1, c6
dp4 oPos.x, v0, c0
dp3 r1.x, r0, r0
dp4 oPos.y, v0, c1
rsq r0.w, r1.x
dp4 oPos.z, v0, c2
mul r0.xyz, r0, r0.w
dp4 oPos.w, v0, c3
dp3 r0.x, v1, r0
max oD0, r0.x, c8.x
mov oT0.xy, v2
// approximately 14 instruction slots used
};
//No embedded pixel shader
}
}
Isso consome aproximadamente 14 slots e consome 9 registros constantes. Com um pré-processador, o compilador move as expressões estáticas para fora do sombreador e para o pré-processador. O mesmo sombreador ficaria assim com um pré-sombreador:
technique RenderVS
{
pass P0
{
vertexshader =
asm {
//
// Generated by Microsoft (R) D3DX9 Shader Compiler 9.15.779.0000
//
// Parameters:
//
// float3 g_LightDir;
// float4x4 g_mWorldInverse;
//
//
// Registers:
//
// Name Reg Size
// --------------- ----- ----
// g_mWorldInverse c0 3
// g_LightDir c3 1
//
preshader
dot r0.x, c3.xyz, c0.xyz
dot r0.y, c3.xyz, c1.xyz
dot r0.z, c3.xyz, c2.xyz
dot r1.w, r0.xyz, r0.xyz
rsq r0.w, r1.w
mul c4.xyz, r0.w, r0.xyz
// approximately 6 instructions used
//
// Generated by Microsoft (R) D3DX9 Shader Compiler 9.15.779.0000
//
// Parameters:
//
// float4x4 g_mWorldViewProjection;
//
//
// Registers:
//
// Name Reg Size
// ---------------------- ----- ----
// g_mWorldViewProjection c0 4
//
vs_1_1
def c5, 0, 0, 0, 0
dcl_position v0
dcl_normal v1
dcl_texcoord v2
dp4 oPos.x, v0, c0
dp4 oPos.y, v0, c1
dp4 oPos.z, v0, c2
dp4 oPos.w, v0, c3
dp3 r0.x, v1, c4
max oD0, r0.x, c5.x
mov oT0.xy, v2
// approximately 7 instruction slots used
};
//No embedded pixel shader
}
}
Um efeito executa um pré-sombreador imediatamente antes de executar um sombreador. O resultado é a mesma funcionalidade com maior desempenho do sombreador porque o número de instruções que precisam ser executadas (para cada vértice ou pixel, dependendo do tipo de sombreador) foi reduzido. Além disso, menos registros constantes são consumidos pelo sombreador como resultado das expressões estáticas serem movidas para o pré-sombreador. Isso significa que sombreadores anteriormente limitados pelo número de registros constantes que exigiam agora podem compilar porque exigem menos registros constantes. É razoável esperar uma melhoria de desempenho de 5% e 20% dos pré-sombreadores.
Tenha em mente que as constantes de entrada são diferentes das constantes de saída em um pré-sombreador. A saída c1 não é a mesma que o registro c1 de entrada. Gravar num registo constante em um pré-sombreador de facto grava no espaço correspondente de entrada (constante) do sombreador.
// BaseDelta c0 1
// Refinements c1 1
preshader
mul c1.x, c0.x, (-2)
add c0.x, c0.x, c0.x
cmp c5.x, c1.x, (1), (0)
A instrução cmp acima lerá o valor c1 do preshader, enquanto a instrução mul gravará nos registos do shader de hardware para serem utilizados pelo sombreador de vértice.
Usar blocos de parâmetros para gerenciar parâmetros de efeito
Os blocos de parâmetros são blocos de alterações de estado de efeito. Um bloco de parâmetros pode registrar alterações de estado, para que elas possam ser aplicadas posteriormente com uma única chamada. Crie um bloco de parâmetros chamando ID3DXEffect::BeginParameterBlock:
m_pEffect->SetTechnique( "RenderScene" );
m_pEffect->BeginParameterBlock();
D3DXVECTOR4 v4( Diffuse.r, Diffuse.g, Diffuse.b, Diffuse.a );
m_pEffect->SetVector( "g_vDiffuse", &v4 );
m_pEffect->SetFloat( "g_fReflectivity", fReflectivity );
m_pEffect->SetFloat( "g_fAnimSpeed", fAnimSpeed );
m_pEffect->SetFloat( "g_fSizeMul", fSize );
m_hParameters = m_pEffect->EndParameterBlock();
O bloco de parâmetros salva quatro alterações aplicadas pelas chamadas de API. Chamando ID3DXEffect::BeginParameterBlock começa a gravar as alterações de estado. ID3DXEffect::EndParameterBlock deixa de acrescentar as alterações ao bloco de parâmetros e retorna um identificador. O identificador será usado ao chamar ID3DXEffect::ApplyParameterBlock.
No EffectParam Sample, o bloco de parâmetros é aplicado na sequência de renderização:
CObj g_aObj[NUM_OBJS]; // Object instances
if( SUCCEEDED( pd3dDevice->BeginScene() ) )
{
// Set the shared parameters using the first mesh's effect.
// Render the mesh objects
for( int i = 0; i < NUM_OBJS; ++i )
{
ID3DXEffect *pEffect = g_aObj[i].m_pEffect;
// Apply the parameters
pEffect->ApplyParameterBlock( g_aObj[i].m_hParameters );
...
pEffect->Begin( &cPasses, 0 );
for( iPass = 0; iPass < cPasses; iPass++ )
{
...
}
pEffect->End();
}
...
pd3dDevice->EndScene();
}
O bloco de parâmetros define o valor de todas as quatro alterações de estado imediatamente antes de ID3DXEffect::Begin ser chamado. Os blocos de parâmetros são uma maneira prática de definir várias alterações de estado com uma única chamada de API.
Tópicos relacionados