Create light shapes on plane using C# (similar to the game Closure)

Hey guys, I’m having some trouble coming up with a function in C# since I’m new to unity. I’m making a 2.5D game with a couple of friends and it mostly revolves around using light. So the levels are mostly hidden (in the dark) and the parts that you see are lit by your light or other light sources,a little like the games Ascension or Closure.

What I first did was a test where I used a big plane and put a “hole” in it (made using flash : here, this is just a test), the plane’s size is three times that of the screen, so even if the mouse moves the edge of the screen you don’t see the limits of the plane. This kind of works, but allows only one light source when I need multiple light sources (in quad and ellipse shapes), I also need the light to be created via script so that I can change it dynamically.

The second test, was made using the Java framework “Processing”. Since the light mostly have ellipsoidal or quad shapes (like in this picture), I first create an quad (or ellipse) coloured in black. then I use an array Java (well processing) creates with all the screen pixels positions and colours to turn all the black pixels (in this case, the quads and ellipses pixels) to a transparent colour, all the remaining pixels (the rest of the screen) are then coloured in black. This is the most viable solution I could come up with so far, sadly it needs a lot of resources (it takes about 10seconds to render one frame).

Now I’m trying to do something similar (or better) in unity using C#, but I have no idea where to start, that is why I’m you asking for your help, I know this is a lot to ask, but I thank you in advance for your help.

Cordially, Radouane.

P.S : I already tried using the available lighting options, but since we’re using custom made textures (we paint them ourselves) the results are not great, the textures are kind of burned, that’s why I’m trying to use this way of lighting.

There are three methods for this (that I know), each of which will have similar results.

The first two methods both use cookies for lights:

picture

Be sure to turn off ambient light through edit–>render settings–>ambient light–>“make the texture black aka (0,0,0) for RGB”

[edit:] I forgot to mention, the reason your textures look burned is because you are using a bad shader, and it would be a shame to ignore the cookie solution because of it. Essentially all you want to do is use a special shader that 1) ignores angles 2) does not decrease intensity with distance, only with the input from the cookie.

This makeshift solution is modified from the Default Diffuse shader (and it works on my machine). If you set the range of the light to “999999” and tweak the intensity of the light (~= 1) this should look virtually identical to an “Unlit Diffuse” shader. Also, use a “spotlight”, if that’s what you want.

It renders like this:

8685-lightingalwaysexample.png

Despite the fact that the plane is rotated 82 degrees, the brick wall still seems like it is fully lit. With a normal shader the larger brick wall’s color values would be multiplied by the cosine of 82 or .139 making it 14% as bright as it should be.

Shader "Custom/DistanceOnlyDiffuse"
{
	Properties
	{
	_Color ("Main Color", Color) = (1,1,1,1) //variable _Color of type Color seen as "Main Color" in the inspector
	_MainTex ("Base (RGB)", 2D) = "white" {} // Texture2D that will default to white.
	}
	
	SubShader
	{
		Tags { "RenderType"="Opaque" "Queue" = "Geometry" }
		
		CGPROGRAM // a glorified curly brace START
		#pragma surface surf Always
		//pragma definition: (computer science) A directive inserted into a computer program to prevent the automatic execution of certain error checking and reporting routines which are no longer necessary when the program has been perfected. 
		//surface--> surface shader
		//surf means use the "void surf" function defined later
		//Always means use "LightingAlways"
		
		fixed4 LightingAlways (SurfaceOutput s, fixed3 lightDir, fixed atten) 
		{
			//fixed diff = max (0, dot (s.Normal, lightDir)); //this commented out dot product normally makes things dimmer at an angle, which is normally how light works
	
			fixed4 c;
			c.rgb = s.Albedo * _LightColor0.rgb * (atten); //diff * used to be multiplied in here to dim the color of the object
			c.a = s.Alpha;
			return c;
		}

		sampler2D _MainTex; //tell the CG program code that the shaderlab code has a _MainTex and import it (in Laymans) //sampler2D is a Texture2D
		fixed4 _Color; //similarly inform the CG program that shaderlab has a property for color // color has 4 values RGBA, hence fixed (it wont change) 4

		struct Input
		{
			float2 uv_MainTex; //this is a builtin value that will be handled by unity to save you time/redundancy. other values (see: Surface Shader input structure): http://docs.unity3d.com/Documentation/Components/SL-SurfaceShaders.html
		};

		void surf (Input IN, inout SurfaceOutput o) //use the struct Input as an "IN" (input), the other stuff specifies what happens next
		{
			fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; //texture the object and multiply by the color to tint it.  White color does nothing 
			o.Albedo = c.rgb; //send the calculated value back as an Albedo.  Albedo is how something would look without light
			o.Alpha = c.a;
		}
		ENDCG //glorified curly brace END
	}
	//Fallback "Transparent/VertexLit" //During developement (pre-release) graphics programmers usually comment out fallbacks to find which hardware can handle/support the shader
}

