- Home /
How to fix this point light bug in surface shader?
I've been working on a shader with a custom light model. It works on most objects, but when it comes to flat meshes like planes or terrain it creates this strange effect. I haven't been able to figure out how to fix it.
Here is my code:
Shader "CelShading/FinalCelSurf"
{
Properties
{
_MainTex("Albedo (RGB)", 2D) = "white" {}
_Color("Tint Color", Color) = (1,1,1,1)
_Antialiasing("Band Smoothing", Float) = 5.0
_Glossiness("Glossiness/Shininess", Float) = 400
_BumpMap("Normal/Bump Map", 2D) = "bump" {}
_Fresnel("Rim", Range(0,1)) = 0.5
_OutlineSize("Outline Size", Float) = 0.01
_OutlineColor("Outline Color", Color) = (0,0,0,0)
_ID("Stencil ID", Int) = 1
_LightingRamp("LightingRamp", 2D) = "white"{}
}
SubShader
{
Tags { "RenderType" = "Opaque" }
Stencil
{
Ref[_ID]
Comp always
Pass replace
Fail keep
ZFail keep
}
CGPROGRAM
#pragma surface surf Cel fullforwardshadows
#pragma target 3.0
sampler2D _MainTex;
fixed4 _Color;
float _Antialiasing;
float _Glossiness;
sampler2D _BumpMap;
float _Fresnel;
sampler2D _LightingRamp;
half4 LightingCel(SurfaceOutput s, half3 lightDir, half3 viewDir, half atten)
{
float3 normal = normalize(s.Normal);
float diffuse = dot(normal, lightDir);
float delta = fwidth(diffuse) * _Antialiasing;
float diffuseSmooth = smoothstep(0, delta, diffuse);
//float3 diffuseSmooth = tex2D(_LightingRamp, float2(diffuse * 0.5 + 0.5, 0.5));
diffuse = diffuse > 0 ? 1 : 0;
float3 halfVec = normalize(lightDir + viewDir);
float specular = dot(normal, halfVec);
specular = pow(specular * diffuseSmooth, _Glossiness);
float specularSmooth = smoothstep(0, 0.01 * _Antialiasing, specular);
float rim = 1 - dot(normal, viewDir);
rim = rim * diffuse;
float fresnelSize = 1 - _Fresnel;
float rimSmooth = smoothstep(fresnelSize, fresnelSize * 1.1, rim);
half3 nDotL = dot(s.Normal, lightDir);
float3 lightVec = _WorldSpaceLightPos0.xyz;
float attenuation = 1 / (1+dot(lightVec, lightVec));
float3 col = s.Albedo * ((diffuseSmooth + specularSmooth + rimSmooth) * _LightColor0 * attenuation + unity_AmbientSky);
return float4(col, s.Alpha);
}
struct Input
{
float2 uv_MainTex;
float uv_BumpMap;
};
void surf(Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
}
ENDCG
Pass
{
Cull Front
ZWrite off
ZTest on
Stencil
{
Ref[_ID]
Comp notequal
Fail keep
Pass replace
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float _OutlineSize;
float4 _OutlineColor;
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 vertex : SV_POSITION;
};
v2f vert(appdata v)
{
v2f o;
float3 normal = normalize(v.normal) * _OutlineSize;
float3 pos = v.vertex + normal;
o.vertex = UnityObjectToClipPos(pos);
return o;
}
float4 frag(v2f i) : SV_Target
{
return _OutlineColor;
}
ENDCG
}
}
FallBack "Diffuse"
}
What exactly is the purpose of;
float3 lightVec = _WorldSpaceLightPos0.xyz;
float attenuation = 1 / (1+dot(lightVec, lightVec));
For point and spot lights '_WorldSpaceLightPos0' is the world-space position, and as such 'dot(lightVec, lightVec)' returns the square distance of the light from the centre of the world.
That was just my attempt to fix the issue with manual attenuation. Before adding those to the final color value the light was completely white over the terrain, and with it it looks more like actual light. I just never was able to figure out how to fix the weird square issue.
The squares are the actual light volumes that are supposed to be masked by the attenuation. Is there a reason you can't just use the 'atten' input? That is calculated properly for every light type, and your attenuation basically just compiles to '1 / 2' for directional lights, hence why it's less intense.
Answer by Namey5 · Jul 31, 2020 at 08:33 AM
Replace it with atten and remove the ambient lighting calculation - Unity does this for you and doing it in a lighting function is incorrect;
float3 col = s.Albedo * ((diffuseSmooth + specularSmooth + rimSmooth) * _LightColor0 * atten);
Your answer
Follow this Question
Related Questions
How to edit the new particle surface shader? / Make it so particles receive light from all angles 0 Answers
Lighting works for iPad but not iPhone? 0 Answers
Lighting support in Unlit shader 0 Answers
How to write unlit surface shader? 6 Answers
Point light too bright in single-color shader within range of light 1 Answer