Retro-reflective surface rendering in HDRP

Hi there!

I am trying to create a retro-reflective material (the material on traffic signs, bicycle reflectors, cats eyes, etc.) in HDRP. I have found an example using URP complete with shader code, but it does not translate well to HDRP. In particular, the shader loops through each light to calculate the retro-reflective contribution, something I don’t know how to do in HDRP (I am new to shader programming in Unity).

Can anyone point me in the right direction on how to do this in HDRP? Can I do something in Shader Graph that I don’t know of (I don’t seem to able to do something per light) or maybe with a custom pass? Any ideas are welcome. :slight_smile:

Kind regards,
Michael

Hey,

Your best bet would be to hack the emissive usage of ShaderGraph and put a lighting code inside a custom HLSL node. I’m pretty sure you can access the directional light data and light data (point, spot, reflection probe) defined here: Graphics/Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/ShaderVariablesLightLoop.hlsl at master · Unity-Technologies/Graphics · GitHub

You’d need to loop through all the lights in the custom node and apply your custom lighting inside. Putting the resulting computation in the emissive is hacky but it could work since you don’t have access to lighting customization in ShaderGraph.

The other alternative is to change the code of the package directly by adding a new type of BRDF but that’s a lot more involved. There is a framework in HDRP to do this, for example, the AXF shader was developed with it but there is no documentation about this process: Graphics/Packages/com.unity.render-pipelines.high-definition/Runtime/Material/AxF at master · Unity-Technologies/Graphics · GitHub

1 Like

Hi Antoinel,

Thank you for taking the time to reply!

I have tried the first approach, since I think the second one is over my head and also possibly harder to maintain with newer Unity versions. I can’t get it to work, however. I am getting this error:

Here is my initial test shader file (a .cginc file):

#ifndef LIGHT_LOOP_TEST
#define LIGHT_LOOP_TEST

#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/ShaderVariablesLightLoop.hlsl"

void calculate_lights_float(out float intensity)
{
    intensity = 0.0f;
    for (int i = 0; i < 10; i++)
    {
        intensity += 0.1f;
    }
}

#endif // LIGHT_LOOP_TEST

The include path is correct and if I follow some of files included by ShaderVariablesLightLoop.hlsl I eventually get to one where there is a _ViewMatrix at line 15!

Do you know how to solve this or where I can learn more about this approach?

Kind regards,
Michael

You don’t need this include, it’s already there in the shader. Also this code will not work for the preview of the node and it has to be taken out explicitly (like any HDRP specific code). I think this is why you have this error

Like this:

#ifndef LIGHT_LOOP_TEST
#define LIGHT_LOOP_TEST

#ifndef SHADERGRAPH_PREVIEW

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonLighting.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/PunctualLightCommon.hlsl"

void calculate_lights_float(float3 positionWS, out float intensity)
{
    uint lightCount = _PunctualLightCount;
    uint lightStart = 0;

    float total = 0;

    for (int i = 0; i < _DirectionalLightCount; i++)
    {
        DirectionalLightData dirLight = _DirectionalLightDatas[i];

        total += 1;
    }

    for (int i = lightStart; i < lightCount; i++)
    {
        LightData light = _LightDatas[i];

        float3 L;
        float4 distances;
        GetPunctualLightVectors(positionWS, light, L, distances);
        float attenuation = PunctualLightAttenuation(distances, light.rangeAttenuationScale, light.rangeAttenuationBias,
                                        light.angleScale, light.angleOffset);

        total += attenuation;
    }

    intensity = total;
}

#else // SHADERGRAPH_PREVIEW

// Can't get lighting info inside ShaderGraph
void calculate_lights_float(float3 positionWS, out float intensity)
{
    intensity = 0;
}

#endif  // SHADERGRAPH_PREVIEW

#endif // LIGHT_LOOP_TEST

Ah. Thank you! It works now.

Shadows will not be taken into account with this emissive approach, of course, which would have been nice.

I wish there was a retroreflective shader feature that could be turned on in ShaderGraph settings somewhere. Maybe a retroreflective material type or an option for an existing type. The only special thing would be to adjust the normal toward each individual light in the light loop and calculate a retroreflective contribution, like the URP example, I think. Maybe I should make a feature request from your roadmap?

Thank you for your time. :slight_smile:

That’s correct, you can still sample the shadow maps In the code of the custom node but it’s a bit more complicated.

You can try making a feature request but since it’s a niche case it’s unlikely that it’ll get prioritized. Instead you can ask for custom lighting to be implemented in HDRP :slight_smile:

Hi,

We had/have the same need for our project.
One valuable and insightful thread/discussion to start with was the following: Retroreflective / high-visibility shader

The trick to implementing this shader is to change the normals of the retroreflective surface so that they face the camera.

We created a shader with Shader Graph according to this trick.

And then, few days ago, we found out the “Test Track” sample already implemented such a shader (with Shader Graph):


Assets\External\Shadergraphs\Reflector.shadergraph

Regards,

Tiraflo

Hi Tiraflo,

Thank you for the tip! I ended up with something similar; Pointing the normals toward a specific light source (in my case a search light) but that required updating the lights position (and is only correct for that light). Your approach is much simpler. I don’t know if it is as physically accurate as what I was trying together with Antoinel, but I like the simplicity and maintainability.

Regards,
Michael