Tuesday, November 17, 2020

Procedural Buildings: Improvements Based on Feedback

Yes, I'm still working on procedural building interiors. It seems like there are an infinite number of things to add and fix. This set of improvements to interiors is mostly the result of feedback and suggestions from my nine year old daughter and Paul Spooner. A few of these were old TODO items as well that I finally got a chance to work on. I'll list them here in the approximate order I that implemented them, along with lots of screenshots.

Lamps with Ambient Lighting

I didn't really like the look of the dark walls next to the lamps in the screenshots of my previous post. There should be some diffuse light passing through the lampshade to reach the wall, and also indirect light reflected from the ceiling and top of the dresser or nightstand. The easiest fix was to add a small radius unshadowed light source inside the lampshade and hope that it was small enough that it didn't bleed through the wall onto objects in the adjacent room. Here is a screenshot of that third light source. It looks a bit odd due to the lack of a smooth transition between the top/middle/bottom lights, but I guess it's better than having no middle light.

Lamps now have a small unshadowed light source inside the lampshade that makes it appear to diffusely illuminate the wall around the lamp.

Bed Posts and Canopy Beds

So far my beds have used simple and plain geometry, with single cube bottom, legs, headboard, and foot board. I decided to add bed posts to some of them. Bed posts are randomly selected to be either square or cylindrical. Here is a bed with square posts.

Bed with square bed posts and a lamp on a nightstand next to it.

I took this a bit further and added top bars to some of the beds with posts, making them into canopy beds. So far I haven't added any canopy material, so they're bare wood. Cloth for things like canopies or window curtains is difficult to add. There are two problems. First, what geometry do I use? Cubes won't work here. This cloth needs to be curved into folds to make it look realistic, which would require a large number of triangles. Maybe that's okay for canopies, but there are far too many windows in a house to add high polygon count curtains to each one.

The second problem is that real cloth transmits some amount of light. It's not quite the same as making the material semi-transparent with an alpha value between 0.0 and 1.0. Instead, they need some sort of two-sided lighting model that includes transmission through the back/lit side, similar to tree leaves. This may require a custom shader. I'll have to get back to this problem later. For now, the canopy beds have no canopy and the windows have no curtains.

A large canopy bed that casts interesting shadows. I haven't added the canopy cloth/material yet.

Storage Room Shelves with Boxes

Storage rooms still looked a bit empty to me. I needed to add objects to the shelves, but what? I guess the only objects I currently have that are intended for storage rooms are the same wooden crates I placed on the floors. I decided to add some smaller crates randomly placed on the shelves. Some shelves have none, and others are full of them. I also added an occasional random office chair in the corner of the room (not pictured below).

Storage room with shelves on all walls and crates on the shelves and floor.

I would like to add cardboard boxes, but so far I haven't been able to create a convincing cardboard material. The don't really look like proper boxes if I make them a uniform tan color. I tried to find good box textures online, ones that had various box-like symbols, text, tape, etc. The problem here is that boxes generally don't look right if I use the same texture on all six cube sides. Do I need three different textures, one for the top and bottom, one for the sides, and another for the ends? Maybe, it's tough to justify adding three new textures and three new materials just for cardboard boxes. Maybe adding plastic storage bins would be easier.

I could also just add random room objects to the shelves: books, lamps, bottles (see below), pieces of wood, etc. I don't know how that would look. None of those are really items you see stacked on storage room shelves. Do I need to create several new objects that only appear on shelves in storage rooms?

Walled Stairs with Railings

Most office buildings have enclosed stairs. I hear that's related to fire safety, which I suppose makes sense. I changed my office building stairs so that they at least have walls on the sides. Walls are skipped when the stairs are up against an existing exterior wall of the building. There's still no stairwell door, but this is an improvement. Railings are connected to the walls of the stairs and continue to have vertical support poles when they fit. U-shaped stairs don't have vertical poles due to the complexity of adding them at the correct height. Here is an example of office stairs in a main hallway leading down to an open elevator. The dark spot on the ground is my shadow.

Office building walled stairs with railings. There's an open elevator at the bottom of the stairs.

Computer Keyboards

I added computer monitors to some desks a few weeks ago using the same 3D model as the TV. That's good, but monitors aren't usually placed by themselves (except for in the focus rooms where I work). Monitors are usually connected to a computer, and the computer is often connected to a keyboard and mouse. Keyboards are easy to draw, so I added them first. There are no cords, computers, or mice yet.

Desks with computer monitors now also have a computer keyboard. (That Windows desktop is a screenshot of my home computer's desktop.)

Maybe someone was using the monitor and keyboard with a laptop that's no longer on the desk. ... In that case, then why is the monitor showing a desktop? I don't know, I suppose the computer could be hidden behind the desk somewhere. I'll have to add one under the desk later. Maybe then I can add some desktop computers and keyboards to those storage shelves. I may even be able to get away with making them a single textured cube so that they're basically free to draw.

Colored House Walls

Paul pointed out that the contrast between building walls and trim is too low. The easiest way to fix that is to change the wall color, the trim color, or both. I've left some houses with white walls, but made others have light blue, green, pink, and peach colored walls. Some of these are shown in the screenshots below.

Railings with Balusters and Stair Overhangs

My railings and stairs were looking a bit plain. I was originally worried about having too many triangles for detailed stairs, so I tried to keep them minimal. I've made a lot of optimizations since then, including adding a class of "small" objects and materials that are only generated and drawn when the player is close to the building. This is intended for high detail objects with curved surfaces that can't easily be seen from a distance. This form of level-of-detail is what allows me to add so many different objects to thousands of buildings and still draw everything in realtime.

I split stairs into top and bottom cube parts, where the top is slightly darker in color and overhangs a bit. This makes it easier to tell where the tops of the individual steps are. I also added vertical cylinders for railing balusters to keep the little kids (or careless adults) from falling off the sides of the stairs. I placed one on each stair, which looks pretty good. I put another cylinder along the bottoms so that I can skip adding end cap circles to each baluster. These railings work as proper collision objects for the player as well, to avoid the frustration of unintentionally falling all the way to the bottom of the stairs when making a wrong move trying to reach the top floor.

Stairs with railings, balusters, and overhanging steps in a room with light green walls.

Dark Trim for Office Buildings 

I wanted to keep office building walls white because that's the most common color. My real life office (which I haven't been to since March) does have some colored walls, but they're mostly white. Instead of changing the wall colors to get good contrast, I'll change the trim color to dark gray. There, that looks much better.

Office buildings now have dark gray trim to contrast with the light colored walls, ceilings, and floors.

Maybe I should add trim along the ceilings as well like I have in houses? I haven't quite gotten the angled ceiling trim to meet properly at outside corners of hallways. You can see this on the corners of closets in houses as well, though it's difficult to spot. I think for now I'll add ceiling trim to smaller office buildings, but leave it out of larger buildings with intersecting hallways. That should help with polygon count as well since the largest buildings can have over a thousand rooms.

Reception Desks in Office Lobbies

Most large office buildings don't have their front doors open to a long hallway with rooms on each side. The doors open to a lobby instead. What better object to place in the lobby than a nice big reception desk. I can put one at each end of the hall/lobby to handle both doors.

I originally wanted to use the desk model I created in my earlier (hand made, not procedural) office building scene with the rounded corners created from cylinders. I attempted that, but I couldn't get the textures to line up using the texture coordinate system I implemented for building interiors. There's just no easy/automatic way to remove texture seams from the intersection of a cube and a cylinder. Oh well, I guess I'll just have to make the top surface out of cubes and settle for square corners for now.

An office building lobby reception desk.

Office Building Libraries

3DWorld already places libraries with up to eight bookcases on an upper floor of some large houses. I can reuse that code to turn offices into small libraries for office buildings as well. I need to make sure to limit the number of libraries, because all these books really add up for thousand room office buildings. This was an easy change and adds some variety to offices. Here's an example with a rectangular table. Some libraries have round tables.

A library inside an office building with many bookcases and a rectangular table in the center.

