- Home /
Stop light passing through objects
I'm working from a completely 2D top-down perspective. I have a basic understanding of how lighting works in Unity, and I want to have a light system where the light doesn't go through walls (via using some 3D objects to block 3D light onto a 2D plane). However, this is what's happening:
I've got my 2D map layer set to "Default", and created a point light which only applies to that layer (through the culling mask). I'm trying to effectively block the light from passing into certain places, and after seeing this answer, I tried replicating it, but I can't get that approach to work. I created a 3D cube and placed it into the 2D map (it clips through the map so there definitely shouldn't be any chance of the light slipping under a small gap); the cube is on the same layer as the rest of the map and set to cast and receive shadows (as is the underlying map), I also tried a variety of materials for the cube (e.g. Default-Material, Default-Diffuse) but none of them seemed to do anything. I also tried changing the size and position of the cube and light, to no avail. Having the cube and map on different layers didn't work either.
I suspect I've probably got something set incorrectly somewhere in the inspector, or I'm missing something obvious (note that the lights are set to give hard shadows), but I don't know what it could be. Or maybe I'm approaching this completely wrong and there's a better way to achieve the desired effect.
The short answer is that you need the wall to use a shader which receives light and casts a shadow, then your environment needs a shader which will at least be able to read and receive shadow data cast from other objects (e.g. the wall).
In other words, an object will only be able to cast a shadow if it is able to do so and the object it's casting shadows onto is capable of receiving them.
I think this is what I currently have, see:
So the wall is receiving light, and its "cast shadows" is on.
And the floor (i.e. the background map) is set to receive shadows.
Both are using the Sprites/Diffuse (or Default-diffuse but it made no difference) shader, which I believe should have the desired effect in this case, but maybe I'm wrong?
One thing I noticed while messing around with shaders was that changing the material of some parts of the map (i.e. its layers) will make light apply to only that part and not others. So I can envision a really tedious and probably bad solution of just making different parts of the floor be on different layers so that light only applies to whichever layer it's on, but this could get really convoluted and hard to maintain later. I'll use it as a last resort.
You could script your shadows similarly to this demo I made https://nkoeppel.itch.io/lighting-demo, it creates a mask by detecting polygon collider edges. It's definitely a different approach than the path you're currently on but I thought I'd throw the idea out there.
I don't know how I'd go about doing this. Does it use raycasting? Is it expensive?
Out of interest, here's the "last resort" approach of making different parts of the floor be on different layers.
So the light on the bottom only applies to the "Floor1" layer. However, this means I'd have to make other lights (and maybe other layers) to get everything to display how I want it.
Although this "works" imo it's not a very good solution (e.g. because the player's layer and objects on other layers won't be affected by it without some convoluted tweaking).
hey how's game doing? demo? :) @Nephtis (btw the lightning is the only real pain it the arse whatsoever at least for me,..at first i thought code'll be the biggest one, yeah well...cuz i am having issues myself,i can use 256x256 texs,like duke nukem and baked lights stilll lag,..wish i could know why though...J
Hey there, sorry for the late reply. I will release a demo at some point, but probably not for a while - I work full-time so I can only work on my game in the evenings and weekends. I'm afraid I don't know much about Unity's lighting, it's just something I stuck in without really thinking - I have noticed that having lots of point lights in a scene can cause some lag, so I think generally the fewer lights you can get away with the better, or at least have some sort of rendering system where you only turn on lights that are currently in the player's view. For example, in my 2D top-down game, I could have a trigger collider attached to the player that's slightly bigger than the screen's view, and then only turn on lights that are within that view (so lights would also have trigger colliders, and then on the player's collider I'd say: OnTriggerEnter, turn on light, OnTriggerExit, turn off light).
This is an interesting issue, and while in Unity 4 the only approach was to put objects to different layers, since Unity 5 objects can definitely correctly block lights.
So I am not sure what the issue is... the gray floor element is a 3D object right now, right? Are your walls 3D objects as well with $$anonymous$$eshRenderer, or are they using SpriteRenderers? There was a question recently about SpriteRenderers to cast and receive shadows. Basically you have to a material that uses a shader with shadows (e.g. Default-diffuse), and you also need to go into debug mode with the inspector and manually enable cast shadows for the SpriteRenderer component.
This forum thread is also about casting shadows for sprites: https://forum.unity.com/threads/sprite-receive-shadow.357705/#post-2319797
If you have more info, I'd be glad to help!
Hi, I haven't looked at this for a while, I don't really have more info other than what's already been posted, but I'll give you as much other info as I can:
Basically since this is a 2D game, I have an object that is essentially "the world" viewed from above (FYI the "gray floor" is actually intended to just be a thing that stops light, but yes it is a 3D object sort of "embedded" into the 2D background). The background object consists of several sub-layers - specifically, I'm using Tiled2Unity to create maps in an editor and then import them into Unity (because imo it's easier than trying to do it in Unity). The background object has a mesh renderer component (NOT a sprite renderer), which has "Receive Shadows" ticked, and uses the "Default/Diffuse" shader.
Out of interest I tried creating a regular plane, putting a cube and a point light on it in the same way, and it still did the same thing, so I must be missing something in how I'm setting this all up. I did some quick reading around Unity's lighting just now, and it seems that lights don't care about geometry in their path, so you have to explicitly make shadows, which are like a texture that gets overlaid? But still, I would've thought that an obstacle with "Cast shadows: On" and a floor with "Receive shadows: On" (or two-sided) would produce the desired effect, but I guess not. The "Cast shadows" only seems to apply to the cube itself (because its other side is dark), and not the plane. (I would upload an image but for some reason I can't, I'll try later).
Sorry, I know this is a long-winded ramble, but I'm a complete noob when it comes to lighting. I'll take your suggestion on board and play around with sprite renderers ins$$anonymous$$d of mesh renderers, as well as any other settings until I find whatever combination works. As a last resort there's always things like this, but I'd rather just do it myself, also their free demo is no longer available so I can't test it.
Answer by Nephtis · Apr 05, 2018 at 07:31 PM
So in a nutshell I needed to use a custom shader to allow the sprites to receive shadows.
First of all, at some point I'd evidently gone into my project settings and turned shadows off. This wasn't the cause of my issue but it stumped me for a while, so it's definitely worth checking.
To resolve the actual issue, I grabbed this shader, and applied it to all of my sprite objects that needed to receive shadows. The problem seemed to be that Unity's default Sprites/Diffuse shader isn't able to receive shadows, even though you can tick the "Receive Shadows" option in its mesh renderer's inspector component.
To give a little background, I'm importing 2D maps into Unity using the Tiled2Unity tool. This creates a mesh with the sprites basically applied as materials. Now, the default shader for sprites didn't give me the shadow effect I wanted, and I had the issue in the question above. I noticed that if I gave it the Standard shader, it produced the shadow effect, but it also led to some weird graphical artefacts and things generally looking washed out.
Ultimately the custom shader resolved the issue and I now have the ability to project shadows onto my 2D sprite mesh, while the sprites retain their original colour with no graphical bugs. I do this by simply creating a 3D cube (although I suppose it can be anything which is capable of casting a shadow) and sticking it in my walls, and then setting its "Cast Shadows" property to "Shadows Only" so that the cube itself is invisible, but still casts a shadow.
EDIT: I was having a slight issue with this shader in that I couldn't change the colour of sprites that used it via their material. The fix seems to be to go into the shader posted above, and in the Input struct, add the line:
float4 color : Color;
Then, inside the surf() method, remove the lines:
o.Albedo = c.rgb;
o.Alpha = c.a;
And replace them with:
o.Albedo = c.rgb * IN.color.rgb;
o.Alpha = c.a * IN.color.a;
I can sort of grasp what this means; I guess the posted shader didn't have any sort of colour info in it's input, but I really don't know much about shaders so don't take my word for it. Regardless, this seems to work - I still get the shadow effect and can also change the colour. I'll see if it causes other problems elsewhere.
Thank you so much for this, I have looked for something like this for days now
For a bit more info, I think the bit of interest in the shader is the bit in the Tags section that says "Queue"="Geometry", if you change this to "Queue"="Transparent" then lights go through it. I know basically nothing about shaders but I guess it means something like "if Geometry then treat as a geometrical shape that can cast and receive shadows".
Your answer
Follow this Question
Related Questions
How to fix this point light bug in surface shader? 1 Answer
How to make light that illuminates everything? (Like on these images) 1 Answer
Why does my point light not cast shadows? 0 Answers
Point Light casting weird shadows 0 Answers
How to make light-fixtures with emissive materials that can turn on/off? 0 Answers