【Godot】Godot 2D Lighting Intro: Creating Depth with PointLight2D and Shadows

Created: 2025-12-10

Implement 2D lighting and shadows with PointLight2D and LightOccluder2D. From basic setup to solving common problems.

Overview

In 2D games made with Godot Engine, lighting plays a role beyond mere decoration. Properly configured light and shadow give scenes depth and atmosphere, guide player attention, and dramatically enhance the game's world.

This article focuses on the PointLight2D node, the core of Godot's 2D lighting system, and explains shadow setup methods, while addressing common stumbling points for beginners to intermediate developers and introducing practical best practices.

By reading this article, you'll learn:

  • Basic structure and mechanics of Godot 2D lighting
  • Basic usage and main properties of PointLight2D node
  • Dynamic shadow generation using LightOccluder2D node
  • Common lighting setup mistakes and their solutions

Core Concepts

Godot's 2D lighting system consists mainly of three elements:

ElementNodeRole
Light sourcePointLight2D / DirectionalLight2DEmits light and illuminates the scene. PointLight2D acts as a point light, DirectionalLight2D as parallel light like sunlight.
Light-blocking objectLightOccluder2DDefines boundaries that block light and generate shadows.
Lit objectNodes inheriting CanvasItem (Sprite2D, TileMap, etc.)Objects affected by light sources, casting shadows or hidden in shadows.

How PointLight2D Works

PointLight2D is a node that radiates light in all directions from a specific point. The light's shape is defined by the texture set in the Texture property. Typically, you use a gradient texture that's bright in the center and darker toward the edges.

Tip: You don't need external tools to prepare images. Select New GradientTexture2D from the Texture property dropdown and set Fill to Radial to create beautiful light gradients within the editor.

Main Properties:

  • Texture: Texture defining light shape.
  • Color: Light color.
  • Energy: Light intensity.
  • Mode: How light blends with scene (Add, Sub, Mix, Mask). Default Add is most common.

Shadow Generation: LightOccluder2D

To generate shadows, you need a light source (PointLight2D) and a LightOccluder2D node to block light.

  1. PointLight2D settings: Turn on Shadow > Enabled.
  2. LightOccluder2D settings: Add a LightOccluder2D node to objects you want to cast shadows (walls, obstacles, etc.) and set an OccluderPolygon2D resource to the Occluder property. This polygon becomes the light-blocking boundary.

Common Pitfalls

Here we explain representative mistakes and misconceptions that beginners often encounter with 2D lighting.

Shadows Not Showing at All

If shadows don't appear even after turning on Shadow > Enabled in PointLight2D, the most common cause is missing LightOccluder2D nodes.

  • Mistake: Thinking shadows will automatically generate just by placing PointLight2D and enabling Shadow.
  • Truth: In Godot's 2D lighting system, you need to explicitly define boundaries to block light with LightOccluder2D nodes. This is separate from collision shapes (CollisionShape2D).

Entire Scene Goes Dark

If the entire scene darkens the moment you place PointLight2D, or areas not hit by light become pitch black, the cause may be CanvasModulate node or WorldEnvironment node settings.

  • Solutions:
    1. CanvasModulate: If there's a CanvasModulate node at the scene root, check if its Color is black (#000000). If black, the entire scene darkens.
    2. LightOccluder2D placement error: A LightOccluder2D may be unintentionally set on an object blocking light covering the entire scene.

Note: WorldEnvironment node is mainly for 3D scenes. If mixing 2D/3D, ambient light settings may affect things, but for pure 2D games, first check CanvasModulate color and each sprite's Modulate property.

Light Source Texture Looks Unnatural

The default PointLight2D texture may look unnatural due to abrupt light falloff.

  • Solution: Set a custom gradient texture with smoother falloff to the PointLight2D's Texture property. Also, adjust the Texture Scale property to widen or narrow the light range for a more natural look.

Shadow Shape is Wrong or Inverted

Even after setting up LightOccluder2D, shadows may appear in strange directions or the inside of objects may become shadowed.

  • Solution: The vertex order (clockwise/counter-clockwise) when drawing OccluderPolygon2D may be affecting this. Try changing the Cull Mode in the inspector, or change the direction you draw polygon vertices.

Best Practices and Examples

Here we introduce practical techniques for achieving more professional 2D lighting.

Efficient LightOccluder2D Setup

When using TileMaps, setting LightOccluder2D individually on all tiles is inefficient.

Recommended method:

  1. Set Occluders in TileSet: Set Occlusion Layer for tiles that should block light (walls, etc.) within the TileSet resource. This makes the entire TileMap node function as a single LightOccluder2D, improving performance.
  2. OccluderPolygon2D optimization: When creating OccluderPolygon2D, keep vertex count to the minimum necessary. Overly complex polygons increase rendering cost and impact performance.

Dynamic Light Source Control with GDScript

Dynamically controlling light sources based on player movement or game events can add tension and realism to gameplay.

For example, if the player holds a flashlight, make that light source follow the player with energy that decreases over time.

# Player.gd (assumes PointLight2D node as child)

extends CharacterBody2D

@export var max_light_energy: float = 1.5
@export var energy_drain_rate: float = 0.05 # Drain per second

@onready var flashlight: PointLight2D = $PointLight2D

func _process(delta: float) -> void:
    # Optionally rotate light source based on player direction
    # var mouse_pos = get_global_mouse_position()
    # flashlight.rotation = (mouse_pos - global_position).angle()

    # Gradually decrease energy
    if flashlight.energy > 0.0:
        flashlight.energy -= energy_drain_rate * delta
        flashlight.energy = max(0.0, flashlight.energy)

    # Toggle flashlight on/off
    if Input.is_action_just_pressed("toggle_light"):
        flashlight.enabled = not flashlight.enabled

    # Auto-off when energy reaches zero
    if flashlight.energy == 0.0:
        flashlight.enabled = false

# Function to restore energy with items, etc.
func restore_energy(amount: float) -> void:
    flashlight.energy += amount
    flashlight.energy = min(max_light_energy, flashlight.energy)
    if not flashlight.enabled:
        flashlight.enabled = true

Note: The example above uses an input action called toggle_light. Add toggle_light in "Project Settings" → "Input Map" and assign a keyboard or gamepad button.

Using Light Occlusion Mask Layers

In Godot 4, combining Light2D node's Item Cull Mask and CanvasItem node's Light Mask properties allows fine control over which light sources illuminate which objects.

For example, to separate "normal light" and "special magic light", configure as follows:

  1. Normal light (PointLight2D): Set Range > Item Cull Mask to Layer 1.
  2. Magic light (PointLight2D): Set Range > Item Cull Mask to Layer 2.
  3. Normal objects (Sprite2D, etc.): Turn on Layer 1 in Visibility > Light Mask.
  4. Objects responding only to magic light: Turn on Layer 2 in Visibility > Light Mask.

This allows logical separation of lighting scope even in complex scenes and contributes to performance optimization.

Note: Property names have changed in Godot 4. Light source side is Range > Item Cull Mask, lit object side is Visibility > Light Mask.

Summary

Understanding the combination of PointLight2D and LightOccluder2D is the key to success with Godot's 2D lighting.

  • PointLight2D: The light source itself. Adjust light appearance with texture and energy.
  • LightOccluder2D: Boundary for casting shadows. Essential node for generating shadows.

Once you master these basics, your 2D game will evolve from flat expression to a world rich with depth woven by light and shadow. For next steps, we recommend challenging Normal Maps for more realistic lighting expression or extending light expression with custom shaders.