Bathroom Showers

My daughter asked me why there are no showers in the bathrooms of houses. That's a great suggestion! I can create a simple shower out of mostly cubes. I found a nice bathroom tile texture to use and got to work. My showers are always placed in an empty corner of the bathroom against two interior (windowless) walls. The back and floor are made of tile, with a shiny metal frame and two transparent glass panes on the outside. One of these panes is the door. One of the walls has the shower head, and there's a drain in the tile floor. That's a minimal design, but it works well enough.

A shower placed in the corner of a bathroom with light blue walls.

It took some effort to update the material drawing system so that I could draw transparent objects last, which is required for proper alpha blending. I did eventually get everything to work properly, even when viewed through a mirror.

This shower has an additional door handle, which I later realized was missing in the previous version.

A shower (with door handle added) placed in the corner of a bathroom with peach walls.

I also used that same tile texture for kitchen counter backsplashes. I only added these to interior walls because the windows on exterior walls are large enough that there's no exposed wall space for a backsplash.

Kitchen counter with a tile backsplash added to non-window wall sections.

Improved Light Culling

I was having lots of problems with room lighting, both fragment shader performance issues and light leaking though walls. I already have a system in place that performs ray casts in various directions to determine the bounds of each room light. This allowed me to do some light culling on the CPU side. Rays that hit room walls mean the light is confined to the room containing it, while rays that exit through doorways allow the light to influence other rooms. This lets me to use large light sources while constraining their areas of influence to only a few rooms. I store the computed bounding cube of each light in a texture and use it inside the fragment shader for light source culling.

It took me a few months to finally add fragment shader support for this. I was originally worried it would be very complex to pull this off, and would hurt framerate. In reality it only took about two hours, worked the first time, and actually improved draw time. I guess the ability to early exit out of lighting computations more than offsets the dynamic branching cost in the fragment shader.

This change allowed me to increase the radius/brightness and angle of upward pointing, unshadowed room lights without having to worry about light bleeding through walls into adjacent rooms. You can compare the two shower screenshots above and see that the lower one has more light on the upper walls. I was then able to go and re-tweak other lighting parameters to remove some of the annoying light leaking and unwanted shadows. This really makes a big difference when moving around the city as well because there is far less "popping" from shadow maps being switched on and off.

Bottles and Wine Racks

Paul suggested I add wine racks. I'm not sure how common wine racks are, and I definitely think I'm missing some other household objects that are far more common. I do have a very small wine rack built into a kitchen counter in my house. I guess I can add wine racks, but first I have to add wine bottles. Fortunately, a bottle is pretty easy to make by gluing spheres and cylinders together. I already have support for this. Here is a green bottle with a white cap and label. It's not a wine bottle, but that's fine, I can add other types of bottles on top of tables, desks, and kitchen counters.

A bottle of Sprite, 7Up, or maybe beer on a table.

Once I had a bottle, I could combine those with a modified bookcase to get a wine rack. Real wine racks normally are angled or have extra pieces so that the bottles can't roll around, but this is good enough for now. Maybe I can reuse this geometry for storage cubes later. I added wine racks to 75% of dining rooms, at most one per house. Here is one example.

A wine rack full of wine placed in a dining room with light green walls.

Wait! I can add these bottles to my storage room shelves as well. Now they sort of look like supermarket shelves where customers put their returned items back in the wrong place, or shelves in a recycling center. Or maybe the janitor is an alcoholic. Or the employees had a party down here.

Bottles next to boxes on storage room shelves. This room also has a spare chair against the wall.

Next, there are some improvements to cities. These were all pretty simple additions, though I had to reorganize the code somewhat to make some of them possible. Note that I had cars and pedestrians disabled in these screenshots because I wasn't testing those features and didn't want the added runtime of loading their models.

Roof Helipads

I decided it was easy to add helipad textures to the tops of some of the larger buildings that might be hospitals, etc. No, I haven't added helicopters yet! This was just a test screenshot. Helipads are normally much more sparse and you'll only see a few in each city on the taller buildings.

Testing how the city looks when all tall buildings have helipads on their roofs.

