Retroreflection for Unreal Engine

I’ve always been fascinated by light, but my understanding of it initially came from rendering systems in 3D editors and game engines. Over time, my curiosity grew beyond the solutions these tools provided. I began exploring how modern scientists define a physically accurate lighting model. Drawing from various publications, I developed my own understanding and created a unique illumination model scheme.



Of course, there’s nothing groundbreaking here, but I find it useful to have my own scheme whenever I revisit this topic at work. I’m fully aware that lighting models in game engines are incomplete for practical reasons. I also understand that implementing specular and diffuse transmission is challenging and not always necessary. However, I’ve always wondered why most game engines ignore retroreflection. It’s a simple component of the physical lighting model, easy to implement, and, most importantly, fully adheres to the law of energy conservation.



Retroreflection happens when a surface reflects a significant portion of a directed light beam back toward its source. Researchers uncovered the essence of this phenomenon long ago. Nowadays, many industrial solutions exist for producing retroreflective materials. The technologies for creating such materials generally follow two main approaches: reflection combined with refraction (glass sphere reflectors) and a simple sequence of light bounces (cube-corner or prismatic reflectors).



Retroreflection materials appear brightest to observers closest to the light source. I believe that for some of the projects I’m familiar with, retroreflection could be an excellent solution to both visual and game design challenges.
In this part of the article, I will explain how to add a Retroreflection shading model to Unreal Engine. Modern game engines, including Unreal Engine, use physically accurate microfacet models to simulate reflections and light scattering on rough surfaces. These models are optimized for real-time rendering, often incorporating precomputed integral calculations baked into LUT textures. Clearly, the previously illustrated schemes of light reflection and refraction are not applicable in this context. Unreal Engine uses the Cook-Torrance BRDF with GGX for reflections.


The main calculations are performed in ShadingModels.ush. I decided that, for now, I want to limit it to direct lighting. In other words, in my model, retroreflection will come from all light sources within whose radius the object is located. The simplest way to achieve this is to override the DefaultLitBxDF function for my shading model.

These variables became the basis for the calculation:

Argument

Type

Description

GBuffer

FGBufferData

Data from the GBuffer, including colors, roughness, metallic, and other material parameters.

N

half3

Surface normal

V

half3

View direction (camera vector)

L

half3

Light direction

Falloff

float

Light attenuation coefficient

NoL

half

Cosine of the angle between the normal and the light direction (dot(N, L)). Used for Lambertian shading


"NoL" became the primary target, as modifying it allows for a convincing imitation of retroreflection. The second important factor was "Falloff," which definitely needed adjustment. I introduced two additional variables: "VoL" (the dot product between the view direction and the light direction) and "VoN" (the dot product between the view direction and the surface normal). These variables serve as interpolation weights between the original "NoL" and "Falloff" and their modified versions.



And here is the magic of retroreflection in action. Instead of detailing the embedding process, I'll simply list the files that need to be modified. I don't want to turn this into a tedious tutorial.

File

Modification Description

BasePassCommon.ush

Add CSM to activation list

ClusteredDeferredShadingPixelShader.usf

Add light greed CSM to per shading model pass

DeferredShadingCommon.ush

Add custom GBuffer data for CSM

Definitions.usf

Add CSM macros

ShadingCommon.ush

Add CSM ID and debug colors

ShadingModels.ush

Add CSM BRDF

ShadingModelsMaterial.ush

Fill GBuffer custom dada for CSM

ShadingModelsSampling.ush

Add CSM support in BxDF

EngineTypes.h

Register CSM in shading model stack

HLSLMaterialTranslator.cpp

Add CSM in “Lit” environment

Material.cpp

Activate Custom Data 0 for CSM

MaterialHLSLEmitter.cpp

Add CSM in “Lit” environment

MaterialShader.cpp

Add CSM to string description and appropriate stats

ShaderGenerationUtil.cpp

Add fetch compile bool. Add custom data slot for CSM

MaterialExpressionShadingModel.h

Add CSM to material interface

ShaderMaterialDerivedHelpers.cpp

Add CSM derived materials helpers

ShaderMaterial.h

Add CSM field in FShaderMaterialPropertyDefines

*Custom Shading Model - CSM

After modifying all this you will get additional shading model as in the picture below:


I also activated the Custom Data to mask the retroreflection. And I added the multiplier for "Falloff."



It is possible to add retroreflection to non-directional lighting, but for game design reasons, it was decided to use only directional lighting.

At a time when the creation of game characters and environments has reached the limits of possibility, retroreflection can be the feature that makes your content, design, or idea unique and stand out from the rest.

I want to thank Nick Barre for providing the jacket model for testing the shading model.
I hope this material will be useful, thank you for your attention.