Today, we launched a rendering update that gives reflective objects – both parts with a Reflectance value and terrain water – dynamic reflections based on the sky, the sun and moon, and the time of day. This is a relatively subtle change with a noticeable effect on the immersion of ROBLOX gameplay.
Image courtesy of RbxDevTips, one of the most valuable developer-focused Twitter accounts out there.
To make this happen, we implemented environment maps — a simple, yet effective way to render dynamic reflections. With environment maps, the sky in its current condition is rendered into a cubemap as a special six-sided texture. That cubemap is then is used for all reflections in our shaders. All materials that previously had static reflections are going to get this new dynamic environment map automatically.
The water, however, is getting some extra treatment.
Previously, terrain water was always a bright shade of blue. While water often is blue, in actuality it reflects the color of the sky above it. That’s why the surface color of water is very different at night and during sunsets. Similarly, the “deep water” color was hardcoded and did not change color, ever.
In 3D graphics, each plain texture typically contains more than one image — each one being progressively smaller in resolution. If the original texture is sized at 512×512, it also may contain levels (called ‘mipmaps’, from Latin “multum in parvo”) that are sized 256×256, 128×128 and so on, all the way down to 1×1. Generating each texture helps performance — smaller mipmaps are used on distant objects and hardware can sample them faster. Mipmaps also help prevent texture aliasing and work for cubemaps — every cubemap face contains lower-resolution levels. We take advantage of the graphics hardware to generate mipmaps on the fly, so from a rendered 256x256x6 cubemap we make your graphics card generate the entire mipchain in just 100 microseconds.
Now, you might think that the smallest cubemap level (sized 1×1, i.e., one single texel), being essentially just a single color (six colors, one per each side), is of not much use. However, that single color is approximately the average color of the entire cubemap face! So we use that color in the water shader (by having the cubemap look straight up into the sky), and that gives us a very good approximation of the current sky color/intensity integrated over the top hemisphere. This value becomes our ambient term for the water — the “deep water” color heavily depends on that.
As always, we engineered this feature to have a light footprint, though water is slightly heavier than it was before. We’ve broken terrain water down into two quality levels that will be auto-determined based on the overall performance of the game you’re playing. Unless you’re running a legacy machine from say, 2004 (or a first generation iPad), you’ll see these changes. We here on the ROBLOX rendering team have a huge list of graphical improvements that we’ll be making throughout the year, more of which further involve the sky (3D clouds, anyone?). With our new shadows bundled in with our reflection system, the stage is set for several more aesthetic changes coming up. We’ll keep you posted.