3
\$\begingroup\$

I'm trying to write a simple frag/vert shader that, depending on whether it is in the range of a light, will paint the appropriate colour from either the 'lit' texture or from the 'unlit' texture.

Therefore, I need to compare the distance between the light to the range of the light. I've been googling all kinds of things, but I can't seem to find a way of accessing the range value of the light. Is there a way to do so? If not, is there some kind of derived data I could use as an alternative?

I was able to find this method here, which seems to be the most promising so far, however after playing around for a bit, I still can't seem to get what I need. There's some talk about _LightMatrix0 not being populated. Can anyone confirm?

I've also had it suggested to use SetFloat() and pass in the range like that...however, that provides no easy way of telling one light apart from another short of using the light's position as a key. As several key lights in the game will be moving this is just ugly and inefficient.

Update

I found the variable unity_LightAtten in the Unity Shader Variables documentation. However, this is only used for Vertex Lit shading, which isn't exactly ideal, especially considering the lack of console support. Could there be a way to pipe this variable to Forward Rendering?

Update 2 For clarification, shading should otherwise be 'flat' (ie no need for diffuse etc): Example of desired goal

Also, what I've currently got (albeit, not currently working as desired and currently using a workaround):

Shader "Lantern/Flat Pullup" {
Properties {
    _LitTex ("Lit (RGB)", 2D) = "white" {}
    _UnlitTex ("Unlit (RGB)", 2D) = "white" {}
    //_Falloff ("Falloff", 2D) = "white" {}
    _Pullup ("Pull up Point", Range(0,1)) = 0.7
}
SubShader {
    Pass{
        Tags { "LightMode"="ForwardBase" }
        LOD 200



        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"

        uniform sampler2D _UnlitTex;
        uniform float4 _UnlitTex_ST;
        uniform sampler2D _LitTex;
        uniform float4 _LitTex_ST;
        uniform float _Pullup;

        uniform float4 _LightColor0;

        struct VI {
            float4 vertex : POSITION;
            float4 tex : TEXCOORD0;
        };

        struct V2F {
            float4 pos : SV_POSITION;
            float4 tex : TEXCOORD0;
            float4 posWorld : TEXCOORD1;
        };

        V2F vert(VI v){
            V2F o;
            o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
            o.tex = v.tex;
            o.posWorld = mul(_Object2World, v.vertex);
            return o;
        }

        float4 frag(V2F i) : COLOR {
            float3 flatReflection = _LightColor0.xyz * 
                (1 / length(_WorldSpaceLightPos0.xyz - i.posWorld.xyz));
            float4 color;
            //Improve for light color etc
            if(length(flatReflection) > _Pullup){
                color = tex2D(_LitTex, i.tex.xy * _LitTex_ST.xy + _LitTex_ST.zw);
            }else{
                color = tex2D(_UnlitTex, i.tex.xy * _UnlitTex_ST.xy + _UnlitTex_ST.zw);
            }
            return color;
        }

        ENDCG
    }
}   
FallBack "Diffuse"
}
\$\endgroup\$
1
\$\begingroup\$

Have opted to use my 'workaround' shader as posted in the question. _Pullup can be tuned appropriately in the material to match the range of the light as closely as possible when working by eye.

\$\endgroup\$
0
\$\begingroup\$

I believe you can use: _WorldSpaceLightPos0.xyz

Source: https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html

\$\endgroup\$
  • \$\begingroup\$ No, that's only the position of the light, not it's range. \$\endgroup\$ – LeftRight92 Sep 21 '16 at 0:51
  • 1
    \$\begingroup\$ Oh i am sorry, i misread your intentions. I dont think this is smart to do from within the shader. You would be much better off hit-testing from script (use a test-sphere maybe?) to detect any lights in range. And change the texture via that script. If you would do this calculation inside the shader i believe it would do so for each vertex. So you would be making loads of useless calculations. Not absolutely sure about that though. Anyway, this should work if you give the lights a collider. \$\endgroup\$ – Dennis Sep 21 '16 at 18:57
  • 1
    \$\begingroup\$ Interesting approach, however, this would illuminate the whole object if only a small part of it were inside the light's range. I'm looking to have the only the parts of the object that are within the light's range illuminated, hence why I'm trying to implement it as a shader. Doing the calculations for every vertex is what I want. \$\endgroup\$ – LeftRight92 Sep 22 '16 at 11:09
  • \$\begingroup\$ Oke, hold up. What exactly are you trying to achieve? You say this: change texture from lit to unlit. But you dont mean changing the actual texture in the shader from 1 texture to the other? Do you mean writing a shader that has point-light support? \$\endgroup\$ – Dennis Sep 22 '16 at 11:55
  • 1
    \$\begingroup\$ Ah right. Sorry, now that i see it in pseudocode it all makes sense. So if i get you correctly, all you need at the moment is to be able to access the attenuation in you frag function correct? If so try this: add a float to your output struct (V2F). In your vert function pass the final atten into that float. This should make the float accessable from your frag function, if i am not mistaken. \$\endgroup\$ – Dennis Sep 22 '16 at 12:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.