- Home /
Possibility to check all lights in one pass of a shader?
Hi everyone,
tl;dr: is there a way to check several lights during 1 pass of a shader? I would like to modify the effect of some specific lights depending on whether the pixel is lit by other lights or not.
The long explanation: I am trying to create a surface shader for my 2D game. My shader already works very nicely with my sprites and normal maps. Additionally the game is in the night and I wanted the moonlight to lit the sprites without displaying their color (so everything is grey under the moonlight). I have put a 0 alpha on the moonlight and 255 on the other lights to be able differentiate them in the shader and it works very well. The colors appears only if a light which is not the moon is close to a sprite, otherwise it is lit only with grey/white.
Now my problem is the following: when a light (say a flame) is close to my sprite, I would like to hide the lighting coming from the moon. Right now if my sprite is between a flame and the moonlight, I have 1 side of the sprite with the colors visible and the other side lit by the moonlight so grayish. In real life the luminosity of the flame would prevent our eyes to see the moonlight reflection and this side would be black. This is the effect I want to recreate.
I could implement this effect easily if I could check whether the sprite is near a light which is not the moon in the shader. Then depending on how bright this light is I would attenuate the moonlight reflection. Unfortunately I can't find a way to check the other lights while I am handling the moonlight.
Any ideas?
See my code below. The key part is around line 65.
Shader "Sprites/Sprite normal map"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_BumpMap ("Normalmap", 2D) = "bump" {}
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
_Cutoff ("Alpha Cutoff", Range (0,1)) = 0.5
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="TransparentCutOut"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
LOD 300
// Render front faces only
Cull Back
Lighting On
ZWrite Off
Fog { Mode Off }
CGPROGRAM
#pragma surface surf Lambert alpha vertex:vert addshadow alphatest:_Cutoff
#pragma multi_compile DUMMY PIXELSNAP_ON
sampler2D _MainTex;
sampler2D _BumpMap;
fixed4 _Color;
float _ScaleX;
struct Input
{
float2 uv_MainTex;
float2 uv_BumpMap;
fixed4 color : COLOR;
};
void vert (inout appdata_full v, out Input o)
{
#if defined(PIXELSNAP_ON) && !defined(SHADER_API_FLASH)
v.vertex = UnityPixelSnap (v.vertex);
#endif
float3 normal = v.normal;
v.normal = float3(0,0,1);
v.tangent = float4(1, 0, 0, 1);
UNITY_INITIALIZE_OUTPUT(Input, o);
}
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * IN.color;
if (_LightColor0.a < 0.1f)
{
// Here I would like to test the other lights to know if I should attenuate _LightColor0
o.Albedo = _LightColor0;
}
else
{
o.Albedo = c.rgb;
}
o.Alpha = c.a;
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
o.Normal.z *= -1;
}
ENDCG
}
Fallback "Transparent/Cutout/Diffuse"
}
Answer by Creepgin · Jul 29, 2015 at 09:36 PM
Using forward rendering, this can be done through blending.
Thanks for your comment Creepgin. Correct me if I am wrong but I believe the blending will only control how the result of my shader calculation will blend with what's already drawn. What I am trying to do is change what the shader computes depending on whether there is a light nearby which is not the moon. Just out of curiosity I tried several Blend combinations and also BlendOp but as I suspected it never had the effect I was hoping for.
$$anonymous$$y understanding of shaders is limited so please correct me if needed.
I recently had roughly the same need as you do. That's why I searched for the same topic and came across your question.
Assu$$anonymous$$g your moonlight is a directional light, it will be rendered in the ForwardBase pass. Your flame light (a point light) will be rendered in the ForwardAdd pass. If you do a multiplicatively blend on the ForwardAdd pass with Blend Zero SrcColor, you should have the flamelight to mask out the moonlight. If you do a Blend One Zero, your flamelight will completely replace the moonlight.
Thanks that's actually very interesting and helps me understand how blending works a bit more. Unfortunately my moonlight is a point light too and I don't think I want to change it to a directional light. The moon is often on a screen and I want the elements on its right to have a rim effect on their left side, and the elements on its left to have a rim effect on their right side (which works well now and looks very nice).
I see. I'm researching on more control over Blending (per light vs per pass). But so far I've got nothing since all my questions are left unanswered here or on the forum.
For your case, maybe a Level or Color Lookup image effect can achieve your desired effect.
Actually you can still keep the moonlight as a point light and still separate it out into a separate pass. Set the flamelight's render mode to important and the moonlight to not important. This will force the moonlight to be a vertex light (or a SH light). Or you can do it the other way around, as long as those 2 types of lights are in separate passes.
Your answer