The history of the development of The Light Remake. Part 1





Greetings reader! My name is Sergey, I am an indie computer game developer. I already have several indie projects in my portfolio, some of which were self-released on Steam. The game Light or The Light, released in 2012, was my first pen and pioneer in the world of game development. The project was distributed free of charge, but the public reaction and feedback from the players gave me serious motivation for further work. “The Light” has become for me something like a philosophical parable about humanity and its fate. The plot is abstract and does not pursue any specific goals, it is just an opportunity to reflect on a broad topic.



Since the original game never made it to the release on Steam, I decided to correct this omission and in June 2019 began a large-scale rework of the project so that it could match its gameplay and visually and be considered a full-fledged game.







What are we talking about?



This article will focus specifically on the "Light" project and on the work done related to porting the game to a newer version of the Unity engine. This implies a lot of technical nuances. The article will be presented in two parts.



Part 1



The original version of the game was built on Unity 4.2 back in 2012. Then in my information field there was still no talk about PBR materials, reflex tests and other methods that are currently relevant. Basic shaders in Unity were very simple and not very realistic. Of course, many additional elements could be added, such as fresnel reflections, but then shader programming was a mystery for me. The main shaders used were Normal Bumped Specular and AlphaTest diffuse for objects with transparency (tree leaves, curly metal grilles).



There were no riltime shadows, almost all the lighting was baked into texture and the resolution of these shadows was very low in places. Baking the light on top of everything else reduced the bump effect from the Bumped Specular shader to near zero, making the objects look more like flat cardboard on closer inspection.



The picture was significantly transformed by post-effects on the camera, in particular Bloom and Color Correction. They added color and variety, although they were too intrusive in places.







At the time of creating this project, I still had vague ideas about optimization, batching (combining into one mesh), occlusion - culling (cutting off invisible objects), etc. But due to the fact that technically the scene was not loaded with something complicated, did not have riltime lighting, grass with an alpha channel, etc. - the game came out quite optimized and worked well on weak hardware, giving a pleasant picture for 2012.



After 7 years, you look at any of your creations with different eyes. Technologies have advanced a lot, tools have changed, the latest versions of the Unity engine are significantly different from their predecessors. In connection with all of the above, the transfer of the project from version 4 to version 2017 (I decided to stay on it for a number of reasons) is a rather long and painstaking work. In addition, given the fact that the original project could not be called a full-fledged game (most of the actions in the game were carried out using one script with a trigger function), it was necessary to write all the logic from scratch, interaction with objects, inventory, menu system, system saves, achievements, settings, etc. In general, a very large-scale amount of work awaited me!



Start. Shaders and light



The first thing that is demoralizing when opening a project in a new engine is that the entire visual is broken. Many shaders and post-effects that were imported from third-party sources stop working. Those shaders that continue to work look slightly different. The lighting in the scene changes, lightmaps fly off, everything needs to be recalculated.







New Standart



It was decided to start by replacing old shaders with a new PBR Standart. In the previous articleon the 35MM development topic, I have already mentioned a new type of PBR shaders (Physically Based Rendering), which implies correct physical rendering. The new material Standard no longer has the usual o.Gloss and o.Specular from the previous versions, here we have a metallicity map (o.Metallic) and Smoothness. Also, there are more slots for textures of various categories. For example, we have the opportunity to add an Occlusion map for soft shading. This effect is very useful because it allows you to emphasize volume and shading in areas of the model where there is less light. Without this map and lightmap - the texture, objects look flat and unrealistic.







There is the same Detail Albedo and Detail Normal - these maps add detail to our existing textures, for example, you can place an additional normal map with small cracks and tint it by adjusting the degree of influence. As a result, the original low-resolution bump texture will appear more detailed. For some materials, I used this technique, while others left unchanged.







Vegetation



After replacing the basic materials, I moved on to vegetation. The original build of the game used a standard Alpha Test shader without a speculator and a Normal Map. Of course, this state of affairs in 2019 did not suit me. You could buy or find a ready-made solution on the Internet, there are whole packs with shaders and ready-made models with many features, imitation of swaying in the wind, etc. But traditionally, I try to figure out such issues myself and experiment. This is something of a sporting interest. My new Vegetable shader is based on the Toon Ramp reference from the Unity tutorial.



The presented LightingRamp lighting model allowed to draw the silhouette of shadows both on the lit side of the polygon and on the opposite. This mimicked the ability of foliage to transmit light - translucency.







A similar shader can also be used to render transparent fabrics such as curtains.







