Writing an effect requires that you understand effect syntax, and generate the required state information. You can add shader state, texture and sampler state, and all of the pipeline state that your application requires in an effect.
The effect syntax is detailed in the Effect Format. An effect is typically encapsulated into an effect file (.fx) but could also be written as a text string in an application.
Effects contain three types of state: shader state, texture and sampler state, and pipeline state. Here is an example of an effect from the BasicHLSL Sample:
// Global variables float4 g_MaterialAmbientColor; // Material's ambient color float4 g_MaterialDiffuseColor; // Material's diffuse color int g_nNumLights; float3 g_LightDir[3]; // Light's direction in world space float4 g_LightDiffuse[3]; // Light's diffuse color float4 g_LightAmbient; // Light's ambient color texture g_MeshTexture; // Color texture for mesh float g_fTime; // App's time in seconds float4x4 g_mWorld; // World matrix for object float4x4 g_mWorldViewProjection; // World * View * Projection matrix // Texture samplers sampler MeshTextureSampler = sampler_state { Texture = <g_MeshTexture>; MipFilter = LINEAR; MinFilter = LINEAR; MagFilter = LINEAR; }; 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, uniform int nNumLights, uniform bool bTexture, uniform bool bAnimate ) { VS_OUTPUT Output; float3 vNormalWorldSpace; float4 vAnimatedPos = vPos; // Animation the vertex based on time and the vertex's object space position if( bAnimate ) vAnimatedPos += float4(vNormal, 0) * (sin(g_fTime+5.5)+0.5)*5; // Transform the position from object space to homogeneous projection space Output.Position = mul(vAnimatedPos, g_mWorldViewProjection); // Transform the normal from object space to world space vNormalWorldSpace = normalize(mul(vNormal, (float3x3)g_mWorld)); // Compute simple directional lighting equation float3 vTotalLightDiffuse = float3(0,0,0); for(int i=0; i < nNumLights; i++ ) vTotalLightDiffuse += g_LightDiffuse[i] * max(0,dot(vNormalWorldSpace, g_LightDir[i])); Output.Diffuse.rgb = g_MaterialDiffuseColor * vTotalLightDiffuse + g_MaterialAmbientColor * g_LightAmbient; Output.Diffuse.a = 1.0f; // Just copy the texture coordinate through if( bTexture ) Output.TextureUV = vTexCoord0; else Output.TextureUV = 0; return Output; } struct PS_OUTPUT { float4 RGBColor : COLOR0; // Pixel color }; PS_OUTPUT RenderScenePS( VS_OUTPUT In, uniform bool bTexture ) { PS_OUTPUT Output; // Lookup mesh texture and modulate it with diffuse if( bTexture ) Output.RGBColor = tex2D(MeshTextureSampler, In.TextureUV) * In.Diffuse; else Output.RGBColor = In.Diffuse; return Output; } technique RenderSceneWithTexture1Light { pass P0 { VertexShader = compile vs_1_1 RenderSceneVS( 1, true, true ); PixelShader = compile ps_1_1 RenderScenePS( true ); } }
This effect contains:
The shader state, which is all of the state associated with the vertex shader and pixel shader. This is defined by the vertex and pixel shader functions, any global variables they require, and their input and output data structures, all of which are listed here:
struct VS_OUTPUT { ... }; VS_OUTPUT RenderSceneVS( float4 vPos : POSITION, ... { ... } struct PS_OUTPUT { ... }; PS_OUTPUT RenderScenePS( VS_OUTPUT In, uniform bool bTexture ) { ... }
Texture and sampler state, which are the the global variables for the texture object and the sampler object:
texture g_MeshTexture; // Color texture for mesh sampler MeshTextureSampler = sampler_state { ... };
The other effect state. There is no other effect state explicitly used in this example. If there were, it would be the technique inside of the pass to which it applied:
technique RenderSceneWithTexture1Light { pass P0 { // Any other effect state can be set here. VertexShader = compile vs_1_1 RenderSceneVS( 1, true, true ); PixelShader = compile ps_1_1 RenderScenePS( true ); } }
Effect rendering options are controlled by adding techniques and passes.
Effects can be created with additional passes to facilitate more complex rendering effects. A technique supports an arbitrary number of passes:
technique T0 { pass P0 { ... } pass P1 { ... } ... pass Pn { ... } }
Effects can also be created with an arbitrary number of techniques.
technique T0 { pass P0 { ... } } technique T1 { pass P0 { ... } pass P1 { ... } } ... technique Tn { pass P0 { ... } } technique TVertexShaderOnly { pass P0 { VertexShader = asm { // assembly-language shader code goes here ... }; } }
The techniques and passes can be given arbitrary names.