Reflections in UDK

The reason why we see an object is because light is reflected off its surface. In realtime applications this single physical phenomenon must be approximated for performance reasons, so we end up mixing simplified models: the diffuse term simulates evenly scattered light, the specular highlight is a fake reflection of a light source for exampe. Sometimes we need to create really shiny materials and that’s when texture reflections come in to play.

Choosing the type of the texture reflection requires both technical and artistic considerations. How realistic the result should be, how much time the GPU can spend on it, how much control we want, etc.
Here are the reflection implementations most commonly used in games engines:

2D reflections

This is the simplest kind of reflection: it places a single texture onto the surface. Quite cheap but it does not reflect a 3D environment, the image sticks with the camera. This shortcoming is very apparent on big, contiguous, mirror like surfaces so it’s recommended to use this technique when only small bits and pieces are reflective (scraped or chipped off paint for example).

Using the reflection vector is the cheapest method, costs about 3 instructions (only one without the tiling and offset tweaks).

Using the reflection vector Reflections in UDK Reflections in UDK image01 150x150

It has a few issues however: normal map compression artifacts could be very apparent on flat surfaces and UV seams break the continuity of the reflections.

UV seams on the reflection Reflections in UDK Reflections in UDK image02 150x150

Geometry normals make the projection a bit more expensive (~14 instructions, plus 2 for tiling/offset adjustment) but it’s totally seamless. Since it uses geometry data as a base for mapping the image, the result depends on the topology: dense, organic meshes look better then big, flat polygons.

Using geometry normals Reflections in UDK Reflections in UDK image03 150x150

For an additional 3 instructions we can extend the previous method by including a normal map. The normal map obviously creates the illusion of a more detailed mesh and the compression artifacts make the flat surfaces look slightly less weird.

Using surface normals Reflections in UDK Reflections in UDK image04 150x150
Cubemap reflections

By using pre-rendered cubemaps we can create quite precise reflections showing a static environment. The projection itself only costs around 12 instructions but look out for the memory required for the 6 images making up the cubemap asset, especially if multiple cubemaps are used all at once.

Using a cubemap Reflections in UDK Reflections in UDK image05 150x150

Another option is to use dynamically generated textures for quite realistic reflections. They can be computationally very intensive, so use them with care. The technical details are well explained on the related UDN page, so here I only cover them briefly.

 

Captured cubemap
It provides a very precise reflection but if it is set up to be fully dynamic then it can become a resource hog. Fortunately the capture parameters can be adjusted for optimal realism/cost balance. Quite often it’s enough to render the scenery only once, when the map starts, then use it throughout the level.

 

Captured reflect actor
If a flat, mirror like surface needs dynamic reflections then a SceneCaptureReflectActor is the best choice. It’s cheaper then a dynamic cubemap because only re-renders a portion of the world. Works well for water surface, puddles, shiny floors, mirrors, etc.

Mixing reflections

The created reflection needs to be mixed with the rest of the surface properties. There are several ways to do that so now we’ll take a look at the most common methods.

If the reflection is on the emissive channel then it will always be visible even in pitch black shadows, which may or may not be desirable.

Reflection on the emissive channel Reflections in UDK Reflections in UDK image06 150x150

Adding it to the diffuse data looks similar to the previous technique but the reflection is not visible where the surface receives no light.

Reflection added to diffuse Reflections in UDK Reflections in UDK image07 150x150

By using a linear interpolation node the diffuse color can be totally replaced by the reflection which makes this solution ideal for mirror like effects.

Reflection blended with diffuse Reflections in UDK Reflections in UDK image08 150x150

Here we use the reflection as specular color accompanied by a low specular power (making a wide highlight). It looks similar to the “Additive on diffuse channel” method but this time around the reflection disappears in shadows even if the surface is not pitch black there. Also the reflection is automatically colored by the light.

Reflection on the specular channel Reflections in UDK Reflections in UDK image09 150x150
Extensions

Adjusting the color of the reflection often comes in handy: moving the reflection’s color closer to the diffuse hue will make the surface look less “lacquered”. With this setup one can blend between the diffuse texture and a custom color. (White custom color will not change the the reflection.)

Coloring reflections Reflections in UDK Reflections in UDK image10 150x150

It is easy to interpolate between multiple reflections (even mixing projection types) if the asset needs it. A blurred cubemap can make a surface look sanded and even create fake diffuse illumination.

Multiple reflections Reflections in UDK Reflections in UDK image11 150x150

If instead of lerping we simply add multiple reflections together then we can create clear coat paint. Of course the additive mixing could be done while preparing the textures but then we’d lose the ability to fine tune reflection parameters (mixing ratio, separate contrast tweaks, etc).

Alternatively the blurred cubemap can be connected as diffuse data while the sharp reflection used as specular input.