Tree Planters

Trees growing up through the sidewalk don't look quite right. There should at least be some dirt, even if it's a small square. Surrounding the dirt with a low concrete wall creates a nice, simple city tree planter. That looks much better than the bare trees.

Planters with dirt at the base of city trees placed in paved areas.

City Parks

3DWorld's procedural cities were full of buildings, but there was very little greenery apart from the trees planted between the buildings. It was suggested that I add parks, so I gave every city block a small chance of becoming a park. I replaced the concrete sidewalk texture with a new grass texture, disabled buildings, made the trees more irregularly placed, and removed the planters. Maybe I should use real 3D grass blades rather than a texture, but it's difficult to constrain grass to an exact area like this. Benches are good for parks as well as sidewalks so they could stay. Parks even show up in the overhead city map, along with individual trees placed along sidewalks. Here is an example of a park.

This city block is a park with grass, trees, and benches.

That's about all I can fit in this post. Next on the list are computers, more objects on storage room shelves, microwave ovens, window blinds (maybe), and whatever else I come up with in the next few weeks.


  1. The canopy beds and bed posts are a nice improvement. I'd take the polys out of the pillows and put them in the curtains, drapes, and canopy, but as you say the lighting is the real problem. Lighting from translucent (and all "soft") sources is tricky. Looking forward to seeing your clever solution!

    For a cardboard box, you could get tricky with the UV mapping. Or just use one that someone else made. Like this one right here! https://poly.google.com/view/9KZM2vx3muD
    Feel free to reach out if you want any help with 3D modeling and texturing. I know a thing or two.

    The new trim coloring does look nice. Good attention to detail in omitting it along drop ceilings. I don't think I've ever seen crown molding combined with an acoustic tile ceiling, or if I did it's vastly the minority of cases.

    Texture seams wouldn't be a problem if you used procedural 3D volumetric textures, but there's only so many things you can implement in one post!

    Backsplash and showers look nice. Simple and effective.

    Hey, bottles! One more item of clutter you can add. Would be neat if they were a variety of heights and widths, since they are just components assembled on the fly. Or maybe there is some subtle variation?

    Tree planters do look nice. I think the flat texture-only grass looks fine for parks, as they would get mowed frequently. You could add some bushes and sparse 3d grass to vacant lots though!

    Looking forward to seeing the helicopters as well. It gets better every post!

    1. Thanks for all the feedback! I was thinking that blinds would be easier than curtains, I just need to figure out how to get them to use different lighting models on the inside vs. outside.

      For cardboard boxes, I was trying to avoid having a separate model instance for each one. The crates I made are all merged into a single draw call for each building. Of course, since storage rooms are windowless, the existing wall occlusion culling may remove most of them. Or I could maybe merge the models, or use instancing.

      All bottles were originally meant to be wine bottles, which have a pretty standard size. But you're right, I can make the bottles on tables and shelves a variety of sizes for wine, beer, soda, etc. Maybe also a transparent bottle for water, if I can make that work properly.

    2. Oh, and the texture seams. Using a 3D volume texture would require switching shaders. This is expensive to do per-object, and also expensive to make multiple passes over all the buildings. Maybe I can queue these objects up like I do with the plants, which also use a different shader. I don't know, I want to avoid making it too complex. Right now I don't think it makes sense to add all of that just so that I can have rounded tables. I'll wait until I have other reasons to need it.

      (This is what happened with rotated objects. All of the cubes were previously axis aligned. Then yesterday I decided to add papers to desks, and I wanted them misaligned. So I added a feature that allows me to generate cube-shaped objects and then post-rotate them before merging with the room geometry.)

    3. I couldn't figure out how to download the texture for that poly.google box model, so I found my own texture. This one has the sides folded out into a cross shape like an environment map. I manually measured the pixel positions for each face corner in the texture and calculated my own texture coordinates for it to use with each face. Then I had to experiment with the faces to get all of the "this way up" symbols to point in the correct directions. That took over an hour, but now I have a six sided textured box (with normal map) that I can add to my storage rooms.