Effects System

Microsoft::Xna::Framework::Graphics effects - stock effects, interfaces, custom shaders

Implementation status: All five XNA 4.0 stock effects — BasicEffect, AlphaTestEffect, DualTextureEffect, EnvironmentMapEffect and SkinnedEffect — are implemented and pixel-tested on all three 3D backends (EasyGL, Vulkan and bgfx). Two known gaps remain: EnvironmentMapEffect and SkinnedEffect only forward DirectionalLight0 to the GPU (lights 1 and 2 are dropped), and SkinnedEffect has no specular term. Custom shaders are a different story — CNA cannot load compiled .fx bytecode, so they must be hand-written; see Shader Effects.

Overview

CNA implements the full XNA 4.0 effects pipeline inside the Microsoft::Xna::Framework::Graphics namespace. An effect is a shader program combined with its parameter set and render state. You apply an effect by setting its parameters, then calling effect.CurrentTechnique.Passes[0].Apply() before drawing primitives.

All stock effects implement one or more effect interfaces (IEffectMatrices, IEffectFog, IEffectLights) so that higher-level drawing code can treat effects polymorphically. Parameters can also be accessed directly through EffectParameter objects exposed on the effect.

Custom shaders are supported via the ShaderEffect class, which accepts user-supplied GLSL source (EasyGL backend) or SPIR-V bytecode (Vulkan backend), loaded through the ContentManager pipeline using a .shader.json descriptor.

Stock effects at a glance

Effect Key capability Implements Status
BasicEffect Ambient/diffuse/specular lighting, up to 3 directional lights, texture mapping, fog IEffectMatrices, IEffectFog, IEffectLights Implemented
AlphaTestEffect Alpha threshold discard (greater/less/equal/etc.), single texture IEffectMatrices, IEffectFog Implemented
DualTextureEffect Blends two textures with fog IEffectMatrices, IEffectFog Implemented
EnvironmentMapEffect Cube-map environment reflection and Fresnel, fog IEffectMatrices, IEffectFog, IEffectLights Implemented
SkinnedEffect Skeletal animation, up to 72 bones via UBO, 1–4 skinning weights, fog IEffectMatrices, IEffectFog, IEffectLights Implemented
SpriteEffect Internal 2D effect used by SpriteBatch; not typically used directly IEffectMatrices Implemented

BasicEffect

BasicEffect is the general-purpose 3D effect. It supports per-vertex and per-pixel lighting with up to three independent directional lights, a single diffuse texture, per-vertex color, and distance fog. Lighting is controlled via the IEffectLights interface — call EnableDefaultLighting() to activate a standard three-point light rig in a single call.

// C++23 example
auto effect = std::make_shared<BasicEffect>(graphicsDevice);
effect->SetWorld(Matrix::Identity);
effect->SetView(camera.View());
effect->SetProjection(camera.Projection());
effect->EnableDefaultLighting();
effect->SetTextureEnabled(true);
effect->SetTexture(myTexture);

for (auto& pass : effect->CurrentTechnique->Passes) {
    pass->Apply();
    graphicsDevice->DrawIndexedPrimitives(
        PrimitiveType::TriangleList, 0, 0, indexCount / 3);
}

AlphaTestEffect

AlphaTestEffect renders a single textured surface and discards fragments whose alpha does not satisfy a configurable comparison against a reference value. Supported comparisons are Always, Never, Equal, NotEqual, Less, LessEqual, Greater, and GreaterEqual. This effect is useful for vegetation, fences, and other cut-out geometry where alpha blending would cause sorting problems.

auto effect = std::make_shared<AlphaTestEffect>(graphicsDevice);
effect->SetWorld(transform);
effect->SetView(camera.View());
effect->SetProjection(camera.Projection());
effect->SetTexture(treeBarkTexture);
effect->SetAlphaFunction(CompareFunction::Greater);
effect->SetReferenceAlpha(128);  // discard alpha <= 0.5
effect->CurrentTechnique->Passes[0]->Apply();

DualTextureEffect

DualTextureEffect samples two textures in the same draw call and blends them together. A common use case is lightmap baking: the first texture holds the base colour and the second holds a pre-baked lightmap. Both textures share the same set of texture coordinates unless the shader variant with independent UV channels is selected.

auto effect = std::make_shared<DualTextureEffect>(graphicsDevice);
effect->SetWorld(Matrix::Identity);
effect->SetView(camera.View());
effect->SetProjection(camera.Projection());
effect->SetTexture(diffuseTexture);
effect->SetTexture2(lightmapTexture);
effect->CurrentTechnique->Passes[0]->Apply();

EnvironmentMapEffect

EnvironmentMapEffect adds a cube-map environment reflection on top of a diffuse texture. The amount of reflection versus base colour is controlled by the environment map amount and an optional Fresnel factor, which makes reflections stronger at glancing angles. A single directional light contributes specular highlights in addition to the environment map. Fog is also supported.

auto effect = std::make_shared<EnvironmentMapEffect>(graphicsDevice);
effect->SetWorld(sphereTransform);
effect->SetView(camera.View());
effect->SetProjection(camera.Projection());
effect->SetTexture(diffuseTexture);
effect->SetEnvironmentMap(skyboxCube);
effect->SetEnvironmentMapAmount(0.8f);
effect->SetFresnelFactor(1.0f);
effect->CurrentTechnique->Passes[0]->Apply();

SkinnedEffect

SkinnedEffect is the standard effect for skeletal (skinned) mesh animation. Bone matrices are uploaded to a Uniform Buffer Object (UBO), supporting up to 72 bones per draw call. Each vertex can reference 1, 2, or 4 bones through the WeightsPerVertex property. The effect also supports the full lighting model (three directional lights) and fog.

