Is it possible to render a transparent shader in deferred, if it's only used as a decal above opaque geometry?

Hi there,

I’ve written a simple surface shader (copied below) that i’ve been using to add edge textures (small floating planes slightly extruded) onto solid geometry. This generally works great.

One problem is, naturally this transparent shader always gets rendered forward, which doees actually bring some problems with the amount of lights I want to use.

As you can see below, it’s entirely excluded from the albedo deferred pass.

Now, given the limitation that these are never going to be rendered transparent, and always resting on top of solid geomeetry - is it possible to push these into the deferred pass? It’s my understanding that this is possible, but might require a fragment shader, rather than a surface shader?



The shader is as below, obviously trying to force the lightmode to deferred didn’t do anything - so it seems something more low level is needed?

Shader "Project/DecalDiffuse"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _Emission("Emission", Color) = (0,0,0,0)
        _MainTex ("Diffuse Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags 
        {
            "RenderType" = "Opaque" 
            "IgnoreProjector" = "True" 
            "Queue" = "Geometry" 
            "LightMode" = "Deferred"
        }
        LOD 200

        CGPROGRAM
        #pragma surface surf WrapLambert exclude_path:deferred decal:blend 



        half4 LightingWrapLambert (SurfaceOutput s, half3 lightDir, half atten) 
        {
            half NdotL = dot (s.Normal, lightDir);
            half diff = NdotL * 0.5 + 0.5;
            half4 c;
            c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten);
            c.a = s.Alpha;
            return c;
        }

        struct Input 
        {
            float2 uv_MainTex;
            half4 color : COLOR;
        };
    
        sampler2D _MainTex;
        fixed4 _Color;
        fixed4 _Emission;

        void surf (Input IN, inout SurfaceOutput o) 
        {
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
			o.Albedo = c.rgb * IN.color.rgb;	
			o.Alpha = c.a * IN.color.a;
            o.Emission = _Emission;
        }
        ENDCG
    }
}

Still struggling quite a bit with this one.

I did get passable results using dithered transparency, and clipping:

But, obviously this is a bit iffy, and especially suffers at a distance / at low resolutions:

Writing a basic fragment shader, I’m a little bit stuck on how to actually achieve this, and very new with writing to the GBuffer / deferred in general:

Fragment shader source from Catlike coding: Rendering 13

Not making much progress here, so here’s what I understand so far, if anyone is able to help:

  • To do this, I’d need to write the results of my transparent texture over what’s in the existing gBuffer.
  • and a fragment shader is required for this, is that right?
  • You can’t reference the value of the existing gBuffer while writing an output, and not everything is finalised yet.
  • The way around this is to use something called the “finalgbuffer”.

Unfortunately that’s where the trail runs cold, I can’t find much of any documentation on finalgbuffer outside of a few Unity forum threads, but not any real code examples:

Currently when trying to use this, it doesn’t seem to do anything (I would expect this to be all black?).

#pragma vertex vertDeferred
#pragma fragment fragDeferred

#include "UnityStandardCore.cginc"

// Adding finalgbuffer
#pragma finalgbuffer:finalGBuffer
void finalGBuffer(
    VertexOutputForwardBase IN,
    SurfaceOutputStandardSpecular o,
    inout half4 outDiffuse : SV_Target0,            // RT0: diffuse color (rgb), occlusion (a)
    inout half4 outSpecSmoothness : SV_Target1,    // RT1: spec color (rgb), smoothness (a)
    inout half4 outNormal : SV_Target2,            // RT2: normal (rgb), --unused, very low precision-- (a)
    inout half4 outEmission : SV_Target3            // RT3: emission (rgb), --unused-- (a)
) {
    outDiffuse = half4(0, 0, 0, 1);
}

Have you seen this? Looks like what you need from description

1 Like

Hiya!

Yes thanks I’ve come across that, though unfortunately couldn’t quite piece together how they were doing it, but it’s very close to what I want to achieve (though what I’m looking for is way simpler, just a diffuse overlay).

I was really interested in implementing this myself, but I suppose this is a fallback option.

are there grab passes in your version of unity? since you can reuse a single grabpass for as many models as you like by sharing a name, you could conceivably have all decals be able to blend by just reading the grabpass and blending the decal color with it however you like.

i use the grabpass in forward to render planar reflections (and share a grabpass with water!), and blend just like this, but i don’t know the intricacies of writing for deffered.

1 Like

Hiya!

Thanks, unfortunately I don’t believe you can use a grab pass in deferred rendering? For similar reasons of grabbing the gbuffer / finalgbuffer.

yeah, in that case i think your only option for that method would be to do it in forward with another camera, but that probably won’t be amazing for your performance.

Oh sorry no I maybe wasn’t clear in the original post - the main point is to do this in deferred.

Yes it’s super trivial to do in forward rendering :slight_smile:
Good to know that it’s possible, WRT deferred decals linked above, just finding out how! :sweat_smile:

1 Like