I'm using effect annotations to declaratively define render target textures in my shaders. The engine code parses these annotations when loading the effects and creates textures dynamically that are feed back to the shader through pEffect->SetTexture().
If you look at my blur filter you will find these annotations as RENDERCOLORTARGET in the code below.
texture g_inputTexture; // Input texture to the filter
float2 outputTextureSize : VIEWPORTPIXELSIZE;
texture g_pass2Texture : RENDERCOLORTARGET
<
float2 ViewportRatio = { 1.0, 1.0 };
>;
texture g_pass3Texture : RENDERCOLORTARGET
<
float2 ViewportRatio = { 1.0, 1.0 };
>;
float Spread <
string UIName = "Spread";
> = 0.1f;
// Vertex shader /////////////////////////////////////////
struct AppData {
float3 Position : POSITION;
float2 UV : TEXCOORD0;
};
struct VertData {
float4 HPosition : POSITION;
float2 UV : TEXCOORD0;
float2 PixelDelta : TEXCOORD1;
};
VertData VS_Common(AppData IN)
{
VertData OUT;
OUT.UV = IN.UV.xy;
OUT.HPosition = float4(IN.Position.x, IN.Position.y, 0, 1.0);
float dx = 1.0 / outputTextureSize.x;
float dy = 1.0 / outputTextureSize.y;
OUT.PixelDelta = float2(dx, dy);
return OUT;
}
// Pixel shader /////////////////////////////////////////
sampler Sampler1 = sampler_state
{
Texture = (g_inputTexture);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
BorderColor = float4(0, 0, 0, 0);
AddressU = CLAMP;
AddressV = CLAMP;
};
sampler Sampler2 = sampler_state
{
Texture = (g_pass2Texture);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
BorderColor = float4(0, 0, 0, 0);
AddressU = CLAMP;
AddressV = CLAMP;
};
sampler Sampler3 = sampler_state
{
Texture = (g_pass3Texture);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
BorderColor = float4(0, 0, 0, 0);
AddressU = CLAMP;
AddressV = CLAMP;
};
static const int g_cKernelSize = 13;
static const float BlurWeights[g_cKernelSize] =
{
0.002216,
0.008764,
0.026995,
0.064759,
0.120985,
0.176033,
0.199471,
0.176033,
0.120985,
0.064759,
0.026995,
0.008764,
0.002216,
};
float4 PS_HBlur(uniform sampler Sampler,
uniform float SpreadMultiplier,
float2 uv : TEXCOORD0,
float2 pixelDelta : TEXCOORD1) : COLOR
{
float4 Color = 0;
float SpreadAmount = Spread * SpreadMultiplier;
for (int i = 0; i < g_cKernelSize; i++) {
Color += tex2D(Sampler, uv + float2(-SpreadAmount*pixelDelta.x*(i-6), 0)) * BlurWeights[i];
}
return Color;
}
float4 PS_VBlur(uniform sampler Sampler,
uniform float SpreadMultiplier,
float2 uv : TEXCOORD0,
float2 pixelDelta : TEXCOORD1) : COLOR
{
float4 color = 0;
float SpreadAmount = Spread * SpreadMultiplier;
for (int i = 0; i < g_cKernelSize; i++) {
color += tex2D(Sampler, uv + float2(0, -SpreadAmount*pixelDelta.y*(i-6))) * BlurWeights[i];
}
return color;
}
// Technique /////////////////////////////////////////
technique FilterTechnique
{
pass HBlur1 <
string RenderTarget0 = "g_pass2Texture";
>
{
// Setup render states
ZWriteEnable = FALSE;
ZEnable = FALSE;
AlphaBlendEnable = FALSE;
// Shaders
VertexShader = compile vs_1_1 VS_Common();
PixelShader = compile ps_2_0 PS_HBlur(Sampler1, 8);
}
pass VBlur1 <
string RenderTarget0 = "g_pass3Texture";
>
{
// Setup render states
ZWriteEnable = FALSE;
ZEnable = FALSE;
AlphaBlendEnable = FALSE;
// Shaders
VertexShader = compile vs_1_1 VS_Common();
PixelShader = compile ps_2_0 PS_VBlur(Sampler2, 8);
}
pass HBlur2 <
string RenderTarget0 = "g_pass2Texture";
>
{
// Setup render states
ZWriteEnable = FALSE;
ZEnable = FALSE;
AlphaBlendEnable = FALSE;
// Shaders
VertexShader = compile vs_1_1 VS_Common();
PixelShader = compile ps_2_0 PS_HBlur(Sampler3, 4);
}
pass VBlur2 <
string RenderTarget0 = "g_pass3Texture";
>
{
// Setup render states
ZWriteEnable = FALSE;
ZEnable = FALSE;
AlphaBlendEnable = FALSE;
// Shaders
VertexShader = compile vs_1_1 VS_Common();
PixelShader = compile ps_2_0 PS_VBlur(Sampler2, 4);
}
pass HBlur3 <
string RenderTarget0 = "g_pass2Texture";
>
{
// Setup render states
ZWriteEnable = FALSE;
ZEnable = FALSE;
AlphaBlendEnable = FALSE;
// Shaders
VertexShader = compile vs_1_1 VS_Common();
PixelShader = compile ps_2_0 PS_HBlur(Sampler3, 2);
}
pass VBlur3 <
string RenderTarget0 = "g_pass3Texture";
>
{
// Setup render states
ZWriteEnable = FALSE;
ZEnable = FALSE;
AlphaBlendEnable = FALSE;
// Shaders
VertexShader = compile vs_1_1 VS_Common();
PixelShader = compile ps_2_0 PS_VBlur(Sampler2, 2);
}
pass HBlur4 <
string RenderTarget0 = "g_pass2Texture";
>
{
// Setup render states
ZWriteEnable = FALSE;
ZEnable = FALSE;
AlphaBlendEnable = FALSE;
// Shaders
VertexShader = compile vs_1_1 VS_Common();
PixelShader = compile ps_2_0 PS_HBlur(Sampler3, 1);
}
pass VBlur4 <
string RenderTarget0 = "";
>
{
// Setup render states
ZWriteEnable = FALSE;
ZEnable = FALSE;
AlphaBlendEnable = FALSE;
// Shaders
VertexShader = compile vs_1_1 VS_Common();
PixelShader = compile ps_2_0 PS_VBlur(Sampler2, 1);
}
}
The ViewportRatio parameter I use as a scaling parameter when creating the textures - for effects that for example want lower resolution intermediate textures.
What you are looking for is the enumeration of effect annotations in code similar to this:
void GXEffectFilter::ParseEffectAnnotations(LPD3DXEFFECT pEffect)
{
// Parse parameter annotations
D3DXHANDLE paramHandle;
D3DXPARAMETER_DESC paramDesc;
unsigned int index = 0;
while ((paramHandle = pEffect->GetParameter(NULL, index)) != NULL) {
if (pEffect->GetParameterDesc(paramHandle, ¶mDesc) == S_OK) {
// Iterate through all associated annotations
FLOAT viewportRatio[2] = {1, 1};
for (unsigned int a = 0; a < paramDesc.Annotations; a++) {
D3DXHANDLE hAnnot = pEffect->GetAnnotation(paramHandle, a);
// Get annotation description
D3DXPARAMETER_DESC annotDesc;
if (pEffect->GetParameterDesc(hAnnot, &annotDesc) == S_OK) {
if (_stricmp(annotDesc.Name, "ViewportRatio") == 0) {
pEffect->GetFloatArray(hAnnot, viewportRatio, 2);
}
[...]
}
}
// Handle creation of textures
if (paramDesc.Type == D3DXPT_TEXTURE &&
paramDesc.Semantic != NULL && _stricmp(paramDesc.Semantic, "RENDERCOLORTARGET") == 0)
{
RegisterInternalTexture(index, viewportRatio);
}
[...]
}
index++;
}
}
Then handle the creation of the dynamic texture in the RegisterInternalTexture() call and set the created texture back to the shader by calling
pEffect->SetTexture(pEffect->GetParameter(NULL, parameterIndex), pTexture);
When you later is about the render each pass in your shader you will have to parse the Pass annotations and find "RenderTarget0" and use pD3DDevice->SetRenderTarget() with the texture you have defined in your shader pass.