|||| 8 - 15 nov 2014 ||||
7DFPS ...
Duality: Shader Magic
For this year's 7HFPS challenge I made a stealth game with two worlds entangled by the portal in your hand. I thought I should share some of the shader magic that made it happen. Note: This post is all about my 7FPS entry, Duality. Maybe you want to play it before reading on? http://itch.io/jam/7hfps/rate/13201 Duality is all about jumping between two very similar worlds that contain different cover to hide behind. At any point in time, there is always an active world in which all geometry is collidable and can be used for cover, and a passive world that can be swapped between. The passive world can be seen through an item that the player carries in their hand, let's call it the portal window. To make the game play as simply as possible, the portal is rendered similar to the ones in Portal and Quake, which I find to be a super neat effect. In addition to eye-candy, the appearance of the portal window gives the player some interesting tools, such as seeing through the cover they are currently hiding behind. To achieve this effect, a few custom shaders and some camera rendering order tricks were used, and I wanted to share exactly how the effect was implemented in Unity 3D. The two worlds, I call the orangy one the bright world, and the inversed one the dark world. Notice how the bridge is split between the two worlds. Using multiple cameras in Unity 3D Unity allows multiple cameras in one scene that can render in an order specified by a depth property. The higher the depth, the later the camera will draw relative to other cameras in the scene. Each camera has its own list of culling layers, that is layers that should and should not be rendered by the particular camera. In Duality, there are three main layers:
  • Default - Geometry that exists in both worlds
  • WorldBright - Geometry that only exists in the bright world
  • WorldDark - Geometry that only exists in the dark world
Each world is rendered by a separate camera, by setting it's culling layer to Default and either WorldBright or WorldDark. Finally, each camera's clear flags can be set to the following things:
  • Skybox - Draw a background skybox
  • Solid color - Draw a background color
  • Depth only - Only clear the depth buffer
  • Don't clear - Just render new stuff on top of old stuff
Most of these are fairly self explanatory, with whatever's been rendered before being replaced by a skybox, a color, or not at all. One option surely stands out here, what on earth is clearing the depth buffer? The Color and Depth Buffer Let's start with the color buffer, which is essentially the image you're drawing on. The scene is incrementally drawn to it, triangle by triangle, to eventually become the final image that is then displayed on the screen. But what on earth is the depth buffer? The depth buffer, or Z-buffer, is simply put a way to measure how close a pixel is to the camera. Like the color buffer, the depth buffer can also be visualized as an image: Notice how it's monochrome? A white color means that something is close to the camera, whereas a black color means something is way off in the distance. These values is the way your graphics card decides what objects are drawn in front of others, since faraway objects should be occluded by close ones, a process known as Z-test. In most cases, the Z-test is automatically done when rendering objects, and this is something we can exploit to mask out certain areas of the image, such as the portal window. Glimpsing into another world, depth buffer style To achieve the portal effect, we first draw the passive world, in this case by using the bright world camera. Then, using a separate window mask camera, we clear the depth buffer and draw the portal window, but only to the depth buffer. This can be achieved by using the depth mask shader available here: http://wiki.unity3d.com/index.php?title=DepthMask. As you can see at stage 3 in the image below, this results in the portal window being drawn super close to the camera. After that is done, we draw the active world using the dark world camera with the don't clear flag. This will ensure that we are still using the depth buffer with the window being super close to the camera. Because nothing will occlude the portal window, nothing will be drawn over it in the color buffer. We get that super cool portal effect! Finally, using the same principle as above, I render a first person hand occluded by the world portal using one more camera. This ensures that the hand does not clip through level geometry. A summary of the stages:
  1. Draw the passive world
  2. Clear the depth buffer
  3. Draw the portal window to the depth buffer
  4. Draw the active world
  5. Clear the depth buffer
  6. Draw the portal window to the depth buffer
  7. Draw the first person hands
Here's a list of the cameras and their properties I use to achieve this in the order they are rendered:
  • Bright world camera: depth=0, clearflag=skybox
  • Window mask camera: depth=1, clearflag=depth only
  • Dark world camera: depth=2, clearflag=don't clear
  • Window mask camera 2: depth=3, clearflag=depth only
  • First person hands camera: depth=4, clearflag=don't clear
And the swapping? The swapping mechanic is simply done by swapping the depth and clearflag properties of the bright and dark world cameras. Using triggers, I also check so that the player isn't stuck inside geometry in the passive world before I allow the swap. Final remarks
  • To ensure that the window depth mask gets super close to the camera, I use the offset shader property defined here: http://docs.unity3d.com/Manual/SL-CullAndDepth.html
  • The NPCs only raycast using the default and the active world layer. This means that we can reuse the layers we defined for culling earlier!
Hope this inspired some of you to try out crazy shader magic for your 7DFPS games!
liked by Hisashimaru
Fantastic writeup! I was very impressed by the effect when I played it. Great to see how it was done.
Hisashimaru says...
Nice! Thanks for sharing the shader magic.
7DFPS brought to you by Sven Bergstrom & Sos Sosowski & Jan Willem Nijman | Logo by Cactus Follow news and info about 7DFPS on twitter Games hosted on itch.io