That’s something that probably will solve your problem: it’s a special shader that makes visible a texture only inside the spot angle of a pseudo light. Explaining the “pseudo light”: I could not identify position and direction of a specific light at shader level, thus decided to fake it - this info is passed each frame to the shader by an auxiliary script.
The shader takes the hidden texture’s alpha channel into account, thus only its opaque pixels appear - this is useful for making hidden text messages (for instance) appear when illuminated by the “magic light”:
Shader "Custom/Hidden Texture" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" { }
_SpotAngle ("Spot Angle", Float) = 30.0
_Range ("Range", Float) = 5.0
_Contrast ("Contrast", Range (20.0, 80.0)) = 50.0
}
Subshader {
Tags {"RenderType"="Transparent" "Queue"="Transparent"}
Pass {
Blend SrcAlpha OneMinusSrcAlpha
ZTest LEqual
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
uniform float4 _LightPos; // light world position - set via script
uniform float4 _LightDir; // light world direction - set via script
uniform float _SpotAngle; // spotlight angle
uniform float _Range; // spotlight range
uniform float _Contrast; // adjusts contrast
struct v2f_interpolated {
float4 pos : SV_POSITION;
float2 texCoord : TEXCOORD0;
float3 lightDir : TEXCOORD1;
};
v2f_interpolated vert(appdata_full v){
v2f_interpolated o;
o.texCoord.xy = v.texcoord.xy;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
half3 worldSpaceVertex = mul(_Object2World, v.vertex).xyz;
// calculate light direction to vertex
o.lightDir = worldSpaceVertex-_LightPos.xyz;
return o;
}
half4 frag(v2f_interpolated i) : COLOR {
half dist = saturate(1-(length(i.lightDir)/_Range)); // get distance factor
half cosLightDir = dot(normalize(i.lightDir), normalize(_LightDir)); // get light angle
half ang = cosLightDir-cos(radians(_SpotAngle/2)); // calculate angle factor
half alpha = saturate(dist * ang * _Contrast); // combine distance, angle and contrast
half4 c = tex2D(_MainTex, i.texCoord); // get texel
c.a *= alpha; // combine texel and calculated alpha
return c;
}
ENDCG
}
}
}
Save this shader as “HiddenTexture.shader” (or other suitable name) in some Assets subfolder, select it in the Project view and click Create->Material - this creates the hidden texture material. Assign it to the object, then add a second material, which actually will behave as the main one: it appears all the time, and is covered by the hidden material only when the “light” is over it.
As I said, the shader actually doesn’t care about the actual lights: you must inform to it the position and direction of the light that reveals the hidden texture. A simple way to do this is to make the object find the light object at Start, and update its shader each frame in Update, like below (script attached to the object that has the hidden texture):
var tfLight: Transform;
function Start () {
// find the revealing light named "RevealingLight":
var goLight = GameObject.Find("RevealingLight");
if (goLight) tfLight = goLight.transform;
}
function Update () {
if (tfLight){
renderer.material.SetVector("_LightPos", tfLight.position);
renderer.material.SetVector("_LightDir", tfLight.forward);
}
}
NOTES:
1- The “RevealingLight” object in the script above is a game object with this name, which has a spot light. As mentioned in the text, the light itself is just a “cosmetic” aid: the position and direction of the object is what really matters for the shader. All objects that have a “hidden texture” material must have this script attached, so that they can keep track of the light. If more than one revealing light exists, a different approach should be used instead: the light object should cast a ray in its forward direction, and pass the light info to the hit object case it has a hidden texture (some specific tag could mark these objects, for instance).
2- Two materials are used in each object: the first one is the hidden material, and the second is actually the main material - it appears all the time until the light aims at the object, revealing the hidden texture. This approach allows to use almost any shader in the main material.
3- The hidden texture alpha is preserved - if you want to make something like a symbol or message painted with invisible ink being revealed by the light, just make the texture background transparent in the image editor: the main texture will show through the transparent areas even under the “magic” light - like this:
