I’m fighting for the performance right now and I’m combining different meshes into one, drawing it with a single material/shader.
I have a shader pragma like this:
#pragma surface surf Standard vertex:vert noinstancing fullforwardshadows addshadow
And my main problem that I want to control the “fullforwardshadows” by myself. Meaning that some submeshes will have the fullforwardshadows behaviour and some – not. I’m going to control this with vertex color or UV channel.
My idea is to write the shadow receiving block by myself, but I didn’t find any info on that. I’d like to piece of code to which the “fullforwardshadows” keyword is unfloded.
I’ve tried to view the generated shader code and there’s nothing useful there.
The difference between including fullforwardshadows and not including it is a single line.
With fullforwardshadows the "ForwardAdd" pass uses #pragma multi_compile_fwdadd_fullshadows, otherwise it uses #pragma multi_compile_fwdadd.
That’s it.
That multi compile line changes between if the shader includes the shadow receiving keyword variants for forward add passes or not. And you can’t swap to different multi_compile lines with keywords from another multi_compile or shader_feature. That is not something Unity allows for, as all variants need to be known at compile time, and that particular built in multi_compile also likely sets some internal flags for that shader depending on it existing or not.
Now you can prevent shadows from appearing using a custom keyword by overriding some internal defines. The shader will still try to use the shadow receiving version of the shader, but because of the way Unity’s code works for sampling shadows you can force it to return 1.0 (no shadow) instead of actually sampling the shadow map when it attempts to. But that doesn’t help you with what you’re trying to do since you want to modify the shadow receiving intensity effectively per pixel.
Those keywords enable what different macros do deep inside Unity’s shader code several steps away from anything that appears even in the generated shader code. The generated shader code usually calls lighting functions that are defined elsewhere (like UnityLighting.cginc or UnityPBRLighting.cginc), which in turn call the light functions (in AutoLight.cginc) which in turn call the shadow functions (in UnityShadowLibrary.cginc). HLSL has no ability to override functions, but can override macros, which most of the lighting code uses to jump between different sets of functionality. But you can only do that at compile time, not per pixel. So to change this behavior would require modifying one or potentially several different .cginc files, though Surface Shaders can make this harder as it’ll usually load the real ones before any code from a Surface Shader is included, and the first version of a file that’s included is the one that gets used.
But there is a way to do this without any of that.
There’s a “shadow strength” setting on lights. That gets passed to shaders using a uniform shader variable. Specifically _LightShadowData.r. There’s a fun hack where you can modify inputs to a shader to override the value for that one vertex / fragment. That means you all you need to do is stick this in your surf function:
// assumes a vertex color alpha of 0.0 means no shadow, 1.0 means normal shadow
// also requires you add the color to the Input struct
_LightShadowData.r *= IN.vertexColor.a;
Because the surf function runs before the shadow sampling happens, but the time it does that shadow strength value will have been modified and you can turn off shadow receiving per pixel.
Adding the _LightShadowData.r = 0 line doesn’t change anything. I tried to null the whole light variable by _LightShadowData = 0, I tried to move that into the vertex function – the result is the same, nothing’s changed.
(Both primitives – the cube and the plane have the same material using this shader)
Asking just in case of there’s another obscure hack available –
Is there any way to disable directional shadow receiving in the shader code, as it was done for point lights? (To control it with some parameter, not making a separate shader variation).
That one is a little tougher. Directional shadows are rendered into a full screen texture before your object even renders its shader, and the object shader just samples that texture. Shadow strength is used to modify the shadow when it’s rendered to that texture. So no, you can’t modify the shadow strength with a simple hack. For that you would need to go down the rabbit hole of deeper modifications.