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

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
    }
}

1 Like

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

1 Like

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);
}

1 Like

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

2 Likes

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

Not making any great progress on this. I can’t seem to find any information on the finalgbuffer at all.

It definitely exists, and I’m imagining I can blend it by the existing buffer multiplied by (1 - the alpha of the texture) to blend these decals in the deferred pass, but the documentation is elusive.

This might actually work, and funnily enough it doesn’t use finalgbuffer at all.

The one problem with this is that it outputs using VertexOutputDeferred as the fragment input, which doesn’t carry any vertex colour information. If there’s a way you can access vertex colour information with that input, perfect.

Just want to amend, I only want to output to the first gBuffer, 0 (albedo only) to blend in semi transparent textures (and solid textures being multiplied by the vertex colour alpha). Everything else is not required.

void fragDeferredDull(
	VertexOutputDeferred i,
	out half4 outGBuffer0 : SV_Target0,
	out half4 outGBuffer1 : SV_Target1,
	out half4 outGBuffer2 : SV_Target2,
	out half4 outEmission : SV_Target3          // RT3: emission (rgb), --unused-- (a)
#if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4)
	, out half4 outShadowMask : SV_Target4       // RT4: shadowmask (rgba)
#endif
)

i.e. therer’s no equivalent of color :COLOR0 as far as I can tell?

 struct v2f
        {
            float4 pos : SV_POSITION;
            float3 normal : NORMAL;
            float2 uv_Main : TEXCOORD0;
            float2 uv_Overlay : TEXCOORD1;
            fixed4 color : COLOR0;
        };
struct VertexOutputDeferred
{
    UNITY_POSITION(pos);
    float4 tex                            : TEXCOORD0;
    float3 eyeVec                         : TEXCOORD1;
    float4 tangentToWorldAndPackedData[3] : TEXCOORD2;    // [3x3:tangentToWorld | 1x3:viewDirForParallax or worldPos]
    half4 ambientOrLightmapUV             : TEXCOORD5;    // SH or Lightmap UVs

    #if UNITY_REQUIRE_FRAG_WORLDPOS && !UNITY_PACK_WORLDPOS_WITH_TANGENT
        float3 posWorld                     : TEXCOORD6;
    #endif

    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};
1 Like

Can you add your vertex color into this sctruct as : TEXCOORD7 ?

Ah, as far as I understand, no - unfortunately that VertexOutputDeferred is a built in struct and can not be easily edited to the best of my knowledge?

Still not making any great progress on this unfortunately.
I’ve been going through this thread for more information on using the finalgbuffer, but unfortunately didn’t come across any real code examples of how to properly use the finalgbuffer:

Haven’t made any progress on this unfortunately, so just bumping in-case anyone familiar with gBuffer stuff sees it.

1 Like