Is there a way around this? I struggled to find anything at all about this on the forums or online, so seems like something that’s either trivial to solve, or there’s a basic alternative I’m missing.
The shader code is as below. The clipping is done via the distance from the camera to the surface of the shader.
It appears the custom lighting function “LightingWrapLambert” is causing this issue.
If using the regular Lambert lighting, no issue occurs.
Not sure how to resolve that, presumably need to also clip in the lighting function, as the default lambert function must be doing this, but where can I find the code for that?
Deferred rendering generally only supports one shading model. The “Standard” shading model in the case of Unity’s BIRP. When you use the built in Lambert shading in a Surface Shader, it approximates Lambert with 0.0 smoothness and black specular Standard shading, which isn’t the same as Lambert, but close enough for most people. Humorously the Standard shading model can 100% match Lambert using a smoothness of 1.0 and a black specular, but that’s not how Unity chose to implement it.
When you use a custom lighting model, Unity has to render that object using forward rendering instead. And basically the deferred rendering path is entirely skipped… or so you might think.
Many things in Unity make use of the camera depth texture. When using deferred rendering, the depth texture is generated while rendering the deferred GBuffers. This means all forward rendered opaque surfaces are still rendered into the GBuffers, but do so using their shadow caster pass. Since your surface shader does not have a custom shadow caster pass, it renders into the GBuffer depth as fully solid, leading to the weirdness you’re seeing.
The solution is… add addshadow to your #pragma surface line so your Surface Shader generates a custom shadow caster!
At least it should be. Unfortunately it’s not quite that straightforward, as there are two issues with doing that. The first one is a long time bug with Surface Shaders that screenPos isn’t calculated for Surface Shader generated shadow caster passes. So you have to use a vertex function to calculate it manually instead.
The second issue is now your object’s real time shadows will also potentially get dithered, not based on the player’s camera, but based on the light position. Though that might not be an issue for you if you’re not using real time shadows. If you are, you have to try to detect if the shadow caster pass is being used to render during the shadow map rendering or for camera depth rendering, which isn’t 100% accurate.
My suggestion to you is, stick with forward rendering and don’t use deferred. Looking at your screenshots it doesn’t seem like you’re using a lot of dynamic lights, or intend to use the Standard shading model on most surfaces. Those are the only reasons to use deferred.
Hmm, I maybe spoke too soon - other transparent objects (cutout / clip transparency) in the background seem to be drawn on top of the dithered objects:
All the shaders have “addshadow”, but I’m unclear how the screenpos would play into this.
Switching the lighting model back to “Lambert” from my custom “WrapLambert” fixes this issue - what’s going on there?
The basic lambert lighting model seems to work flawlessly when you set it (even if like you say, it’s fake lambert with a standard shader), so not sure why this custom model is different and what needs to change.
I don’t quite understand this? Shaders with the custom lighting model are being lit deferred, with arbitrary number of dynamic lights per mesh (I tried over 50).
Edit: I’m not sure the above is true actually, maybe the editor was accidentally displaying it without the custom lighting model… Yes, using my custom lighting model only allows 8 lights.
Deferred shaders supports Clip function. What you looking for is probably it + you’ll need to add dithering effect before clip, shading models not matters.