To add this shader to your project, right click in the project panel and click create–>shader, rename it, and copy and paste this exactly into its code (delete the pre-existing code too).

To select this shader click on the material you want to change–>look at it in the inspector–>click the dropdown shader bar–>go into the “Custom” folder–>select “DistanceOnlyDiffuse”.

I wasn’t able to get shadows to work, but I think you can get this shader to use shadows. Shadow-ception if you will. Of course this requires Unity Pro.

For C# Programmers:

A simple programming method would be to get a few cookie textures in an array and manually cycle through them with a script. Essentially this would be:

    using UnityEngine;
    using System.Collections;
    
    public class SomeClass
    {

    public Light light;
    public Texture2D[] textures;
    int index = 0;
    int length = 1;
    float interval = 0.2f;
    float nextSwapTime = 0.2f;
    
    void Start()
    {
         length = textures.Length;
    }
    
    void Update()
    {
         if(Time.time > nextSwapTime)
         {
              nextSwapTime += interval;
              index++;
              if(index == length) index = 0;
              light.cookie = textures[index];
         }
    
    }
    
    }

The problem is that usually you would want at least 8-16 textures for a reasonable aura effect, and that will take up a lot of space if each texture is 256x256.

For people willing to spend money:

If you would like to use cookies but do not want to make X different texture for the light animation, buy Substance Designer by Allegorithmic. http://www.allegorithmic.com/products/substance-designer

It is a visual scripting knowledge (it uses node based logic). It is often very expensive, but if you can get it on sale it’s a great tool. It does take some knowledge on how their “substances” work. These substance files, set up correctly, will overwrite the texture at runtime, you don’t need to change the texture each frame, but you have to know how to create and integrate the substance. For easy and complex lighting effects this method probably isn’t worth it, but it will definitely look the smoothest since it will update each frame.

I happened to make a tutorial on how to use SD2 so I will gratuitously link it: - YouTube

The cool thing about substances is they use vector images which are dynamically updated at run-time due to any parameters you want (usually time), they are very accurate, and they happen to use up way less file memory (at least 70% less, I think it can go as low as approx. 97% less, which speeds up download time, if that’s an issue for mobile or web-players).

This can create effects from bugs crawling on the floor, to sand moving across sand-dunes, to the aforementioned light cookie manipulations, to dynamic skyboxes for sunrise–>sunset–>night. Anything imaginable that can be explained by vector mathematics (which in my opinion is everything, although depending on what you want it could be difficult).

A laborious alternative for programmers:

The pure programming route is to learn how to use CGPrograms in Shaders. While you are at it, you will also need a rudimentary understanding of ShaderLab as well. What you would essentially do is make a new surface shader.

A example method would be to use the cull() function on all undesired pixels within a #CGPROGRAM#ENDCG bracket within a custom shader. This would definitely work for a single ellipse, although I have no idea what this mysterious “quad” is. The problem with this method, I realize, is that handling multiple lights could be difficult.

[edit:3] dang that was long.

Hey Matt,
Thanks a lot for your answer, it’s been very helpful. I tried the first method and it worked pretty well. I’m testing the second one right now.
Again thank you for you help, you’ve saved me a lot of time and programming.