Unfortunately, in this mode, the automatic rendering of the back side of the polygon using the Cull Off method did not give a very correct result, so the Backface had to be added manually in the editor. Next, the Normal and Specular map was added to the shader. I could not use the PBR shader lighting model and connect the Reflection Probes, but with the help of masks and the Emission map I added Ambient Occlusion imitation. And finally, it was extremely necessary to revive the whole thing and give the vegetation movement. With the Vertex function and the same mask, the desired areas of the foliage plates come to life. The vertex offset is based on the example Normal Extrusion with Vertex Modifier from the Unity manual .







You can set the speed and amplitude. By the way, in these experiments the new function for managing shaders' global variables came in handy ... You can easily assign any variable or texture to all necessary shaders from the script.







Shine



The approach to the very lighting device in the scene has been significantly changed. In the original, all light was baked into lightmaps, and the rendering worked in Forward mode. In the remake, an important aspect was the use of real-time light to be able to change the time of day. The render mode has been changed to Deferred. The main light (sun) was used in Mixed mode, directional lighting and shadows were drawn in real time, and global illumination was baked into textures. This made it possible to change the light level and direction, but at the same time retained the effect of soft global illumination and reflections, which always give the picture extra realism. Lightmap textures were not baked for all objects, mainly for large and more or less simple ones. Small and difficult to create sweeps remained dynamic and were highlighted either with light probes,reflexion tests, or just basic ambient light (which is specified in the main lighting settings). Baking was done with a Progressive lightmapper.















Day and Night



To change the time of day, a skybox shader was created with two textures: day and night. Both options were supplemented with an offset function to simulate cloud movement. The night sky textures and anomalous glow for the scene of the flight of combat missiles were also added. All this disgrace was controlled by a special script that rotated the skybox and the sun, at certain positions of the sun, the color of the skybox changed, the textures were mixed using the Lerp method and the night sky and stars were smoothly revealed. The effect of the glow of the sky after launching the missiles was also added on a separate layer.







For optimization at the minimum light level (about 0.1-0.2), shadows were smoothly turned off. To change the time, it was necessary to take into account a couple more important points. The stage was attended by particles - poplar fluff. Their material did not react to light (at that time I had not yet found a suitable shader), so the color of the fluff material had to be changed with a script depending on the time of day.



Also, when changing the time, the script adjusted the desired color for the Global Fog post-effect, since in the daytime the fog should look like a light gray-blue haze, and at night it should have a darker, almost black tone.







Models and new content



The main part of the location and basic models: the model of the main building, trees, benches, lampposts remained original. Many other props have been completely or partially redone. The model of the kerosene lamp was redesigned, a new diesel generator with separate animatable elements was created, a new model of a cinema projector, etc.















Nearby buildings that previously could not be entered have been redesigned and made available to the player. The interior layout of the main building was also changed. In addition to reworking the old content, new ones were also added, for example, a PAZ bus model, a gaming machine, a soda vending machine, a puzzle - an electrical panel, a hermetic door in the basement, inventory items, etc. Some of the content was created independently, while some of the models were outsourced.







For almost all objects for which lightmaps were baked, a UV2 scan was manually created, with the most optimal arrangement of fragments for economy. For example, for buildings, all hard-to-reach areas (for example, ceilings and walls of upper floors), which the player will practically not see, took up minimal space on the scan.



Effects and custom shaders.



Water







With each new project, I want to take into account the nuances to which we previously closed our eyes. The overall picture is created from separate details and even not very important elements can affect perception. Plus, it's kind of a challenge - every time to improve something with which I've already worked. It is especially satisfying to find solutions on your own, rather than using ready-made assets. In all previous projects, I have worked with water. As a rule, it was a fairly simple vertex shader that did not react to the player and the lighting in the scene. It was based on Mirror Reflection from the Unity Wiki, which was an example of a mirror implementation.







The surface was dynamic due to the displacement of the vertices (imitation of waves), but always too monotonous and somewhat boring. Through trial and error for the Light project, I managed to create a Surface version of a similar shader, which, like a sample, can: receive a specular reflection texture from a Mirror script, deform vertexes to simulate waves, write a screen into a Grabpass texture to create refractions underwater, have soft Alpha edges when intersecting with geometry (depth fade). Also, for the effect of reaction to the player, information about the coordinates of the player's position is passed to the shader. A dynamic spot is drawn at the coordinate point, which simulates splashes when the character is directly in the water column. The most important thing that the Surface shader allowed was to receive lighting from any light source. Thus, the water seems more tangible,volumetric substance and allows you to play with it using lighting effects.







Caustics



