- Home /
Shader - different passes depending on Lightmap being set or not
I'm trying to implement custom cartoon shading but it needs to work differently for Lightmapped objects and non-lightmapped (i.e. apply ramp to lightmap texture vs. apply ramp to light itself).
How can I choose which pass gets used depending on whether the object has a lightmap set or not? I tried all the different tags (Tags { "RenderType" = "VertexLM" }) but they don't seem to work. I know I could just use 2 different shaders but I'd rather keep everything unified if possible (and I may want to might Lightmap + RT lighting as well).
I tried two approaches:
Here approach #1, using two different surface shaders (only one works at a time; I tried combining them into one but only one model works at a time due to the #pragma surface surf directive)
Shader "K/Cartoon Lightmap2"
{
Properties {
_MainTex ("Texture", 2D) = "white" {}
_Ramp("Ramp", 2D) = "white" {}
}
SubShader {
CGPROGRAM
#pragma surface surf Standard
sampler2D _Ramp;
half4 LightingStandard (SurfaceOutput s, half3 lightDir, half atten) {
half NdotL = dot (s.Normal, lightDir);
half4 c; c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten * 2);
c.a = s.Alpha;
return c;
}
inline fixed4 LightingStandard_SingleLightmap (SurfaceOutput s, fixed4 color) {
half3 lm = DecodeLightmap (color);
//return fixed4(lm, 0);
float lightmapAvg = (lm.r + lm.g + lm.b)/3.0;
fixed4 ramp = tex2D(_Ramp, float2(lightmapAvg, 0));
return ramp;
}
inline fixed4 LightingStandard_DualLightmap (SurfaceOutput s, fixed4 totalColor, fixed4 indirectOnlyColor, half indirectFade) {
half3 lm = lerp (DecodeLightmap (indirectOnlyColor), DecodeLightmap (totalColor), indirectFade);
return fixed4(lm, 0);
}
inline fixed4 LightingStandard_StandardLightmap (SurfaceOutput s, fixed4 color, fixed4 scale, bool surfFuncWritesNormal) {
UNITY_DIRBASIS
half3 lm = DecodeLightmap (color);
half3 scalePerBasisVector = DecodeLightmap (scale);
if (surfFuncWritesNormal)
{
half3 normalInRnmBasis = saturate (mul (unity_DirBasis, s.Normal));
lm *= dot (normalInRnmBasis, scalePerBasisVector);
}
return fixed4(lm, 0);
}
struct Input {
float2 uv_MainTex;
};
sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutput o)
{
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}
ENDCG
}
SubShader {
CGPROGRAM
#pragma surface surf Ramp
sampler2D _Ramp;
half4 LightingRamp (SurfaceOutput s, half3 lightDir, half atten) {
half NdotL = dot (s.Normal, lightDir);
half diff = NdotL * 0.5 + 0.5;
half3 ramp = tex2D (_Ramp, float2(diff)).rgb;
half4 c;
c.rgb = s.Albedo * _LightColor0.rgb * ramp * (atten * 2);
c.a = s.Alpha;
return c;
}
struct Input {
float2 uv_MainTex;
};
sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutput o)
{
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}
ENDCG
}
Fallback "Diffuse"
}
Here's approach #2 : Surface + multiplicative Lightmap pass (messes up on non-LM objects)
Shader "K/Cartoon Lightmap"
{
Properties
{
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Texture 1", 2D) = "white" {}
_Ramp("Ramp", 2D) = "white" {}
}
SubShader
{
LOD 200
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float2 uv_MainTex;
float2 uv2_LightMap;
};
sampler2D _MainTex;
sampler2D _LightMap;
fixed4 _Color;
void surf (Input IN, inout SurfaceOutput o)
{
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * _Color;
}
ENDCG
Pass {
// Disable lighting, we're only using the lightmap
Lighting Off
Blend DstColor Zero // Multiplicative
CGPROGRAM
// Must be a vert/frag shader, not a surface shader: the necessary variables
// won't be defined yet for surface shaders.
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv0 : TEXCOORD0;
float2 uv1 : TEXCOORD1;
};
struct appdata_lightmap {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
float2 texcoord1 : TEXCOORD1;
};
// These are prepopulated by Unity
sampler2D unity_Lightmap;
float4 unity_LightmapST;
sampler2D _MainTex;
sampler2D _Ramp;
float4 _MainTex_ST; // Define this since its expected by TRANSFORM_TEX; it is also pre-populated by Unity.
v2f vert(appdata_lightmap i) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, i.vertex);
// UnityCG.cginc - Transforms 2D UV by scale/bias property
// #define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
o.uv0 = TRANSFORM_TEX(i.texcoord, _MainTex);
// Use `unity_LightmapST` NOT `unity_Lightmap_ST`
o.uv1 = i.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
return o;
}
half4 frag(v2f i) : COLOR {
half4 main_color = tex2D(_MainTex, i.uv0);
// Decodes lightmaps:
// - doubleLDR encoded on GLES
// - RGBM encoded with range [0;8] on other platforms using surface shaders
// inline fixed3 DecodeLightmap(fixed4 color) {
// #if defined(SHADER_API_GLES) && defined(SHADER_API_MOBILE)
// return 2.0 * color.rgb;
// #else
// return (8.0 * color.a) * color.rgb;
// #endif
// }
fixed3 lightmap = DecodeLightmap(tex2D(unity_Lightmap, i.uv1));
float lightmapAvg = (lightmap.r + lightmap.g + lightmap.b)/3.0;
fixed4 ramp = tex2D(_Ramp, float2(lightmapAvg, 0));
main_color.rgb *= ramp.rgb;
return ramp;
//return main_color;
}
ENDCG
}
}
}
Your answer
Follow this Question
Related Questions
Static+Real Time lighting in Angry Bots Demo 1 Answer
Unity 3 Problem: Legacy Lightmap Shaders don't seem to work. 1 Answer
Replacing lightmap shader pass 0 Answers
Is there a way to Separate the light contribution from the shadow? 0 Answers
Different materials using the same colours doesn't match. 0 Answers