Playing around with the new Point lights

Nothing majorly impressive, but I think it looks great with the new shadow enhancements. Now, if I can integrate a nice GUI which allows you to pick the colour and range of the light, it would be pretty immense.

By Neil_Knight Posted in XNA

UDK maps

I caught wind that you could use UDK to develop maps and run them in XNA. This intrigued me some what, so I decided to give it a go. The lighting needs to be tweaked, but I think that there is more to the exporting process than just clicking Export. Still, my shadow mapping works really nicely.

New lighting system

Over the next couple of days, I’ll be revisiting the lighting system in order to get a more complete system which is easy to use, and also implement new lights like Spot Lights. Here is the new hemispheric light:

Got the kettle on?

Introducing the Light Manager…

I have finally managed to get round to implementing the LightManager. This post will explain how this in implemented into the engine and how to use the new class.

Back in the earlier version of this project, I wrote a simple Light Manager. This new LightManager class is based on the old one, but with a few new modifications. Start by creating a class in the Core folder called LightManager. Add the following namespaces:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using ProjectVanquish.Renderers;
using ProjectVanquish.Cameras;

Declare the class as public and then add the following fields:

Effect directionalLightEffect, hemisphericLightEffect, pointLightEffect;
Model sphereModel;
QuadRenderer fullscreenQuad;
Vector2 halfPixel;
Texture2D hemisphericColorMap;
static Lights.DirectionalLight light;
static IList<Lights.PointLight> pointLights;

Here we are taking some of the fields from the DeferredRenderer class, so we can delete them from there. We can now add the constructor:

public LightManager(Game game)
    // Load Effects, Models and Quad Renderer
    directionalLightEffect = game.Content.Load("Shaders/Lights/DirectionalLight");
    pointLightEffect = game.Content.Load("Shaders/Lights/PointLight");
    hemisphericLightEffect = game.Content.Load("Shaders/Lights/HemisphericLight");
    sphereModel = game.Content.Load("Models/Sphere");
    fullscreenQuad = new QuadRenderer(game);
    halfPixel = new Vector2()
        X = 0.5f / (float)game.GraphicsDevice.PresentationParameters.BackBufferWidth,
        Y = 0.5f / (float)game.GraphicsDevice.PresentationParameters.BackBufferHeight

    // Load the Color Map for the Hemispheric Light
    hemisphericColorMap = game.Content.Load("Textures/ColorMap");

    // Make our directional light source
    light = new Lights.DirectionalLight();
    light.Direction = new Vector3(-1, -1, -1);
    light.Color = new Vector3(0.7f, 0.7f, 0.7f);

    // Instantiate the PointLights List
    pointLights = new List();

Back in the DeferredRenderer class, we had all of the old DrawLights code. We’ll start adding this into this class.

void DrawDirectionalLight(RenderTarget2D colorRT, RenderTarget2D normalRT, RenderTarget2D depthRT, Camera camera)
    // Set all parameters
    directionalLightEffect.Parameters["InvertViewProjection"].SetValue(Matrix.Invert(camera.ViewMatrix * camera.ProjectionMatrix));

    // Apply the Effect

    // Draw a FullscreenQuad
    fullscreenQuad.Render(Vector2.One * -1, Vector2.One);

You’ll notice that the method declaration is different from the old one. This is because we don’t have direct access to the required fields like we did before, and I’d like to keep it that way. We’ll be passing them through the modified DrawLights method, which we’ll get to shortly. The next method to move is the DrawHemisphericLight:

void DrawHemisphericLight(GraphicsDevice device, SceneManager scene, Camera camera)
    device.BlendState = BlendState.Opaque;

    // Only apply the effect to those models in the Frustum
    foreach (Models.Actor actor in scene.Models.Where(a => camera.BoundingFrustum.Intersects(a.BoundingSphere)))
        foreach (ModelMesh mesh in actor.Model.Meshes)
            foreach (ModelMeshPart part in mesh.MeshParts)
                // Set the Effect Parameters
                hemisphericLightEffect.Parameters["matWorldViewProj"].SetValue(actor.World * camera.ViewMatrix * camera.ProjectionMatrix);
                hemisphericLightEffect.Parameters["vLightDirection"].SetValue(new Vector4(light.Direction, 1));
                hemisphericLightEffect.Parameters["SkyColor"].SetValue(new Vector4(light.Color, 1));

                // Apply the Effect

                // Render the Primitives
                device.SetVertexBuffer(part.VertexBuffer, part.VertexOffset);
                device.Indices = part.IndexBuffer;
                device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, part.NumVertices, part.StartIndex, part.PrimitiveCount);

    device.BlendState = BlendState.AlphaBlend;