Another important detail was the creation of the caustic effect - light reflections falling on the surface. In the darkness of the water-flooded basement tunnels, this technique was essential. The effect was created using a Projector object and a glowing material with an animated texture. The shader mixes 2 caustic textures, which are displaced in different directions, resulting in a dynamic effect. In most of my shaders, to save money, I usually use texture masks containing 4 channels (RGBA) - each for a specific purpose. The R channel can have a basic texture, the G channel has softer light spots, and the B channel has a noise texture to distort the caustic pattern.







The surface of the water has a collider with a special tag. As soon as the character enters the water, the script detects this using the raycast method, and smoothly turns on the caustic. In this case, several conditions are also spelled out, such as the presence of a lighter, flashlight or kerosene lamp nearby in the hands.







Particles



An interesting effect has been implemented for small particles in the air that are visible in the light. The idea itself was inspired by the Homesick project, in which I once saw a similar thing. At a certain distance, the particles have a regular texture, something like the texture of poplar fluff. But as the camera approaches, the texture in the shader smoothly changes to the second version, reminiscent of the defocus effect. In dynamics, it looks quite nice and the particles seem more tangible.







As noted above, almost all base surfaces in the scene use a modified version of the Standart PBR shader. Some have added masks with different spot options for additional detail on Albedo textures. Added a spot mask to simulate rain puddles on surfaces like asphalt or paving stones. The floor tile shader in the main building has an additional map for reflections, which are rendered by the Mirror script similarly to a water surface. For optimization, only basic and large objects are included in the rendering of reflections, and at a certain distance, the rendering of reflections is turned off.



Fog / smoke



An important detail that I wanted to take into account when creating the remake is the dynamic fog and the effect of lighting on it, in particular the light of a lantern. In earlier works, the main shader for smoke and fog was the standard Particle blend and its modifications. This is a vertex shader that is good in terms of performance but does not react to light at all. The display of fog with such a material will always be the same in the shade and in the light, it cannot be illuminated with a lantern and it does not always look attractive and natural. In the underground catacombs I had conceived, the haze was supposed to be highlighted by a lantern from the darkness, emphasized by dynamic lighting. To solve this problem, a shader from a free Asset was used . The shader cannot boast of high performance, but visually it coped with its task perfectly.







Decals



Another important point was finding a suitable shader for the decals. The game planned a lot of all kinds of graffiti, which were most often installed using the old decal system plugin (since the old version of Unity 4.6). For graffiti, inscriptions and signs, a large atlas of 4096 by 4096 was created. Images on decals are translucent and rendered in Transparent alpha mode, so in the case of dynamic lighting they do not always look adequate, since the standard alpha shader is not able to receive shadows.



A special two-pass shader was created to solve the problem. The first pass draws the dark parts of the image, the second the light parts using the Blend DstColor One blending method. I may not quite correctly understand the drawing method, so I will refrain from detailed explanations, but I managed to achieve the desired result: in the shadow, the image is buried in darkness, and in the light it appears and even plays with colors. The two-pass shader had no effect on performance, since the Decal system initially combines all decals into one large mesh. Probably there are better ways, but this option suits me perfectly.











Earth / Grass



Another two-pass shader was created for the ground surface. The very territory at the location is made by geometry in 3d max. But for rendering the grass, an additional terrain was created. I had to manually adjust the terrain heights in the right places to match the terrain. Then the rendering of the terrain itself was turned off and several variants of the prepared grass meshes were applied to the surface with a brush. The native grass on the terrain is terribly heavy on the scene in terms of performance, since it is not batchable and each element creates an additional draw call (maybe someone will correct me if I'm wrong), but this method is extremely convenient in terms of work. The density of the grass is not high and in those places where it is absent, the simplicity of the surface is very striking. In connection with this, as an experiment, I used a two-pass shader,which was mentioned above. The first pass is a regular opaque geometry, the second is a slightly raised duplicate of the surface in the Alpha test mode. Roughly speaking, a copy of the ground / grass surface is additionally drawn with hard transparency and vertices shifted upward. This method complements the flat grass somewhat and creates the illusion of additional detail and volume.







Light outside the window



One-sided translucent sprites were placed in some places outside the windows of the main building to simulate highlights. Also, the Global Fog post-effect has a bright white fog parameter, which is activated as the hero moves deeper into the building. That is, when the character is in the dark corridors of the case, the environment outside smoothly acquires a whitish glow. This creates a rather beautiful artistic effect and allows you to emphasize the near and far plan.







This concludes the first part of the article. Link to the second part. Thank you all for your attention!



All Articles