[Unity3D] Use Shader and change Mesh to achieve the dynamic shadow effect of 2D games, unity3dmesh
Recently I saw a very interesting puzzle game. It was a thief who went into the house to steal things,
In fact, there are already a lot of such games on the market. What attracts me is the flashlight-like effect,
There is a shadow in the blocked area. It's a bit similar to the war fog in strategy games.
After racking your brains for a day, you can finally achieve similar results.
Technologies used to achieve this effect:
1. Shader achieves transparent camera penetration effect (illumination implementation)
2. dynamically change the mesh in the Code (implementation of shadow shapes)
Do not talk nonsense.
Bytes --------------------------------------------------------------------------------------------
After careful observation, we can see that the place where the light is not shining is gray, the place where the light is colored, and the place where the light is shining,
It not only shows the effect (colored ground) after being illuminated, but also displays some items, such as keys and enemies.
It can be inferred that this scenario should be composed of two layers. One layer is under the color ground, and the enemy and other items to be hidden,
On the other layer, the map is not illuminated, and then crops the map based on the light.
The upper layer is cropped to expose the lower part to achieve the effect of "illuminating.
Therefore, we should first consider the following scenarios:
1. the question is about 2D games. I have made 3D games here. In fact, they are all called. The final visual view is actually the same as that of 2D games, this is to help you identify my hierarchy.
Up is the upper layer. In fact, there is nothing, so there is only one texture that is not illuminated (I am lazy here and I replaced it with a gray one)
Down is the lower layer, where almost all elements are filled, players, enemies, and walls.
2. Next, put various types of objects in the corresponding layer to facilitate camera screening (the layer here is different from the layer mentioned above, and the layer here is defined in Unity)
Here I want to facilitate your identification and set a layer for a type of object. The actual operation will certainly not be so tedious and unnecessary.
3. Create two cameras to display their respective layers.
(Ignore Mask first. I will talk about it later)
If the settings are correct, your display should be the same as mine, and set the Up camera's Depth level to a high point so that it is displayed before the camera goes Down.
4. blocks the upper layer and displays the lower layer.
That's how it works.
How to block it? I first thought of the UGUI Mask component. Later I thought that this Mask is a fixed image and can only display fixed occlusion (although it can be changed dynamically, It is troublesome ),
Later I thought, we would like to block the object through the mesh. After all, it is easy to dynamically change the shape of the mesh, and it can be easily achieved through the Shader.
Write such a Shader first as follows:
Shader "Masked/Mask" { SubShader { // Render the mask after regular geometry, but before masked geometry and // transparent things. Tags {"Queue" = "Geometry+10" } // Don't draw in the RGBA channels; just the depth buffer ColorMask 0 ZWrite On // Do nothing specific in the pass: Pass {} }}
If you understand Shader, you can understand it at a Glance. It doesn't matter if you don't understand it. Copy it directly.
It actually turns your mesh into a shelter. When the camera sees the mesh, it will directly penetrate the past and directly see the background.
Then add a GameObject, add the MeshFilter and MeshRender components, and assign the Shader as the Mask.
If the current Mesh of your Mask is not empty, you can intuitively see that it is not rendered by the camera,
Directly into a penetrating hole:
It is not only transparent, but also a cross-dimensional hole. Even the things behind it cannot be displayed, the background is displayed directly.
It directly penetrated into the background, so we could not see the underlying layer. What should we do with it?
Yes, it Only penetrated the current camera to the background, so we set the Clear Flags of the current camera to "Depth Only", so that the first camera
The background of the second camera is displayed. In this way, we can easily achieve the penetration we want.
5. How can we achieve dynamic illumination changes when occlusion is implemented?
Here I use RayCast, which uses the main character as the origin and emits a certain amount of rays to the surrounding area to simulate the illumination,
Then combine the points detected by rays into the mesh of the blocks mentioned above. In the end, we can achieve the desired effect.
Using UnityEngine; public class FOVMesh: MonoBehaviour {// player public GameObject playerGameObject; // illumination radius public float range = 3; // illumination quality, the lower the value, the more rays that radiate to the surrounding area, the better the effect, but the lower the performance. public int levelOfDetails = 1; // the object that receives the Light Shadow. public LayerMask [] layerMask; private Vector3 direction; private int index = 0; private int triIndex = 0; private int row id = 1; private float width; private GameObject go; private GameObject pGo; private Mesh mesh; private Vector3 worldPos; private Vector3 [] verts; private int [] tris; private Vector2 [] uvs; private GameObject didHit; private int mask; // Code that runs on entering the state. public void Start () {go = gameObject; pGo = playerGameObject; mesh = new Mesh (); go. getComponent <MeshFilter> (). mesh = mesh; fine d = levelOfDetails; width = range; // If loa is 1, 360 rays are emitted as light, then there are 360 vertices plus a center verts = new Vector3 [(360/levels of detail) + 1]; // each two vertices form a triangle with the center of the circle, therefore, the number of triangles is set to 3 tris = new int [(360/levels) * 3]; uvs = new Vector2 [verts. length]; for (int I = 0; I <layerMask. length; ++ I) {mask | = layerMask [I] ;}// Code that runs every frame. public void Update () {index = 0; triIndex = 0; worldPos = pGo. transform. position; verts [index] = worldPos; index ++; for (var a = 0; a <360; a + = layd) {var direction = new Vector3 (Mathf. sin (Mathf. deg2Rad * a), 0, Mathf. cos (Mathf. deg2Rad * a); direction = direction * width; RaycastHit hit; if (Physics. raycast (worldPos, direction, out hit, width, mask) {// if it is under radiation detection, verts [index] = new Vector3 (hit. point. x, hit. point. y, hit. point. z);} else {// otherwise, use the end of the ray as the vertex of the grid. verts [index] = new Vector3 (direction. x + worldPos. x, worldPos. y, direction. z + worldPos. z);} index ++;} // triangle Combination Based on the mesh vertices (var I = 1; I <(360/levels); I ++) {tris [triIndex] = 0; tris [triIndex + 1] = I; tris [triIndex + 2] = I + 1; triIndex + = 3 ;} tris [(360/levels of detail) * 3)-3] = 0; tris [(360/levels of detail) * 3)-2] = 360/levels of detail; tris [(360/levels) * 3)-1] = 1; // mesh texture int j = 0; while (j <uvs. length) {uvs [j] = new Vector2 (verts [j]. x, verts [j]. z); j ++;} // re-combine the mesh. clear (); mesh. vertices = verts; mesh. triangles = tris; mesh. uv = uvs; mesh. recalculateNormals ();}}
Final Effect
Source code:
Click here to download it. You are welcome to reprint it. For more information, see the source.
Okay, just one hour after I wrote this post, I had a 2D pseudo shadow on Baidu... Then I found a theme...
A similar plug-in has been available for a long time, and the effect is very strong. The implementation method is similar to mine, but it can also realize the strength of light and shade,
Bright in the dark near area in the distance.
Http://www.narkii.com/club/thread-358505-1.html
There is also a very embarrassing foreign writing:
Http://blog.jobbole.com/89193/