Before moving onto the DrawLights method, we’ll implement the DrawPointLights method:

void DrawPointLight(GraphicsDevice device, RenderTarget2D colorRT, RenderTarget2D normalRT, RenderTarget2D depthRT, Camera camera, Lights.PointLight pointLight)
    // Set the G-Buffer parameters

    // Compute the light world matrix
    // scale according to light radius, and translate it to light position
    Matrix sphereWorldMatrix = Matrix.CreateScale(pointLight.Range) * Matrix.CreateTranslation(pointLight.Position);

    // Light position

    // Set the color, radius and Intensity

    // Parameters for specular computations
    pointLightEffect.Parameters["InvertViewProjection"].SetValue(Matrix.Invert(camera.ViewMatrix * camera.ProjectionMatrix));

    // Size of a halfpixel, for texture coordinates alignment

    // Calculate the distance between the camera and light center
    float cameraToCenter = Vector3.Distance(camera.Position, pointLight.Position);

    // If we are inside the light volume, draw the sphere's inside face
    if (cameraToCenter < pointLight.Range)
        device.RasterizerState = RasterizerState.CullClockwise;
        device.RasterizerState = RasterizerState.CullCounterClockwise;

    // Reset DepthStencilState
    device.DepthStencilState = DepthStencilState.None;

    // Apply the Effect

    // Draw the Sphere mesh
    foreach (ModelMesh mesh in sphereModel.Meshes)
        foreach (ModelMeshPart meshPart in mesh.MeshParts)
            device.SetVertexBuffer(meshPart.VertexBuffer, meshPart.VertexOffset);
            device.Indices = meshPart.IndexBuffer;
            device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount);

    // Reset RenderStates
    device.RasterizerState = RasterizerState.CullCounterClockwise;
    device.DepthStencilState = DepthStencilState.Default;

You may have noticed some changes to this method. We are now using a pointLight object. We’ll need to modify the PointLight class to allow for the new properties, but we’ll finish this class first. Lastly, we’ll add the modified DrawLights method:

public void DrawLights(GraphicsDevice device, RenderTarget2D colorRT, RenderTarget2D normalRT, RenderTarget2D depthRT, RenderTarget2D lightRT, Camera camera, SceneManager scene)
    // Set the Light RenderTarget

    // Clear all components to 0
    device.BlendState = BlendState.AlphaBlend;
    device.DepthStencilState = DepthStencilState.None;

    // Render either the Directional or Hemispheric light
    if (UseHemisphericLight)
        DrawHemisphericLight(device, scene, camera);
        DrawDirectionalLight(colorRT, normalRT, depthRT, camera);

    // Render each PointLight
    foreach (Lights.PointLight pointLight in pointLights)
        DrawPointLight(device, colorRT, normalRT, depthRT, camera, pointLight);

    // Reset RenderStates
    device.BlendState = BlendState.Opaque;
    device.DepthStencilState = DepthStencilState.None;
    device.RasterizerState = RasterizerState.CullCounterClockwise;

    // Reset the RenderTarget

We have now got our new LightManager class ready, so we can plug this into the DeferredRenderer class. In the DeferredRenderer class, add a new field:

private LightManager lightManager;

In the LoadContent method, we’ll instantiate the LightManager:

// Instantiate the LightManager
lightManager = new LightManager(Game);

We are reaching the final goal posts. There is one big change to the DeferredRenderer class. As we have modified the DrawLights call, we need to build a new method:

