Looking to write a shader that only renders the bump map

What I want to make is a shader that renders only the lighting (shadows/highlights/etc) generated by a bump map. I want the rest to blend with what is behind it. I want to use this for tire tracks on dirt kind of like the picture below.

void surf (Input IN, inout SurfaceOutput o) {
    fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    o.Albedo = _Color.rgb;
    o.Normal = UnpackNormal(tex2D(_BumpMap,
    IN.uv_BumpMap));
    o.Alpha = _Color.a;
}

One way I can think of is using blending to multiply the ground below by the Normal lighting.

The other is to take the lighting and insert it into o.Alpha.

I’m not sure where to start on either of these two approaches. Can someone point me in the right direction?

There’s no simple way with just a surface shader unless you do a somewhat expensive method, which is to do a GrabPass, set that as your output color, as well as sample the DepthNormals texture and blend your normal with that and set it as output. But an issue with this is that the final normals output are going to be adjusted by the object you’re rendering this shader on…

Another option, if using Deferred rendering, is to simply use a vert/frag shader to output directly to the Normals GBuffer and everything works simply, no grab pass needed.

Okay, got it, thanks Invertex. Unfortunately both of those options won’t work for the project. I guess I was hoping to be able to toss out the Albedo output data and leave behind only the lighting generated by the Normals.

Well, technically that’s what a surface shader in Transparent mode does when it’s at 100% transparency. All you see is the specular highlights. If that’s all you want that could work. Just output your o.Normal as you want and full transparency alpha.

Only problem with that approach would be even the flat areas of this overlaid normals mesh are going to affect lighting if you ever look at the surface off-angle instead of perfectly straight-on at all times. If this is an orthographic view you’re working with then it shouldn’t be a big issue, but some bright lights could still show even looking directly at the surface unless the Smoothness is 0 or 1

You could get around this somewhat by discard/clip()'ing the pixels away where you normal map is a flat color though (but that wouldn’t look great in many cases either as specular might end up being missing at the peaks of bumped areas for example). The other option would be to add your own lightingModel to the shader as well (since you’re working with Forward rendering right?), that way you can control how the lighting is applied.

You can find some simple custom lighting model examples here:
https://docs.unity3d.com/Manual/SL-SurfaceShaderLightingExamples.html

When I set the alpha to 0, the bumpmap/normal highlights and shadows are set to the same alpha, so the tire tracks disappear. I’m not using any specular since the surface is very rough (dirt). It seems that Alpha effects not just the diffuse but all other outputs.

The view shouldn’t be a problem since the camera is always going to be pretty perpendicular to the tire marks (top down game).

Yes, I’m using forward renderer but the lighting doesn’t seem to be the issue.

This is my current shader

Properties
    {
        _Color ("Main Color", Color) = (1,1,1,1)
        _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
        _BumpMap ("Normalmap", 2D) = "bump" {}
    }

    SubShader
    {
        Tags { "RenderType" = "Transparent" "Queue" = "Transparent" }
        Blend SrcAlpha OneMinusSrcAlpha
        ZWrite Off
        CGPROGRAM
        #pragma surface surf BlinnPhong vertex:vert fullforwardshadows alpha:fade
        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _BumpMap;
        fixed4 _Color;

        struct Input
        {
            float2 uv_MainTex;
            float2 uv_BumpMap;
            float4 color : COLOR;
        };

         void vert (inout appdata_full v, out Input o)
         {
            UNITY_INITIALIZE_OUTPUT(Input,o);
            o.color = v.color; // Save the Vertex Color in the Input for the surf() method
         }

        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));
            o.Alpha = c.a * IN.color.a;
        }
        ENDCG
    }

Ok now that I see your use case…

Well, without any specular, normal maps don’t do much. Even dirt has some specular, it’s just rough, but it’s there enough that bumps would affect the rough lighting of it to make some shape visible.

Bump maps don’t cast shadows, they’ll simply be darker the greater the angle away from any light sources is. And making an object transparent will indeed lose that part of it, which is why I suggested making your own lightingModel function. This would allow you to output the bright and dark shading and do a multiplicative shader blending. All a surf() function is doing is providing basic inputs to a given lighting model to then process how it sees fit. You can take control of that next step with a custom lightingModel. The lightingModel will only be specific to that shader, it won’t affect the rest of the scene.

Another option would be to just make it a vert/frag shader, multiplicative blend mode, and calculate a black/white output based on light direction/intensity and environment lighting.

Oh, very nice! I think I understand now. Cheers!