////////////////////////////////////////////// // 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; } }