Use Penumbra with a custom Matrix [Solved]

I’ve recently tried to implement the Penumbra library into my game so I could add lights. However, it’s using the same matrix that I use for my SpriteBatch, which always follows the player. There’s a problem with this since whenever I add a light, the light moves along with the camera instead of being stationary.
Like in this .gif:

Here’s the constructor of Game1:

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        IsMouseVisible = true;
        graphics.PreferredBackBufferWidth = 1280;
        graphics.PreferredBackBufferHeight = 720;
        Window.Position = new Point((GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width / 2) - (graphics.PreferredBackBufferWidth / 2),
                                         (GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height / 2) - (graphics.PreferredBackBufferHeight / 2));
        Content.RootDirectory = "Content";
        // Lightning Engine
        penumbra = new PenumbraComponent(this);
        Components.Add(penumbra);
        penumbra.Lights.Add(new PointLight() { Position = new Vector2(720, 350) });
    }

Here’s where I call the Draw method from Penumbra:

    protected override void Draw(GameTime gameTime)
    {
        penumbra.BeginDraw();
        controller.DrawObjects(ref spriteBatch);
        base.Draw(gameTime);
    }

This is the other Draw method I use:

    public void DrawObjects(ref SpriteBatch spriteBatch)
    {
        spriteBatch.GraphicsDevice.Clear(Color.CornflowerBlue);
        spriteBatch.Begin(transformMatrix: Camera.TransformMatrix());
        foreach (Component component in components)
        {
            component.Draw(ref spriteBatch);
        }
        spriteBatch.End();
    }

And this is my Camera class:

class Camera
{
static Matrix transform;

    public Camera()
    {
    }
    public void Follow(Sprite sprite)
    {
        Matrix position = Matrix.CreateTranslation(
            -sprite.Position().X - (sprite.Rectangle().Width / 2),
            -sprite.Position().Y - (sprite.Rectangle().Height / 2),
            0);
        Matrix offset = Matrix.CreateTranslation(
            Game1.screenWidth / 2,
            Game1.screenHeight / 2,
            0);
        transform = position * offset;
    }
    static public Matrix TransformMatrix() { return transform; }
}

I tried changing the Matrix like it says in the documentation:

But it didn’t work for me. Maybe I’m doing something wrong I don’t know. Here’s the code:

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        IsMouseVisible = true;
        graphics.PreferredBackBufferWidth = 1280;
        graphics.PreferredBackBufferHeight = 720;
        Window.Position = new Point((GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width / 2) - (graphics.PreferredBackBufferWidth / 2),
                                         (GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height / 2) - (graphics.PreferredBackBufferHeight / 2));
        Content.RootDirectory = "Content";
        // Lightning Engine
        penumbra = new PenumbraComponent(this);
        Components.Add(penumbra);
        penumbra.SpriteBatchTransformEnabled = false;
        penumbra.Transform = Matrix.CreateTranslation(-720, -400, 0);
        penumbra.Lights.Add(new PointLight() { Position = new Vector2(0, 0) });
    }

And the result:

If you could help me that would be really appreciated.

1 Like

You forgot to call penumbra.Draw() at the end of your drawing function:

protected override void Draw(GameTime gameTime)
{
         penumbra.BeginDraw();
         controller.DrawObjects(ref spriteBatch);

         penumbra.Draw(); // <- Implement this here!

         base.Draw(gameTime);
}

I get this error when adding that. Also, it needs the gameTime parameter.

Ah I see you put penumbra into the component list. Please remove it from components and try running it again.

I removed “Components.Add(penumbra);” and now I get this error:


Constructor:

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        IsMouseVisible = true;
        graphics.PreferredBackBufferWidth = 1280;
        graphics.PreferredBackBufferHeight = 720;
        Window.Position = new Point((GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width / 2) - (graphics.PreferredBackBufferWidth / 2),
                                         (GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height / 2) - (graphics.PreferredBackBufferHeight / 2));
        Content.RootDirectory = "Content";
        // Light Engine
        penumbra = new PenumbraComponent(this)
        {
            SpriteBatchTransformEnabled = false,
            Transform = Matrix.CreateTranslation(0, 0, 0)
        };
    }

Initialize:

    protected override void Initialize()
    {
        screenWidth = graphics.PreferredBackBufferWidth;
        screenHeight = graphics.PreferredBackBufferHeight;
        controller = new Controller();
        // Point Light
        penumbra.Lights.Add(new PointLight() { Position = new Vector2(650, 350) });
        base.Initialize();
    }

Draw:

    protected override void Draw(GameTime gameTime)
    {
        penumbra.BeginDraw();
        controller.DrawObjects(ref spriteBatch);
        penumbra.Draw(gameTime);
        base.Draw(gameTime);
    }

Here are my variables:

    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    Controller controller;
    // Light Engine
    PenumbraComponent penumbra;
    public static int screenWidth;
    public static int screenHeight;

Call Initialize on the PenumbraComponent before using it.

1 Like

I added it to the code but there are still no lights
Initialize method:

   protected override void Initialize()
    {
        screenWidth = graphics.PreferredBackBufferWidth;
        screenHeight = graphics.PreferredBackBufferHeight;
        controller = new Controller();
        // Light Engine
        penumbra = new PenumbraComponent(this);
        penumbra.SpriteBatchTransformEnabled = false;
        penumbra.Initialize();
        penumbra.Transform = Matrix.CreateTranslation(0, 0, 0);
        penumbra.Lights.Add(new PointLight() { Position = new Vector2(720, 400) });
        base.Initialize();
    }

Draw method:

    protected override void Draw(GameTime gameTime)
    {
        penumbra.BeginDraw();
        controller.DrawObjects(ref spriteBatch);
        penumbra.Draw(gameTime);
        base.Draw(gameTime);
    }

Result:

Now that Penumbra is set up, you have two options:

  • Remove SpriteBatchTransform = false (so it becomes true) and update the Penumbra.Transform Matrix according to your camera Matrix.

OR:

  • Keep SpriteBatchTransform = false and remove Penumbra.Transform Matrix transformations like in your current code:

penumbra.Transform = Matrix.CreateTranslation(0, 0, 0);

You shouldn’t do both.

1 Like

Thank you so much! The light doesn’t move with the camera anymore.
What fixed it for me was the 1st thing you said:
(in case anyone runs into the same problem here’s the thing that worked for me)
Initialize:

protected override void Initialize()
{
    screenWidth = graphics.PreferredBackBufferWidth;
    screenHeight = graphics.PreferredBackBufferHeight;
    controller = new Controller();
    // Light Engine
    penumbra = new PenumbraComponent(this);
    penumbra.Initialize();
    penumbra.Lights.Add(new PointLight() { Position = new Vector2(720, 400) });
    base.Initialize();
}

Update:

    protected override void Update(GameTime gameTime)
    {
        controller.UpdateObjects();
        Window.Title = (1 / gameTime.ElapsedGameTime.TotalSeconds).ToString();
        penumbra.Transform = Camera.TransformMatrix();
        base.Update(gameTime);
    }

Draw:

    protected override void Draw(GameTime gameTime)
    {
        penumbra.BeginDraw();
        controller.DrawObjects(ref spriteBatch);
        penumbra.Draw(gameTime);
        base.Draw(gameTime);
    }
1 Like