XNA 4 tutorial: Environment Mapping and Cubemaps

Recently I stumbled across an article from Shawn Hargreaves about Cubemaps and Environment Mapping, which is used to reflect the environment or any picture on an object’s surface. I like the idea generating Cubemaps from a 2D texture, because its a simple, quick, but non realistic solution. In the Reach Graphics Demo on XNAHub a 3D model is loaded through a custom Model Processor, which converts a 2D texture to a Cubemap and assigns it to the also converted Effect Class of the loaded 3D model.

Cubemap Texture Processor

Until now the Effect Class contains all textures in the simple game engine. Please have a look at the dependencies in this diagram:
[singlepic id=2129 float=none]

But there is one problem passing a Cubemap to an Effect class: a Cubemap (dds) file is needed. In the Reach Graphics Demo the Cubemap is generated at runtime while the Content Pipeline is loading the 3D model. There is no need and no output for a Cubemap (dds) file.
[nggallery id=302]

Fortunately only a few changes are needed to make the code from the Reach Graphics Demo work with the simple game engine. Instead of a custom Model Processor a custom Texture Processor is required in order to load a 2D texture, convert it to a Cubemap and assign it to an Effect class. This is also a good time getting more familiar with the XNA Content Pipeline. :-)
Note: Every time a new 2D texture file is used as a Cubemap, its Content Processor must be changed in the file properties within Visual Studio.
Note: This Cubemap Texture Processor also supports the Alpha Texture Processor from the Reach Graphics Demo for drawing multiple shiny light spots on a surface without great performance penaltities. A second texture file with a „_alpha“ suffix is required.

/// Custom content pipeline processor derives from the built-in
/// MaterialProcessor. This changes the material to use our custom
/// environment mapping effect, and also builds the environment map
/// texture in a special way.
/// The XNA Content project must have a reference to this project.
[ContentProcessor(DisplayName = "Texture – Environment Cubemap Processor")]
public class EnvironmentMappedMaterialProcessor : TextureProcessor
{
    /// Builds a texture for use by this material.
    public override TextureContent Process(TextureContent input, ContentProcessorContext context)
    {
        var alphaInput = context.Convert(input, "TexturePlusAlphaProcessor");
        return CubemapGenerator.Process(alphaInput, context);
    }
}
    
class SphereEntityComponent : ModelEntityComponent
{
    public SphereEntityComponent(Game game, ICamera camera)
        : base(game, camera)
    {
    }

    protected override void LoadContent()
    {
        var content = Game.Content;

        Model = content.Load("models/Sphere");

        // Load effect
        var environmentMapEffect = content.LoadDerivedEffect();
        // The Content Processor in the texture file properties must be set from the default 
        // "Texture - XNA Framework" to "Texture – Environment Cubemap Processor".
        environmentMapEffect.EnvironmentMap = content.Load("textures/environment/Kitchen");
        environmentMapEffect.TextureEnabled = false;
        environmentMapEffect.FresnelFactor = 0.6f;
        Effect = environmentMapEffect;

        base.LoadContent();
    }

    public override void Draw(GameTime gameTime)
    {
        base.Draw(gameTime);
    }
}

Cubemap Generator Tool

Unfortunalely the XNA Content Pipeline outputs only data in xnb format, as Shawn Hargreaves stated in his comments section. So it is not possible to save the generated Cubemap into a dds file from the XNA Content Pipeline. He suggested though writing a own system for loading and saving data.
After rewriting the code from the Reach Graphics Demo, it outputs the six Cubemap faces as image files. So far so good, but the real problem starts now generating a Cubemap (dds) file from these six Cubemap faces, because the TextureCube class in XNA 4 doesn’t support saving its data to a file any more. :-(
There are some, mostly outdated, Cubemap tools on the internet, which can do the job as well. For example the tool DxTex located in the Microsoft DirectX SDK. DxTex takes six images and generates a Cubemap (dds) file. The sticking point is the graphical usability of DxTex – this tool cannot generate a Cubemap (dds) file via the command line. And clicking each Cubemap together by hand is neither a fast nor a handy solution…

[singlepic id=2125 w=400 float=right] Finally there is a DDS Generator class written by Popescu Alexandru-Cristian:
„In XNA 4.0 the Texture.FromFile() and Texture.Save() methods where removed letting us with no option on directly loading or saving dds files in our applications, we can load dds files trough the content pipeline but for an editor or for any application/game where you can create or even add content dynamically this is simply not an option.“
This code implements the dds file format being the last missing piece needed to generate a Cubemap file from the six face images and finishing the Cubemap Generator Tool.
The following code snippet finally shows how to load the generated Cubemap dds file into any XNA project:

var environmentMapEffect = new EnvironmentMapEffect(GraphicsDevice);
environmentMapEffect.EnvironmentMap = content.Load("textures/environment/Kitchen.dds");

Conclusion

Generating Cubemaps from a 2D texture is a simple, quick, but non realistic solution. A more realistic solution would be taking six camera shots from the same spot within an existing level and save them to a Cubemap dds file. The most realistic but also less-performance solution would be taking the six camera shots at runtime, so that an 3D model always reflects its actually environment.

The XNA Content Pipeline is a great system for loading and processing external content. Since every content file has to be processed before the game application starts, it is not possible adding content dynamically to a game or a game editor: a restart of the game will be required. Anyhow the Cubemap Generator Tool could be rewritten to enable dynamically content loading.

Resources

Download tutorial solution (source code)
Download cubemap generator tool (binary and source code)


Beitrag veröffentlicht

in

von