void CombineFinal(RenderTarget2D shadowOcclusion)
    // If SSAO is enabled, set the RenderTarget
    if (SSAORenderer.Enabled)

    // Set the effect parameters

    // Apply the Effect

    // Render a full-screen quad
    quadRenderer.Render(Vector2.One * -1, Vector2.One);

This will create our final scene. In the Draw method, we need to alter it to use the new LightManager.DrawLights method, and also include the new method. Place the following after the Render Shadows call:

// Draw Lights
lightManager.DrawLights(GraphicsDevice, colorRT, normalRT, depthRT, lightRT, camera, scene);

// Combine the Final scene

Before we can compile this, we need to modify the PointLight class. Below is the complete listing for the PointLight class:

protected float intensity = 0.0f;
protected float range = 0.0f; 

public PointLight(Vector3 position, Vector3 color, float range, float intensity)
    : base()
    Position = position;
    Color = color;
    Range = range;
    Intensity = intensity;

public float Intensity
    get { return intensity; }
    set { intensity = value;}

public Vector3 Position
    get { return worldMatrix.Translation; }
    set { worldMatrix.Translation = value; }

public float Range
    get { return range; }
    set { range = value; }

Because the SpotLight class is inherited from the PointLight class, we’ll need to modify the constructor of that too. Here is the modified version:

public SpotLight(Vector3 position, Vector3 color, float range, float intensity)
    : base(position, color, range, intensity)

Now, compiling the project shouldn’t give you any errors and you’ll be ok to run it, albeit without Point Lights as these are not yet implemented into the LightManager class yet. The full sourcecode can be downloaded from the Codeplex page, and the source code is found here.

What’s next inline?

Well, after resolving the issue of the PointLight, it highlighted the fact that there is no real way of adding a new PointLight.  So, I’m going to start work on a LightManager class that will make this a lot easier.  As soon as I’ve done this, I’ll create a post explaining what has been implemented with a step-by-step guide so you can implement it yourselves.

Some interesting findings

Whilst I was so eager to get the Sky dome rendering working, I hadn’t realised but I had broken the PointLight implementation. I had changed the RenderTarget settings so that the new Moon functionality would work, but this is where it all went wrong. For now, I’ve commented out the DrawGlow and DrawMoon methods and reinstated the correct RenderTarget settings:

Correct settings:

colorRT = new RenderTarget2D(GraphicsDevice, backBufferWidth, backBufferHeight, false, SurfaceFormat.Color, DepthFormat.Depth24);
normalRT = new RenderTarget2D(GraphicsDevice, backBufferWidth, backBufferHeight, false, SurfaceFormat.Color, DepthFormat.None);
depthRT = new RenderTarget2D(GraphicsDevice, backBufferWidth, backBufferHeight, false, SurfaceFormat.Single, DepthFormat.None);
depthTexture = new RenderTarget2D(GraphicsDevice, backBufferWidth, backBufferHeight, false, SurfaceFormat.Single, DepthFormat.Depth24);
lightRT = new RenderTarget2D(GraphicsDevice, backBufferWidth, backBufferHeight, false, SurfaceFormat.Color, DepthFormat.None);
sceneRT = new RenderTarget2D(GraphicsDevice, backBufferWidth, backBufferHeight, false, SurfaceFormat.Color, DepthFormat.Depth24);

That's fixed the Sky brightness

Sky rendering

It has been a very long time since I’ve managed to get some time to work on Project Vanquish. And, as a result, I’ve managed to add in atmospheric scattering based on the example over at XNA Community. There are a few problems with it at the moment, for example, the sky is very dark and the clouds don’t appear, but I’ll be resolving those after I’ve fixed an issue with, what looks like, Z-Fighting. Anyway, here’s a screen grab of the latest developments.

Hmmm... need some form of AntiAliasing

Found out the darkness is caused by not correctly setting the Exposure.

By Neil_Knight Posted in XNA

Well, now I feel silly

The problem was not with the transparency but with the texture that I was rendering. I was using the Color RenderTarget instead of the Color Map texture. D’oh!! Also, further investigation showed that if I lowered the Lights intensity, this would make the models transparent again. So, I shuffled the draw order around and it’s resolved.

High Light intensity

Low Light intensity

By Neil_Knight Posted in XNA