Sun shadows

Posted August 8th, 2013 by Tynan Sylvester

There are now shadows from the sun in the game. They fade in and out based on time of day, move across the ground, don’t shade indoors, come from people and small objects as well as large structures, project to varying heights, and change color slightly to complement the color of the sky.

This involved creating a whole system for roofing as well – it tracks which areas are roofed, shows you when you create a new roof, and collapses unsupported sections.

It’s always amazing how many times one simple system demands that you model something that seems almost unrelated. The logic of the world is very interconnected; it’s tough to simulate one thing without simulating the things it interacts with.

On the gameplay side, I’ve been working on the power system. So look forward to solar and geothermal plants, power surge and explosions, power cables, battery stacks, and solar flares to shut down your machines.

Daylight Twilight

6 Responses to “Sun shadows”

  1. Daniel

    Nice, shadows improves perception of the layout quite a bit.

  2. Chris

    Hi Tynan. Just found out about this game. Wow, looks so awesome!

    I’ve been dabbling with Unity now for a while and was intrigued to see that you are also using Unity to build this. I am building a top-down terraria-like 2D game in Unity and the prototype looks very similar in style to what you have created here. I know you are very busy, I hope you don’t mind if I pick your brain a little if you have time?

    1) Is your tile mesh flat (one z layer) or do you have multiple z-levels?
    2) Lighting: for the shadows you explain in this post, did you a) use a unity directional light to produce these or are you b) generating them with code?
    3) If ‘a’, I would assume the tile mesh would need to have multiple z layers with unseen faces (because of the orthographic camera) facing in the x and y directions that are used to project the shadows based on the direction of the directional light? If ‘b’, are you using a custom shader or something?
    4) For the point lighting (on the ground and within buildings), are you using unity point lights? If so, how are you preventing the light from penetrating through the walls and how are you causing the light to reflect only off of the inner side of the walls if you are using flat tiles (or are they angled or something)?

    I’m interested in your design and lighting techniques as I am trying to achieve this same level of lighting in a 2D game written in a 3D IDE, and I would like to get started down the right path.

    You can always reply to me via email if you prefer. I can always show you some screenshots/video of what I’ve got so far.

    Thanks!

    p.s. Just picked up your book too!


  3. 1. There are no z-levels.

    2. The shadows are done with very little help from Unity. They’re generated with code.

    3. They’re a material with a vertex shader that moves some of the vertices (determined by their vertex color alpha) some distance along the sun shadow vector, which is updated globally by the CPU every frame. This has the effect of stretching the shadow tip vertices (A>0) away from the shadow base vertices (A=0). To make several apparent object heights, I set the shadow tip vertex alphas to be different; this changes how far the shadows stretch. The sun shadow mesh only generates along the bottom and side edges of objects, because these shadows never stretch upward.

    4. Point lights propagate their light through a grid and around corners. They find the path distance to nearby squares using Djikstra’s algorithm, which is how the light can spill around corners while attenuating nicely. They attenuate according to a mixed function that combines linear and quadratic falloff. These glow values are used for various gameplay purposes.

    Once that’s done, another system takes those square glow values and applies them to the vertex colors on a tesselated sheet that goes over the entire map (the sheet is cut up so it can be updated in pieces). This sheet carries a shader that multiplies whatever is under it by its vertex color if it has one. If not, it multiplies by the global sunlight color.

    There are no “real” lights in the entire game.

    That’s the real quick explanation; there are lots of fun details to work out. In general the lighting system is actually at least four separate systems: The sun shadows, the edge shadow mesh (fake SSAO), the local lighting overlay, and the shadow/weather stencil, which is used to make sun shadows and weather not render indoors.

  4. Chris

    Thanks for the detailed reply Tynan!

    1. No z-levels. Can I assume that you are only speaking for terrain tiles? Are you representing other objects (rocks, trees, bushes, furniture, characters and other objects etc..) on a z-level closer to the camera?

    1a. Are you using box or capsole coliders on your objects to detect collision and/or trigger triggers, or are you also using your own method for collision detection?

    2. Curious, why did you choose to generate your own lights and 2D shadows instead of using the built in 3D lighting features in Unity? (i.e. Performance, control, avoiding 3D-to-2D gymnastics, etc..)

    3. Very interesting technique. So if I understand correctly, you have a script attached to most above-ground objects that generates the shadow mesh you described when the object is instantiated. The sun shadow mesh is generated using vertices that correspond to the bottom and side edges of the object mesh (4 vertices per edge, 2 shadow base and 2 shadow tip). The quads representing each shadow-casting edge are stretched along the sun shadow vector by moving the shadow tip vectors according to the vertex color alpha value on the shadow tip vectors. This is performed by a custom vertex shader on a material applied to the sun shadow mesh. The sun shadow vector is updated in real-time by the CPU.

    3a. I also notice that you have a slight ‘border shadow’ surrounding some of the above-ground objects and structures. Is this the edge shadow mesh (fake SSAO) you mentioned? Are you using a similar method for generating this shadow by creating another shadow mesh slightly larger than the object? (The shadow looks rounded and gradient, are you using a fragment shader?)

    4. So, the light may spill (crawl) and attenuate around a corner to a tile if the tile falls within the calculated light distance (using Djikstra’s algorithm)? Are you iterating for the distance of nearby tiles as a unit (quads in the mesh) or individual vertex distances? Are you adjusting vertex colors values based on light distances/color and you using a fragment shader on a material to produce the effect?

    So, your point lights are just game objects with a custom script that manipulates vertex color values on the tesselated sheet?

    Ok, so the tesselated (love that word) sheet. Is this is a texture-less mesh composed of a grid of quads, broken into chunks (of say 16×16 quads each)? Have you written a shader that can multiply the color values of all vertices below it along the same z axis? I’m relatively new to shaders, I didn’t know that you could access and modify vertices outside of the current vertex being processed?

    Thank you again for taking time to answer my newb questions. I find this subject fascinating and challenging. Your descriptions are very concise and informative and very helpful to me.


  5. The depth you’re looking for is a beyond article comments at this point. Maybe I’ll write a full article about this some day; at the moment I have to work on the game sadly.

    In general, though, there are no gameobjects or unity components at all in RimWorld. I did a custom system for tracking all objects, ticking them, and doing all object interactions because Unity can’t do what I needed (thousands of lightweight objects in a tile-based world). There are no scripts attached to anything; it’s all in the RW codebase.

  6. Chris

    Tynan, thanks for the time you took to answer my questions. Now your got me totally intrigued. Best if luck to you.