• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
Question by Liamh101 · Jan 15, 2013 at 09:43 PM · shadertexturelighthidden

Two textures on one surface (Revealing Light)

I'm trying to have a game where when the player shines a light on a surface it will refile some form of texture that was once not seen. Is there anyway to write a shader so when a object light shines on a certain object (like a cube) will swap out the texture but for only the bit the light is shining on?

Comment
aldonaletto

People who like this

1 Show 2
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image greatestprez · Jan 15, 2013 at 09:46 PM 0
Share

just guessing, but maybe you could use a projector?

avatar image Liamh101 · Jan 15, 2013 at 10:10 PM 0
Share

The problem there would be having two objects next to each other that have different textures.

3 Replies

  • Sort: 
avatar image
Best Answer

Answer by aldonaletto · Jan 16, 2013 at 04:40 AM

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:

alt text


hiddenmessage.png (114.7 kB)
Comment
save
robhuhn
Lo0NuhtiK
kophax
Axternaly
chelnok
JohnEvelyn
wojecz

People who like this

8 Show 7 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Axternaly · Sep 28, 2017 at 04:21 PM 2
Share

Logged in just to upvote this answer, 28. September 2017. , Unity 2017.1.1 this still works perfectly!

Thanks a lot!

avatar image JohnEvelyn · Feb 14, 2018 at 12:13 PM 1
Share

Also just logged in to say - you're a hero, great answer! (and you seem to answer pretty much every question I google for Unity!)

avatar image JohnEvelyn · Feb 14, 2018 at 01:23 PM 1
Share

@aldonaletto This is really cool, I've got a version working as above, but I'm trying to expand upon the idea...

Could I ask 2 questions please? - how would I change the size of the area that is revealed? - I tried to implement multiple lights - I changed the RevealingLight code to loop through an array of objects but only 1 of the objects locations is revealed by the shader, do you know what I'm doing wrong?

 void Update () {
         foreach (Transform t in RevealPseudoLights) {
             Rend.material.SetVector ("_LightPos", t.position);
             Rend.material.SetVector ("_LightDir", t.forward);
         }
     }

Any help at all would be fantastic! Thank you!

avatar image aldonaletto JohnEvelyn · Feb 22, 2018 at 01:31 AM 0
Share

@JohnEvelyn This shader calculates the area that the revealing spot light illuminates and makes the material visible in such area. A spot light generates a cone of light (as shown in this legacy doc), and you can control its size with the shader parameter _SpotAngle
My script is intended to find a single revealing light and keep track of it. If you have several revealing lights in a list, each foreach iteration will overwrite the revealing light position and direction in the shader, thus only the last one in the list will take effect. A simple (but not 100% correct) solution would be to discard the revealing lights that were too far or pointing in the wrong direction - for instance:

 public float range = 5f;
 public float angle = 30f;
 
 void Update(){
   foreach (Transform t in RevealPseudoLights){
     Vector3 dir = transform.position - t.position;  // object direction relative to light
     float dist = dir.magnitude; // object distance from light position
     // only update shader parameters if object inside light range and angle
     if (dist<=range && Vector3.Angle(dir, t.forward)<=angle/2){
       Rend.material.SetVector("_LightPos", t.position);
       Rend.material.SetVector("_LightDir", t.forward);
     }
   }
 }

avatar image JohnEvelyn aldonaletto · Feb 22, 2018 at 11:19 AM 0
Share

@aldonaletto Fantastic! thank you so much, that's a really clear explanation

avatar image jemronMcB · Mar 14, 2018 at 05:35 PM 0
Share

This is awesome, but I'm very new to Unity and I'm having trouble getting the script you mentioned to work. What am I doing wrong?

 using UnityEngine;
 using System.Collections;
 
 
 
 public class LightReveal2 : MonoBehaviour {
 
     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);
          }
     }
 
 }
 
 

I'm getting an error on line 8 that says the ':' and ';' are "unexpected"

avatar image jemronMcB · Mar 14, 2018 at 07:58 PM 0
Share

Haha, I'm kind of slow and I figured it out. It's a javascript that you posted, not a C# script! Now it works perfectly. Thanks!

avatar image

Answer by insominx · Jan 15, 2013 at 10:50 PM

This is doable but non-trivial. You will need to make a custom frag shader that can test whether the light is shining on a particular texel and if it is then sample the 2nd texture for that texel.

To determine whether a spot light is shining on it you would have to know the position and direction of the spotlight and the position of the texel you're currently operating on in the shader. From there you can calculate the angle between the light and your point on the wall. If it is less than the angle of your light, then you could consider it to be in the light (maybe use maximum distance away too).

As for getting the position of the texel, you could pass it from the vertex to the frag shader so it will get interpolated for every texel.

Comment
Lo0NuhtiK

People who like this

1 Show 0 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image

Answer by robertbu · Jan 15, 2013 at 11:35 PM

I have a very limited understanding of shaders. @Insomix's solutions sounds like the "right" solution, but it occured to me that maybe this behavior could be faked. That is write a shader that has a diffuse texture combined with an overlay texture. Without light, just the overlay texture is displayed. With light the diffuse texture plays an increasing role:

 Shader "Custom/DiffuseTexture" {
  Properties {
         _Color ("Main Color", Color) = (1,1,1,0.5)
         _MainTex ("Base (RGB)", 2D) = "white" { }
         _OverTex ("Over (RGB)", 2D) = "white" { }
     }
 
     SubShader {
         Pass {
             Material {
                 Diffuse [_Color]
             }
             Lighting On
             SetTexture [_MainTex] {
                 constantColor [_Color]
                 Combine texture * primary DOUBLE, texture * constant
             }
             SetTexture [_OverTex]
             {
             Combine previous + texture
             }
         }
     }
 }

The overlay texture needs to be fairly dark. Changing the operator used in the final Combine chages the behavior in ways that might be useful. Maybe someone with more shader exerience can suggest changes to move it closer to your ideal.

Comment
Lo0NuhtiK

People who like this

1 Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image insominx · Jan 16, 2013 at 12:31 AM 0
Share

You could also make a very simple shader that just checks the intensity of the light and does something like: if (lightIntensity > someValue) { color = mySecondTextureColor; } else { color = myFirstTextureColor; }

Unity Answers is in Read-Only mode

Unity Answers content will be migrated to a new Community platform and we are aiming to launch a public beta by June 9. Please note, Unity Answers is now in read-only so we can prepare for the final data migration.

For more information and updates, please read our full announcement thread in the Unity Forum.

Follow this Question

Answers Answers and Comments

17 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

no shadow with until texture ?? 2 Answers

Noob Question - Custom trees and aliasing 1 Answer

HowTo Texture, Shader, Light to reach example included 0 Answers

Shader that ignores only Directional Light? 2 Answers

Explain Toon Shading Please 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges