//////////////////////////////////////////////
// Glow Shader:
// Glows are rendered as a separate pass
// to the rest of the scene. Glowing objects
// get rendered to a texture, the texture is
// blurred, then the texture is overlaid onto
// the main scene.
//////////////////////////////////////////////
texture glowTexture;
// texTrans is a transform used to map a 1x1 quad
// to fill the screen.
float4x4 texTrans : WorldProjection;
// pSize is the pixel size of the texture,
// equivalent to the inverse of the texture width.
float pSize;
sampler GlowSampler = sampler_state
{
Texture = (glowTexture);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
};
//Passed to the vertex shader from the pipeline
struct GLOW_INPUT
{
float4 pos : POSITION;
float2 texCoord : TEXCOORD;
};
//VS output / PS input:
struct GLOW_OUTPUT
{
float4 pos : POSITION;
float2 texCoord0 : TEXCOORD0;
float2 texCoord1 : TEXCOORD1;
float2 texCoord2 : TEXCOORD2;
float2 texCoord3 : TEXCOORD3;
};
struct TEXTURE_OUTPUT
{
float4 pos : POSITION;
float2 texCoord0 : TEXCOORD0;
};
//PS output:
struct pixel
{
float4 color : COLOR;
};
//////////////////////////
// Vertex Shaders:
//////////////////////////
// These glow vertex shaders blur the texture in
// specific direction - up, down, left, and right.
// They are used one after the other to obtain a full blur.
// VS1.3 hardware could do this in 2 passes rather than 4,
// but I only have a lowly Geforce4...
// They work by offsetting the texture coordinates into
// 4 texCoord streams. The pixel shader then reads the
// texture color at each of these texcoords and averages them
// together, effectively sampling a cluster of pixels.
GLOW_OUTPUT glowVSHorizontal1(GLOW_INPUT IN)
{
GLOW_OUTPUT OUT;
OUT.pos = mul(IN.pos, texTrans);
OUT.texCoord0 = IN.texCoord + float2(-pSize*3, 0);
OUT.texCoord1 = IN.texCoord + float2(-pSize*2, 0);
OUT.texCoord2 = IN.texCoord + float2(-pSize*1, 0);
OUT.texCoord3 = IN.texCoord + float2(0, 0 );
return OUT;
}
GLOW_OUTPUT glowVSHorizontal2(GLOW_INPUT IN)
{
GLOW_OUTPUT OUT;
OUT.pos = mul(IN.pos, texTrans);
OUT.texCoord0 = IN.texCoord + float2(pSize*3,0);
OUT.texCoord1 = IN.texCoord + float2(pSize*2, 0);
OUT.texCoord2 = IN.texCoord + float2(pSize*1, 0);
OUT.texCoord3 = IN.texCoord + float2(0, 0 );
return OUT;
}
GLOW_OUTPUT glowVSVertical1(GLOW_INPUT IN)
{
GLOW_OUTPUT OUT;
OUT.pos = mul(IN.pos, texTrans);
OUT.texCoord0 = IN.texCoord + float2(0,-pSize*3);
OUT.texCoord1 = IN.texCoord + float2(0,-pSize*2);
OUT.texCoord2 = IN.texCoord + float2(0,-pSize*1);
OUT.texCoord3 = IN.texCoord + float2(0,0);
return OUT;
}
GLOW_OUTPUT glowVSVertical2(GLOW_INPUT IN)
{
GLOW_OUTPUT OUT;
OUT.pos = mul(IN.pos, texTrans);
OUT.texCoord0 = IN.texCoord + float2(0,pSize*3);
OUT.texCoord1 = IN.texCoord + float2(0,pSize*2);
OUT.texCoord2 = IN.texCoord + float2(0,pSize*1);
OUT.texCoord3 = IN.texCoord + float2(0,0);
return OUT;
}
// This is the plain vertex shader used to overlay the
// final glow texture over the rest of the scene.
TEXTURE_OUTPUT outputGlowVS(GLOW_INPUT IN)
{
TEXTURE_OUTPUT OUT;
OUT.pos = mul(IN.pos, texTrans);
OUT.texCoord0 = IN.texCoord;
return OUT;
}
//////////////////////////
// Pixel Shaders:
//////////////////////////
// Add the texture values at each of the supplied texCoords
// together, weighted by some arbitary function that gives
// a reasonable appearance.
// These weights are critical to the glow behaviour,
// and tiny changes in the values can suddenly make the glow
// invisible or overpowering. If anyone knows how to make this
// better, please let me know...
pixel glowPS(GLOW_OUTPUT IN)
{
pixel OUT;
float4 color = tex2D( GlowSampler, IN.texCoord0 ) * 0.1;
color += tex2D( GlowSampler, IN.texCoord1 ) * 0.3;
color += tex2D( GlowSampler, IN.texCoord2 ) * 0.4;
color += tex2D( GlowSampler, IN.texCoord3 ) * 0.25;
OUT.color = color;
OUT.color.a = 1.0f;
return OUT;
}
// This is the pixel shader used to overlay the final glow image
// onto the rest of the scene.
pixel outputGlowPS(TEXTURE_OUTPUT IN)
{
pixel OUT;
OUT.color = tex2D( GlowSampler, IN.texCoord0 );
return OUT;
}
//////////////////////////
// Techniques:
//////////////////////////
// Four passes blur the texture in different directions.
// The final one overlays the texture onto the rest of
// the scene.
// Annotations are used so my application can automatically
// sort these passes into the appropriate rendering stage.
technique T0
{
pass P0 <string renderStage="texture";>
{
Sampler[0] = (GlowSampler);
vertexshader = compile vs_1_1 glowVSHorizontal1();
pixelshader = compile ps_1_1 glowPS();
fvf = XYZ | Tex1;
}
pass P1 <string renderStage="texture";>
{
Sampler[0] = (GlowSampler);
vertexshader = compile vs_1_1 glowVSVertical1();
pixelshader = compile ps_1_1 glowPS();
fvf = XYZ | Tex1;
}
pass P2 <string renderStage="texture";>
{
Sampler[0] = (GlowSampler);
vertexshader = compile vs_1_1 glowVSHorizontal2();
pixelshader = compile ps_1_1 glowPS();
fvf = XYZ | Tex1;
}
pass P3 <string renderStage="texture";>
{
Sampler[0] = (GlowSampler);
vertexshader = compile vs_1_1 glowVSVertical2();
pixelshader = compile ps_1_1 glowPS();
fvf = XYZ | Tex1;
}
pass P4 <string renderStage="post";>
{
Sampler[0] = (GlowSampler);
vertexshader = compile vs_1_1 outputGlowVS();
pixelshader = compile ps_1_1 outputGlowPS();
fvf = XYZ | Tex1;
AlphaBlendEnable = true;
BlendOp = Min;
SrcBlend = One;
DestBlend = One;
}
}