auto effect = std::make_shared<SkinnedEffect>(graphicsDevice);
effect->SetWorld(Matrix::Identity);
effect->SetView(camera.View());
effect->SetProjection(camera.Projection());
effect->EnableDefaultLighting();
effect->SetWeightsPerVertex(4);
effect->SetBoneTransforms(boneMatrices);   // std::span<Matrix>, max 72
effect->CurrentTechnique->Passes[0]->Apply();

SpriteEffect

SpriteEffect is an internal 2D effect consumed by SpriteBatch. It sets up an orthographic projection over the back-buffer dimensions and routes through a simple textured-quad shader. Application code does not normally instantiate SpriteEffect directly — SpriteBatch manages it internally, but it is exposed in the public API for completeness and for XNA compatibility.

Backend parity

The table below shows which effect is available on each rendering backend. pixel-tested means rendered output has been verified; compiles/links means the code is present but no pixel-level test has been run yet; not implemented means the effect is absent from that backend; limited 2D only means only the 2D SpriteBatch path works.

Effect EasyGL Vulkan bgfx SDL_Renderer
BasicEffect pixel-tested compiles/links compiles/links limited 2D only
AlphaTestEffect pixel-tested compiles/links compiles/links
DualTextureEffect pixel-tested pixel-tested compiles/links
EnvironmentMapEffect pixel-tested pixel-tested not implemented
SkinnedEffect pixel-tested compiles/links compiles/links
ShaderEffect (custom GLSL/SPIR-V) pixel-tested pixel-tested not implemented

Effect interfaces

IEffectMatrices

Implemented by every stock 3D effect. Provides the three standard transformation matrices used to project geometry from object space to clip space.

PropertyTypeDescription
WorldMatrixObject-to-world transform (model matrix)
ViewMatrixWorld-to-camera transform
ProjectionMatrixCamera-to-clip transform (perspective or orthographic)

IEffectFog

Implemented by all stock effects that render geometry in world space. Distance fog is blended linearly between FogStart and FogEnd. Set FogEnabled = false to disable the computation entirely.

PropertyTypeDescription
FogEnabledboolEnables or disables distance fog
FogStartfloatCamera distance at which fog begins
FogEndfloatCamera distance at which fog is fully opaque
FogColorVector3RGB colour of the fog

IEffectLights

Implemented by BasicEffect, EnvironmentMapEffect, and SkinnedEffect. Exposes an ambient light and three independent directional lights. Call EnableDefaultLighting() to configure a standard three-point lighting setup in one call.

MemberTypeDescription
AmbientLightColorVector3RGB colour of the scene ambient light
DirectionalLight0DirectionalLightFirst directional light
DirectionalLight1DirectionalLightSecond directional light
DirectionalLight2DirectionalLightThird directional light
EnableDefaultLighting()voidConfigures a standard three-point lighting rig

Each DirectionalLight exposes:

  • DirectionVector3 pointing toward the light source (normalized)
  • DiffuseColorVector3 RGB diffuse contribution
  • SpecularColorVector3 RGB specular contribution
  • Enabledbool enables or disables this light

EffectParameter

All effect parameters are accessible through the Effect::Parameters collection as EffectParameter objects. Parameters can be read and written using typed GetValue<T>() and SetValue() overloads. This mirrors the XNA 4.0 API exactly.

Supported types for Get/Set:

  • float — scalar floating-point value
  • Vector2, Vector3, Vector4 — float vectors
  • Matrix — 4×4 column-major matrix
  • Matrix[] — array of matrices (used by SkinnedEffect bone palette)
  • Texture2D — 2D texture sampler binding
  • bool — boolean flag
// Look up a parameter by name and set it directly
auto param = effect->Parameters["DiffuseColor"];
param->SetValue(Vector4(1.0f, 0.5f, 0.0f, 1.0f));

// Read back a matrix
Matrix world = effect->Parameters["World"]->GetValue<Matrix>();

Prefer the typed properties on stock effects (e.g. effect->SetWorld(...)) over raw EffectParameter access where possible. Typed setters validate input and update dependent cached state such as the combined WorldViewProjection matrix.

Custom ShaderEffect

When the stock effects do not meet your needs, ShaderEffect lets you supply your own shader source. The shader is loaded through the ContentManager using a .shader.json descriptor that references the shader source files.

Backend requirement: Custom shaders use GLSL source on the EasyGL backend and SPIR-V bytecode on the Vulkan backend. The bgfx and SDL_Renderer backends do not currently support custom ShaderEffect.

Example .shader.json descriptor:

{
  "vertex":   "shaders/my_effect.vert.glsl",
  "fragment": "shaders/my_effect.frag.glsl"
}

Loading and using a custom effect:

// Load via ContentManager
auto myEffect = content.Load<ShaderEffect>("shaders/my_effect");

// Set parameters by name
myEffect->Parameters["WorldViewProj"]->SetValue(wvp);
myEffect->Parameters["Tint"]->SetValue(Vector4(1, 0.8f, 0.6f, 1));

// Apply and draw
myEffect->CurrentTechnique->Passes[0]->Apply();
graphicsDevice->DrawPrimitives(PrimitiveType::TriangleList, 0, triangleCount);

For the Vulkan backend, replace the GLSL source references in the descriptor with pre-compiled SPIR-V files (.spv). The push constant block is limited to 128 bytes, and uniform buffer layout follows std140 rules, matching standard Vulkan conventions.