Stock Effects
Stock effects provides source code for the five effects (BasicEffect, SkinnedEffect, EnvironmentMapEffect, DualTextureEffect, and AlphaTestEffect), and the default shader used by SpriteBatch (SpriteEffect), built into the XNA Framework. There also is a command-line utility (CompileEffect) that uses the Content Pipeline to compile a .fx source file into a binary blob that can be passed directly to the XNA Framework Effect class constructor.
Overview
This code is provided for educational purposes. It may be a useful starting point when you create more advanced shaders.
Although the built-in effect classes are simple and easy to use, the shader code behind them is complex because these effects support many rendering options within a single shader. BasicEffect, for example, allows you to toggle on or off texturing, vertex colors, and lighting, and to select per-vertex or per-pixel lighting. To support all possible permutations of these options, BasicEffect ends up needing no less than 20 different vertex shaders and 10 pixel shaders, all of which are combined within a single effect file.
If you want an easy starting point to learn shader programming, use the Shader Series samples. The shader samples are simpler to use than the effects described here because they do not include so many adjustable options.
Using Effects
To use these stock effects instead of those built-in, follow these steps:
Compile the appropriate solution—StockEffectsWindows.sln or StockEffectsXbox.sln (depending on which platform you are developing for).
Right-click your project, select Add Reference, and then select the Browse tab.
Select StockEffects\bin\〈platform〉\Debug\StockEffects.dll.
This assembly provides six classes:
- BasicEffect
- SkinnedEffect
- EnvironmentMapEffect
- DualTextureEffect
- AlphaTestEffect
- SpriteEffect
But these new versions are found in the StockEffects namespace, as opposed to Microsoft.Xna.Framework.Graphics. To use these versions instead of the built-in classes, simply change:
effect = new BasicEffect(GraphicsDevice);
to
effect = new StockEffects.BasicEffect(GraphicsDevice);
Changing Effecs
To tweak the shader code, edit the .fx files, and then recompile the StockEffects.dll assembly. If you want to make more extensive changes, or to reuse these shaders in your own projects, you may need to understand how the code is compiled.
The CompileEffect project is a command-line utility that uses the Content Pipeline to compile a .fx file into a binary blob. It does this by calling directly into the EffectImporter and EffectProcessor classes, and then saves the resulting compiled effect code, bypassing the part of the Content Pipeline that normally would embed this data into an .xnb format file.
The StockEffects project has a custom build rule that uses the CompileEffect utility to compile all six effects. To edit this rule, follow these steps:
In Solution Explorer, right-click the StockEffects project.
Select Properties, click the Build Events tab, and then click the Edit Pre-build button.
It may be necessary to resize the dialog box to avoid word wrapping.
You will see that this build rule calls CompileEffect six times, building each .fx file into a .bin file, which is stored in the obj folder.
If you edit Resources.resx, you will see that it includes all six .bin files from the obj folder, so the compiled shader code is embedded directly as an assembly resource. This allows effect wrapper classes such as BasicEffect.cs to access the compiled effect code via Resources.BasicEffect.
If you want to add or remove shaders, you must:
- Add or remove the line that compiles the shader from the custom build rule
- Add or remove the shader output .bin file from Resources.resx
Effect Include Files
The XNA TouchPanel API provides only a polling interface. Your game can query to find the current touch status, but cannot sign up for notification of touch begin/end events. This presents some difficulty with this sample, because the player's point of initial contact with the screen defines the center of the thumbstick. If this point is lost because a frame or two is dropped, the center will not be where the player expects it, and the controls will not feel correct.
Fortunately, the XNA TouchPanel API provides a partial solution by providing a TryGetPreviousLocation method on TouchLocation, which allows one event's worth of history to be kept. As long as the game maintains a good frame rate, this is sufficient to keep the controls responsive.
Extending the Sample
Many of the built-in effects have similar behaviors. All, for example, use the same fog computations, while BasicEffect, SkinnedEffect, and EnvironmentMapEffect all share the same Blinn/Phong lighting model. To reduce code duplication, this shared functionality is implemented in header files (Common.fxh, Lighting.fxh, and Structures.fxh), which are included by all effects.
Macros.fxh serves a different purpose. This header file defines some helper macros that allow the same shader source code to be compiled for DirectX 9 Shader Model 2.0 (as used by the XNA Framework Content Pipeline) or alternatively for Shader Model 4.0 (which is used by DirectX 10 and DirectX 11, and has a slightly different syntax).
Preshaders
A common tension in shader programming is that when you design effect parameters to provide a nice clean API, the resulting parameter formats are not always the most efficient for high-level shader language (HLSL) optimization.
D3D tries to correct any such mismatches through a feature called "preshaders." The HLSL compiler looks for computations that are the same for all vertices or all pixels, and moves these out of the main shader into a special setup pass that runs on the CPU before drawing begins. This is a great feature, but has some fatal flaws:
- The HLSL compiler does not always spot every optimization possibility.
- The virtual machine that evaluates preshaders is not especially efficient.
- Preshaders are not supported on Xbox 360 or Windows Phone.
Instead, Game Studio implements preshader computations in C# by overloading the Effect.OnApply method, which is called immediately before EffectPass.Apply sets parameter values onto the graphics device.
This allows our C# effect wrapper classes to expose whatever properties the API requires, without needing these to match the underlying HLSL shader parameters. When the programmer changes a managed property, we just set a dirty flag, and then recompute derived HLSL parameter values during OnApply. We used this ability to precompute many things:
- Collapse the World, View, and Projection matrices into a single WorldViewProj matrix.
- When lighting is enabled, compute the WorldInverseTranspose matrix. This is necessary for correct normal transforms when using non-uniform scales, but something not done correctly in previous versions.
- Extract the EyePosition vector from the View matrix.
- Combine FogStart, FogEnd, World, and View to generate a vector that can compute fog amount with a single dot product.
- Merge DiffuseColor, EmissiveColor, AmbientLightColor, and Alpha properties into a more efficient set of combined parameters.