DEV Community

taku25
taku25

Posted on

Balancing Physically Accurate Lighting and Symbolic VFX in Unreal Engine

Introduction

Tutorials for setting up physically based rendering (PBR) in Unreal Engine using real-world Lux and EV100 values are becoming common. However, once you implement physically correct lighting, you run into game design problems, like "fire effects becoming invisible during the day."

This article explains how to leverage the auto-exposure (Eye Adaptation) effect—like when emerging from a cave—to create a realistic, physically-based day/night cycle while ensuring that "symbolic" effects remain visible regardless of the ambient brightness.


Step 1: EV Value Calculation and Basic Auto Exposure Setup

First, let's set up our camera (PostProcessVolume) and ambient light (SkyLight) to handle a day/night cycle.

1. EV100 Calculation Formula

To perform physically based lighting, we first need to calculate the "correct exposure" (EV100) from the real-world "illuminance" (Lux).

The formula to find the EV100 required to properly expose an 18% middle-gray object based on the illuminance (Lux) hitting it is:

EV100=log2(Luxπ) EV_{100} = \log_2 \left( \frac{Lux}{\pi} \right)

2. Calculate Day and Night EV Values

Let's use this formula to calculate the "limit" values for day and night.

  • Day (12:00 PM):

    • Assume bright sunlight is 100,000 Lux.
    • EV100=log2(100,000/3.14159)EV_{100} = \log_2 (100,000 / 3.14159) \approx 15
    • (We'll set it to 18 to give some margin for bright reflections, like snow.)
  • Night (12:00 AM):

    • Consider moonlight (0.1 Lux) or starlight (0.001 Lux).
    • EV100=log2(0.001/3.14159)EV_{100} = \log_2 (0.001 / 3.14159) \approx -8.3
    • (Therefore, we'll set the night limit to -8.)

3. Lock Auto Exposure Range to "Physical Limits"

Based on the values from Step 1.2, we'll define the camera's (eye's) adaptation limits in the PostProcessVolume. Do not change these settings during gameplay.

  • Metering Mode: Auto Exposure Histogram (leave as auto)
  • Min EV100: -8.0 (The limit for capturing starlight)
  • Max EV100: 18.0 (The limit for sunlight, including margin)

PostProcessVolume EV100 Settings

By fixing this range, the camera will constantly try to auto-expose (Eye Adaptation) from the darkest places (deep caves) to the brightest (direct sunlight on snow).

(TIPS: In UE 5.3 and earlier, you had to enable Extend default Luminance range in Project Settings, but recent versions use this wide EV100 range by default.)


Step 2: Control Day/Night with DirectionalLight "Lux" and "Color"

We'll prepare two Curve assets to control the DirectionalLight (Sun/Moon).

  • C_DayNight_SunLux (for Intensity)
  • C_DayNight_LightColor (for Light Color)

Use these curves to update the DirectionalLight values on a Tick event. (For debugging, it's helpful to create a system where one day loops every 24 seconds by adding DeltaTime * 0.1 to the time.)

1. "Lux" Curve (Physical Light Intensity)

  • Day (12:00): 100,000 Lux (Clear sky sun)
  • Night (0:00): 0.1 Lux (Moonlight) Lux Curve

2. "Color" Curve (Artistic Color)

  • Day (12:00): (R:1.0, G:1.0, B:1.0) (Pure white)
  • Night (0:00): (R:0.1, G:0.1, B:0.15) (A faded dark gray, slightly blueish)

Color Curve

Why change the "Color" too?

Since our Min EV100 is set so low (-8.0), the camera sees the dark night scene (0.1 Lux) as 'underexposed' and automatically boosts its sensitivity (EV value) to -8.0 to compensate.

The result is a bright, noisy image that looks like daytime, not the dark night scene we intended.

While the correct approach might be to control the PostProcessVolume's Exposure Compensation value (e.g., setting it to -5.0 at night), tuning that curve can be tedious.

So, for this simple example, we'll use a more direct method to create the "nighttime feel": intentionally setting the Light Color to a "dark gray." Auto Exposure will still try to brighten the scene, but because the light source itself is a dark gray, the final image will be desaturated and have the intended "nighttime" atmosphere.


Step 3: Protecting "Symbolic Effects" from Exposure

Now for the main topic. Let's display a fire magic effect (material emissive brightness: 3000 cd/m²) in this physically-based world.

  • At Night: The EV100 is around -8.0 (settings for a bright picture), so the effect glows brilliantly.
    At Night

  • At Day: The EV100 is around 18.0 (settings for a dark picture). Compared to the 100,000 Lux sunlight, the 3000 brightness value is tiny, and the effect becomes almost invisible.

Effect at Day

This is physically correct, but it will lead to a storm of complaints from players ("I can't see the hitbox!"). We can't use "it's physically correct!" as an excuse for symbolic, gameplay-critical effects.

[Solution] Cancel Exposure Compensation in the Material

Instead of making separate effects for day and night, we can solve this with a single material. Luckily, UE provides a node to get the current camera exposure compensation value.

  1. Open the material used by your effect.
  2. Add the EyeAdaptationInverse node.
    • This node returns a large value when the camera is trying to darken the scene (daytime) and a small value when it's trying to brighten it (nighttime).
  3. Multiply your original Emissive Color calculation by this EyeAdaptationInverse node.

EyeAdaptationInverse Material Node

(Note: If you are on an older version and can't find the EyeAdaptationInverse node, you can get the exact same result by adding the *Exposure (Eye Adaptation)** node and Divide-ing your original Emissive Color by it.)*

Result

By applying this material, the effect will now maintain its intended perceived brightness regardless of the scene's exposure (day or night, inside a cave or out).

  • Day:
    Corrected Effect at Day

  • Night:
    Corrected Effect at Night


Summary

  1. EV100 Formula: Calculate day/night exposure limits (e.g., -8 and 18) based on EV100=log2(Lux/π)EV_{100} = \log_2 (Lux / \pi) .
  2. EV Min/Max (Range): Fix the PostProcessVolume's EV range to these limits (e.g., -8 to 18) for the entire game.
    • → This keeps "Eye Adaptation" (bright/dark adaptation) constantly working.
  3. SkyLight: Enable Real Time Capture.
    • → Allows it to automatically follow changes from the DirectionalLight.
  4. DirectionalLight (Intensity & Color): Dynamically change using Curve assets.
    • → Use Lux for physical brightness and Color (dark gray) for artistic "nighttime feel."
  5. Symbolic VFX: In the material, Multiply the Emissive Color by EyeAdaptationInverse.
    • → This cancels the exposure compensation, maintaining a consistent perceived brightness.

Using this method, you can balance physical accuracy with the "symbolic" readability required for games.

Top comments (0)