An acquaintance of mine, Sahak, found me with an interesting challenge: he wanted me to get his “Virtual city of Oslo” map to a presentable form.
He imported simple buildings from City Engine to UDK and made a map of the city.
Problems with the City Engine -> Unreal Engine asset pipeline and the sheer scope of the project led to an interesting task specification:
- The city barely fits Unreal’s “world", which is a roughly 10x10 km area.
- 4188 unique buildings made up the city. No instancing at all. Each building had its pivot at the world origin.
- No textures or proper UVs.
- I had 48 hours to finish.
After the two days, I came up with this:
Virtual Oslo flythrough.
(HD version on Vimeo.)
The more technical, “behind the scenes” stuff is after the break.
Originally Sahak wanted to use Lightmass for global illumination, but that was not a viable option for several reasons: The lightmap UVs for the buildings were very wasteful. I’ve made a few test and determined that a building of average size would need at least a 128x128 lightmap while the ground pieces would require 4 lightmaps, 4096x4096 each. This meant that the lighting data would be around 18 Gb before compression. (3x6 Gb because of the directional lightmapping.)
Of course it never become a problem in practice because Lightmass ran out of memory loooong before producing actual lightmaps.
So I had to do something else. First I wanted to go entirely with a Tron/Rez style, but then I felt that some fake GI might make things more interesting.
These are the unlit buildings:
And here are the steps producing the fake GI effect:
Using world relative normals I blend between three colors, one for each axis. The Z (up/down) axis has an almost white color, a bit darker gray goes for Y and an even darker one for X.
Then comes a world relative gradient: dark at sea level and white around 920 units high.
The final touch is an accessibility map of some sort, which darken walls far from open spaces. The texture is based on a top-down screenshot of the map and projected in a similar fashion, world relative.
The top most image is the screenshot of the map, taken from an orthogonal viewport. (I cropped the image here.)
A high-pass filter then highlights areas between buildings. Some tweaks to the histogram curve make it look like actual shadowing.
A ‘dilate’ (or ‘maximum’) filter deletes shadows which are close to only one wall.
Finally a gaussian blur makes sure that rouge, white pixels don’t creep out from under the buildings.
The shadows of the buildings on the ground is using the same world relative planar projection. It was made by simply blurring the original screenshot.
With shadows only, the ground was quite boring, so I added two more design elements: Blurred reflections and a grid:
The reflection is generated by a “render-to-texture” actor and blurred using 8 passes while a noise texture scatters the image even further. Depth is also factored in so the effect of the blur and noise fades out by distance.
The grid also uses depth: the grid size decreases as it gets further away from the camera to prevent excessive moire.
The stripes on the buildings are based on distance from both the world origin and a second point, circling around the origin. (The subtle animation is barely visible in the video.)
The size of the stripes are big enough so no moire happens unless one flies above the city real high. But then the flickering (caused by the z-buffer’s imprecision) and the low framerate ruins the whole thing anyway.
The skydome has billowing clouds on a simple gradient. The cloud texture is a simple, tiling perlin noise, which gets distorted by itself in the shader.
The water is a mix of noise textures, one is using bilinear filtering while the other isn’t. The texture is then fed to a sine function so it’s brightness cycles around.
Unfortunately I ran out of time before I could set up an edge detection based post process to emphasize building contours, but I did enable the fake antialiasing I wrote about recently.