I wanted to go over some of the basics of how the mesh lights are put together, and some additions and fixes I've stumbled upon recently.
The above gif is from some lighting tests I'm doing for a project I'm working on that's going to probably be implementing these mesh lights. You can see a few more gifs in this thread https://twitter.com/Ed_dV/status/1321332232879943681
One of the things I've added to that new versions is being able to not only blend between the inner and outer light colour, but also add in a 'bounce light' colour that shows up on the normals facing away from the light source. This can create some nice playful colours that you can't really get with standard Unity lights. To do that, just negate then saturate the N.L and use that to add in a bounce colour.
Here's a breakdown of how the Mesh Lights work.
PART 1:

This first part gives you the coverage between the backface of the light mesh (in this case an icosahedron), and all the pixels it intersects. It's *almost* what we want, as it gives us all the pixels inside the mesh volume, but unfortunately it also gives us all the pixels in front of the volume as well. Without the falloff coming in later we wouldn't be able to use this at all. If anybody can think of a way to *just* get the overlapping pixels I would love to know.
The important bits are:
Change it from Back Face culling to Front face culling, so we see the inside of the mesh - that way you can see see the light when you're inside its radius.
Turn ZWrite Off, so it doesn't get in the way of itself when we need to sample the Depth Buffer later.
Turn ZTest to Greater or Equal, because we don't need any of the pixels behind the backfaces of the mesh.
Change to Additive Blend Mode, because that's what lights do. Although you could also make multiplicative lights (or "darks" as I like to call them) if you wanted to.
And change the RenderType to Transparent, to make sure it can stack on top of itself when it renders and avoid culling issues.
PART 2:
The other two important elements of faking a light source is the light falloff (the light getting dimmer on pixels further away from the centre of the light source), and the N.L lighting calculation (the light only showing up on surfaces that are angles towards the light source).
N.L

The N.L requires the environment normals, and the light direction, both in world space.
To get the normals of the environment, we can use the _CameraDepthNormalsTexture that Unity renders out for us (forced if necessary by setting the Camera.DepthTextureMode on the camera to DepthTextureMode.DepthNormals. It's in view space, so we'll have to transform it into world space with a Transform Direction node before we can use it.
To get the light direction, we can simply get the pivot relative position of the pixel positions. That is: take the world space position of the pivot of the mesh, and subtract the world space position of any pixel that's affected.
To get the world position of the environment pixels, we use a handy node that Amplify includes called Reconstruct World Position from Depth. Now previously I was using the depth from the _CamreaDepthNormalsTexture to derive this, but I've since found out that it doesn't have as much precision as the standard depth buffer, so you'll get incorrect light coverage when the camera is further away from your lights. Using the included Amplify node bypasses this and uses the more precise depth buffer.
This gives us a vector pointing outwards from the pivot towards each overlapped pixel. In other words, a spherical light direction. Woo! N.L is done.
Falloff.

Getting the falloff is much easier, you just get the world position, transform it into object space (you could also just use the 'light direction' vector we calculated in the N.L part), and then get one minus its length, or its square magnitude if you want (that's what using a Dot Product of itself is). There are many ways to calculate falloff, depending on the lighting you want. This is just one simple way to get a spherical falloff.

Combine those two together, and you get a basic Mesh light. In deferred rendering mode, the _CameraDepthNormalsTexture even picks up normal maps, which is a nice bonus.
Depending on how you use this, you might need to also manually increase or decrease the render queue value. I found that if I wanted both Additive and Multiplicative lights to play nicely together, I had to have one drawing after the other. You'll also need to do this if you are using them with sprites or other transparent objects, as they will ignore these and you'll see the light through them. Just decrease the render queue so they render before your other sprites, or bump your other sprites render queues +1, and that should fix any issues.
If anybody has any questions let me know in the comments or on discord.

Thanks!
Ed
Puck Loves Games
2021-05-27 05:57:09 +0000 UTCSky
2021-05-27 04:52:04 +0000 UTCJeff
2020-10-29 09:06:45 +0000 UTC