There’s 4 lights max alllowed per sprite renderer. How can I allow more lights per sprite renderer?
I’ve seen in one of Ferr2D’s sprite shader that he bumped the classic 4 lights max to 8 lights max.
There’s 4 lights max alllowed per sprite renderer. How can I allow more lights per sprite renderer?
I’ve seen in one of Ferr2D’s sprite shader that he bumped the classic 4 lights max to 8 lights max.
Does someone know about this?
Unity’s “LightMode”=“ForwardBase” pass supports a single directional light and 4 vertex lights. You can add additional lights using “LightMode”=“ForwardAdd” passes and per pixel lights (though you can write the add passes to treat the lights as vertex lighting if you wish).
If you use “LightMode”=“Vertex” instead it’ll use up to 8 vertex lights using this line:
half3 lighting = ShadeVertexLightsFull(v.vertex, v.normal, 8, true);
Here’s the function definition in UnityCG.cginc:
// Used in Vertex pass: Calculates diffuse lighting from lightCount lights. Specifying true to spotLight is more expensive
// to calculate but lights are treated as spot lights otherwise they are treated as point lights.
float3 ShadeVertexLightsFull (float4 vertex, float3 normal, int lightCount, bool spotLight)
edit: One note for anyone wondering why not just use the ForwardAdd passes. It’s because they don’t behave nicely with transparent sprites or mesh and can cause lighting from the the add passes to “bleed” on top of other objects.
From what I’ve seen using vertex lighting is not really what I want as the lighting is not correct because I might not have enough vertices on the sprite. But I’m interested in what you said about “LightMode”=“ForwardAdd”.
I usually make shaders with ShaderForge but I changed that line as you suggested.
I get this weird behaviour where only the parts of the sprite that is lit is being shown.
https://i.imgur.com/4gn2TzS.gifv
Am I doing something wrong or is this what you mentioned in your note?
Here’s the shader code:
// Shader created with Shader Forge v1.38
// Shader Forge (c) Neat Corporation / Joachim Holmer - http://www.acegikmo.com/shaderforge/
// Note: Manually altering this data may prevent you from opening it in Shader Forge
/*SF_DATA;ver:1.38;sub:START;pass:START;ps:flbk:,iptp:1,cusa:True,bamd:0,cgin:,lico:1,lgpr:1,limd:0,spmd:1,trmd:0,grmd:0,uamb:True,mssp:True,bkdf:False,hqlp:False,rprd:False,enco:False,rmgx:True,imps:True,rpth:0,vtps:0,hqsc:True,nrmq:0,nrsp:0,vomd:0,spxs:True,tesm:0,olmd:1,culm:2,bsrc:0,bdst:7,dpts:2,wrdp:False,dith:0,atcv:False,rfrpo:True,rfrpn:Refraction,coma:15,ufog:False,aust:True,igpj:True,qofs:0,qpre:3,rntp:2,fgom:False,fgoc:False,fgod:False,fgor:False,fgmd:0,fgcr:0.5,fgcg:0.5,fgcb:0.5,fgca:1,fgde:0.01,fgrn:0,fgrf:300,stcl:True,atwp:True,stva:128,stmr:255,stmw:255,stcp:6,stps:0,stfa:0,stfz:0,ofsf:0,ofsu:0,f2p0:False,fnsp:False,fnfb:False,fsmp:False;n:type:ShaderForge.SFN_Final,id:1873,x:33510,y:32715,varname:node_1873,prsc:2|emission-1749-OUT,custl-4260-OUT,alpha-603-OUT;n:type:ShaderForge.SFN_Tex2d,id:4805,x:32551,y:32729,ptovrint:False,ptlb:MainTex,ptin:_MainTex,varname:_MainTex_copy,prsc:2,glob:False,taghide:False,taghdr:False,tagprd:True,tagnsco:False,tagnrm:False,ntxv:0,isnm:False;n:type:ShaderForge.SFN_Multiply,id:1086,x:32812,y:32818,cmnt:RGB,varname:node_1086,prsc:2|A-4805-RGB,B-5983-RGB,C-5376-RGB;n:type:ShaderForge.SFN_Color,id:5983,x:32551,y:32915,ptovrint:False,ptlb:Color,ptin:_Color,varname:_Color_copy,prsc:2,glob:False,taghide:False,taghdr:False,tagprd:False,tagnsco:False,tagnrm:False,c1:1,c2:1,c3:1,c4:1;n:type:ShaderForge.SFN_VertexColor,id:5376,x:32551,y:33079,varname:node_5376,prsc:2;n:type:ShaderForge.SFN_Multiply,id:1749,x:33025,y:32818,cmnt:Premultiply Alpha,varname:node_1749,prsc:2|A-1086-OUT,B-603-OUT;n:type:ShaderForge.SFN_Multiply,id:603,x:32812,y:32992,cmnt:A,varname:node_603,prsc:2|A-4805-A,B-5983-A,C-5376-A;n:type:ShaderForge.SFN_LightColor,id:3248,x:32866,y:32171,varname:node_3248,prsc:2;n:type:ShaderForge.SFN_LightAttenuation,id:159,x:32866,y:32302,varname:node_159,prsc:2;n:type:ShaderForge.SFN_Multiply,id:557,x:33129,y:32446,varname:node_557,prsc:2|A-3248-RGB,B-159-OUT,C-638-OUT;n:type:ShaderForge.SFN_Multiply,id:4260,x:33281,y:32682,varname:node_4260,prsc:2|A-557-OUT,B-1749-OUT;n:type:ShaderForge.SFN_LightVector,id:5315,x:32766,y:32640,varname:node_5315,prsc:2;n:type:ShaderForge.SFN_NormalVector,id:2031,x:32766,y:32491,prsc:2,pt:False;n:type:ShaderForge.SFN_Dot,id:638,x:32925,y:32563,varname:node_638,prsc:2,dt:1|A-2031-OUT,B-5315-OUT;proporder:4805-5983;pass:END;sub:END;*/
Shader "Shader Forge/SpriteLit" {
Properties {
[PerRendererData]_MainTex ("MainTex", 2D) = "white" {}
_Color ("Color", Color) = (1,1,1,1)
[HideInInspector]_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
_Stencil ("Stencil ID", Float) = 0
_StencilReadMask ("Stencil Read Mask", Float) = 255
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilComp ("Stencil Comparison", Float) = 8
_StencilOp ("Stencil Operation", Float) = 0
_StencilOpFail ("Stencil Fail Operation", Float) = 0
_StencilOpZFail ("Stencil Z-Fail Operation", Float) = 0
}
SubShader {
Tags {
"IgnoreProjector"="True"
"Queue"="Transparent"
"RenderType"="Transparent"
"CanUseSpriteAtlas"="True"
"PreviewType"="Plane"
}
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardAdd" // changed this from ForwardBase to ForwardAdd!
}
Blend One OneMinusSrcAlpha
Cull Off
ZWrite Off
Stencil {
Ref [_Stencil]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
Comp [_StencilComp]
Pass [_StencilOp]
Fail [_StencilOpFail]
ZFail [_StencilOpZFail]
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#define UNITY_PASS_FORWARDBASE
#pragma multi_compile _ PIXELSNAP_ON
#include "UnityCG.cginc"
#include "Lighting.cginc"
#pragma multi_compile_fwdbase
#pragma only_renderers d3d9 d3d11 glcore gles
#pragma target 3.0
uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
uniform float4 _Color;
struct VertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 texcoord0 : TEXCOORD0;
float4 vertexColor : COLOR;
};
struct VertexOutput {
float4 pos : SV_POSITION;
float2 uv0 : TEXCOORD0;
float4 posWorld : TEXCOORD1;
float3 normalDir : TEXCOORD2;
float4 vertexColor : COLOR;
};
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
o.uv0 = v.texcoord0;
o.vertexColor = v.vertexColor;
o.normalDir = UnityObjectToWorldNormal(v.normal);
o.posWorld = mul(unity_ObjectToWorld, v.vertex);
float3 lightColor = _LightColor0.rgb;
o.pos = UnityObjectToClipPos( v.vertex );
#ifdef PIXELSNAP_ON
o.pos = UnityPixelSnap(o.pos);
#endif
return o;
}
float4 frag(VertexOutput i, float facing : VFACE) : COLOR {
float isFrontFace = ( facing >= 0 ? 1 : 0 );
float faceSign = ( facing >= 0 ? 1 : -1 );
float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz);
float3 normalDirection = i.normalDir;
float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);
float3 lightColor = _LightColor0.rgb;
////// Lighting:
float attenuation = 1;
////// Emissive:
float4 _MainTex_var = tex2D(_MainTex,TRANSFORM_TEX(i.uv0, _MainTex));
float node_603 = (_MainTex_var.a*_Color.a*i.vertexColor.a); // A
float3 node_1749 = ((_MainTex_var.rgb*_Color.rgb*i.vertexColor.rgb)*node_603); // Premultiply Alpha
float3 emissive = node_1749;
float3 finalColor = emissive + ((_LightColor0.rgb*attenuation*max(0,dot(i.normalDir,lightDirection)))*node_1749);
return fixed4(finalColor,node_603);
}
ENDCG
}
Pass {
Name "FORWARD_DELTA"
Tags {
"LightMode"="ForwardAdd"
}
Blend One One
Cull Off
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#define UNITY_PASS_FORWARDADD
#pragma multi_compile _ PIXELSNAP_ON
#include "UnityCG.cginc"
#include "AutoLight.cginc"
#include "Lighting.cginc"
#pragma multi_compile_fwdadd
#pragma only_renderers d3d9 d3d11 glcore gles
#pragma target 3.0
uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
uniform float4 _Color;
struct VertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 texcoord0 : TEXCOORD0;
float4 vertexColor : COLOR;
};
struct VertexOutput {
float4 pos : SV_POSITION;
float2 uv0 : TEXCOORD0;
float4 posWorld : TEXCOORD1;
float3 normalDir : TEXCOORD2;
float4 vertexColor : COLOR;
LIGHTING_COORDS(3,4)
};
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
o.uv0 = v.texcoord0;
o.vertexColor = v.vertexColor;
o.normalDir = UnityObjectToWorldNormal(v.normal);
o.posWorld = mul(unity_ObjectToWorld, v.vertex);
float3 lightColor = _LightColor0.rgb;
o.pos = UnityObjectToClipPos( v.vertex );
#ifdef PIXELSNAP_ON
o.pos = UnityPixelSnap(o.pos);
#endif
TRANSFER_VERTEX_TO_FRAGMENT(o)
return o;
}
float4 frag(VertexOutput i, float facing : VFACE) : COLOR {
float isFrontFace = ( facing >= 0 ? 1 : 0 );
float faceSign = ( facing >= 0 ? 1 : -1 );
float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz);
float3 normalDirection = i.normalDir;
float3 lightDirection = normalize(lerp(_WorldSpaceLightPos0.xyz, _WorldSpaceLightPos0.xyz - i.posWorld.xyz,_WorldSpaceLightPos0.w));
float3 lightColor = _LightColor0.rgb;
////// Lighting:
float attenuation = LIGHT_ATTENUATION(i);
float4 _MainTex_var = tex2D(_MainTex,TRANSFORM_TEX(i.uv0, _MainTex));
float node_603 = (_MainTex_var.a*_Color.a*i.vertexColor.a); // A
float3 node_1749 = ((_MainTex_var.rgb*_Color.rgb*i.vertexColor.rgb)*node_603); // Premultiply Alpha
float3 finalColor = ((_LightColor0.rgb*attenuation*max(0,dot(i.normalDir,lightDirection)))*node_1749);
return fixed4(finalColor * node_603,0);
}
ENDCG
}
Pass {
Name "ShadowCaster"
Tags {
"LightMode"="ShadowCaster"
}
Offset 1, 1
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#define UNITY_PASS_SHADOWCASTER
#pragma multi_compile _ PIXELSNAP_ON
#include "UnityCG.cginc"
#include "Lighting.cginc"
#pragma fragmentoption ARB_precision_hint_fastest
#pragma multi_compile_shadowcaster
#pragma only_renderers d3d9 d3d11 glcore gles
#pragma target 3.0
struct VertexInput {
float4 vertex : POSITION;
};
struct VertexOutput {
V2F_SHADOW_CASTER;
};
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
o.pos = UnityObjectToClipPos( v.vertex );
#ifdef PIXELSNAP_ON
o.pos = UnityPixelSnap(o.pos);
#endif
TRANSFER_SHADOW_CASTER(o)
return o;
}
float4 frag(VertexOutput i, float facing : VFACE) : COLOR {
float isFrontFace = ( facing >= 0 ? 1 : 0 );
float faceSign = ( facing >= 0 ? 1 : -1 );
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
FallBack "Diffuse"
CustomEditor "ShaderForgeMaterialInspector"
}
Your shader already has a ForwardAdd pass, and you kind of need both the forward base and forward add pass as the base pass handles ambient lighting and emission. Also, as you noticed, only the area around the light is rendered for the add pass.
For what your doing, don’t change the shader at all, you only need to increase the per pixel light count in the Quality settings.
Waw that’s it, thanks bgolus!!! That’s much easier than expected
Just how expensive is it to add the option for more lights? Is putting the pixel light count to 20 crazy?
Does it matter that I allow for 20 lights yet throughout my game a sprite renderer only ever has 10 lights max on it?
You could put the number at 999 if you wanted, if you only ever have 10 lights it’ll be no worse than if you set the limit to 10.