A screenshot. Look how the knot is refracted in the foreground object.
A screenshot. Look how the knot is refracted in the foreground object.
A screenshot for comparison. Notice how the refraction has changed.
A screenshot for comparison. Notice how the refraction has changed.

Dynamic Cubemapping is the process of creating a cubemap at runtime, that is, while the game is actually running (in contrast to static/precomputed cubemapping). It is realisable on most of today's graphics cards and is widely used especially in racing games. What you need to do is the following...

To see the demo on YouTube, click here

How is Dynamic Cubemapping achieved?

  • First, test GPU capabilities.
  • Then, create a new camera at the object's center you want to apply the cubemap to. Set the FOV to 90 degrees, the aspect ratio to 1 and fix the yaw axis. A near clip distance of 5 is recommended.
  • Create a cubical, dynamic texture.
  • Because a cubemap generally consists of 6 individual faces, you need to create exactly 6 render targets, so one for each face.
  • After that, assign the camera created in the first step to each of the render targets. You will get a new viewport. Set the 'OverlaysEnabled' property to 'false'.
  • Subscribe to the 'PreRenderTargetUpdate' and 'PostRenderTargetUpdate' signals and set 'IsAutoUpdated' appropriately.
  • Optionally subscribe to Root's 'FrameStarted' and 'FrameEnded' signals to be able to hide entities you don't want to be shown in the cubemap.
  • Now we are ready to actually render the cubemap. Hide the entities you want to be hidden. Set the camera's orientation to 'Mogre.Quaternion.IDENTITY'. Yaw and pitch the camera appropriately so that it 'takes shots' of the surroundings.
  • Create a material based on the texture created in step three. You may also do this in a material file if you want to.
  • Assign the newly created material to the object where it belongs to.
  • Have fun watching your dynamic cubemap being created every frame. You may also recreate it every 5 frames or so to reduce computation costs. This technique is demonstrated in the 'DynamicCubeMapper' class below.

The DynamicCubeMapper class

using System;

namespace Mogre.DynamicCubeMapping
{
    class DynamicCubeMapper
    {
        private readonly Camera _cubeCamera;
        private readonly RenderTarget[] _targets = new RenderTarget[6];
        private readonly Entity[] _entitiesToExclude;

        public DynamicCubeMapper(SceneManager sceneManager, params Entity[] entitiesToExclude)
        {
            if (sceneManager == null) throw new ArgumentNullException("sceneManager");

            TestGpuCapabilities(Root.Singleton.RenderSystem.Capabilities);

            _entitiesToExclude = entitiesToExclude;

            // Create the camera used to render to our cubemap
            _cubeCamera = sceneManager.CreateCamera("CubeMappingCamera");
            _cubeCamera.FOVy = new Degree(90);
            _cubeCamera.AspectRatio = 1;
            _cubeCamera.SetFixedYawAxis(false);
            _cubeCamera.NearClipDistance = 5;

            // Create our dynamic cube map texture
            var dynTexture = TextureManager.Singleton.CreateManual("DynCubeMap",
                                                                   ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME,
                                                                   TextureType.TEX_TYPE_CUBE_MAP, 128, 128, 0,
                                                                   PixelFormat.PF_R8G8B8,
                                                                   (int) TextureUsage.TU_RENDERTARGET);

            // Assign our camera to all 6 render targets of the texture (1 for each direction)
            for (uint i = 0; i < 6; i++)
            {
                _targets[i] = dynTexture.GetBuffer(i).GetRenderTarget();
                var vp = _targets[i].AddViewport(_cubeCamera);
                vp.OverlaysEnabled = false;

                _targets[i].PreRenderTargetUpdate += PreRenderTargetUpdate;
                _targets[i].PostRenderTargetUpdate += PostRenderTargetUpdate;

                _targets[i].IsAutoUpdated = false;
            }

            Root.Singleton.FrameStarted += FrameStarted;
            Root.Singleton.FrameEnded += FrameEnded;

            Enabled = true;
            //Root.Singleton.FrameRenderingQueued += FrameRenderingQueued;
        }

        static bool FrameEnded(FrameEvent evt)
        {
            // For testing the output
            //_targets[0].WriteContentsToFile("face1.png");
            //_targets[1].WriteContentsToFile("face2.png");
            //_targets[2].WriteContentsToFile("face3.png");
            //_targets[3].WriteContentsToFile("face4.png");
            //_targets[4].WriteContentsToFile("face5.png");
            //_targets[5].WriteContentsToFile("face6.png");
            return true;
        }

        public event EventHandler<UpdateEventArgs> PreUpdate;
        public event EventHandler<UpdateEventArgs> PostUpdate;
        public class UpdateEventArgs : EventArgs
        {
            public UpdateEventArgs(Camera cubeCamera)
            {
                CubeCamera = cubeCamera;
            }

            public Camera CubeCamera { get; private set; }
        }

        private int _thisFrame;
        bool FrameStarted(FrameEvent evt)
        {
            if (_thisFrame != _updateInterval)
                _thisFrame++;
            else
                _thisFrame = 1;

            if (_thisFrame % _updateInterval == 0 && Enabled)
                foreach (var target in _targets)
                    target.Update();

            return true;
        }

        private int _updateInterval = 1;
        public int UpdateInterval
        {
            get
            {
                return _updateInterval;
            }
            set
            {
                if (value <= 0) throw new ArgumentOutOfRangeException("value");

                _updateInterval = value;
            }
        }

        public bool Enabled { get; set; }

        ~DynamicCubeMapper()
        {
            if (TextureManager.Singleton != null)
                TextureManager.Singleton.Remove("DynCubeMap");
        }

        static void TestGpuCapabilities(RenderSystemCapabilities capabilities)
        {
            if (!capabilities.HasCapability(Capabilities.RSC_CUBEMAPPING))
                throw new NotSupportedException(
                    "Your graphics card does not support cube mapping, so you cannot run this application. Sorry!");
        }

        void PostRenderTargetUpdate(RenderTargetEvent_NativePtr evt)
        {
            if (evt.source == _targets[5] && PostUpdate != null) PostUpdate(this, new UpdateEventArgs(_cubeCamera));

            foreach (var entity in _entitiesToExclude)
                entity.Visible = true;
        }

        void PreRenderTargetUpdate(RenderTargetEvent_NativePtr evt)
        {
            if (evt.source == _targets[0] && PreUpdate != null) PreUpdate(this, new UpdateEventArgs(_cubeCamera));

            // Hide the given entities (characters etc.)
            foreach (var entity in _entitiesToExclude)
                entity.Visible = false;

            // Point the camera in the right direction based on which face of the cubemap this is
            _cubeCamera.Orientation = Quaternion.IDENTITY;

            if (evt.source == _targets[0]) _cubeCamera.Yaw(new Degree(-90));
            else if (evt.source == _targets[1]) _cubeCamera.Yaw(new Degree(90));
            else if (evt.source == _targets[2]) _cubeCamera.Pitch(new Degree(90));
            else if (evt.source == _targets[3]) _cubeCamera.Pitch(new Degree(-90));
            //else if (evt.source == _targets[4]) _cubeCamera.Yaw(new Degree(-180));
            else if (evt.source == _targets[5]) _cubeCamera.Yaw(new Degree(180));
        }
    }
}


To download the full example, look here.