HLSL (or ShaderGraph) Skybox Shader with Single Pass Instancing

I am trying to implement a custom skybox shader with HLSL.
I figured however that URP is still using the (legacy?) CG Skybox shader and any shader created with either Shader Graph or HLSL is not rendered at all.

This is my HLSL implementation:

Shader "Skybox/Gradient"
{
    SubShader
    {
        Tags { "Queue"="Background" "RenderType"="Background" "PreviewType"="Skybox" }
        Cull Off ZWrite Off

        Pass
        {
            HLSLPROGRAM
            #pragma target 2.0
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"           

            sampler2D _SkyGradient;

            struct Attributes
            {
                float4 positionOS   : POSITION;     
                float2 uv : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct Varyings
            {
                float4 positionHCS  : SV_POSITION;
                float2 uv : TEXCOORD0;
                UNITY_VERTEX_OUTPUT_STEREO
            };           

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                UNITY_SETUP_INSTANCE_ID(IN);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                OUT.uv = IN.uv;
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                return tex2D(_SkyGradient, float2((IN.uv.y+1.0)*0.5,0));
            }
            ENDHLSL
        }
    }
}

Which is not being rendered. The sky is black and will not even be cleared.

This is the exact same shader in CG but it works:

Shader "Skybox/Gradient Legacy"
{
    SubShader
    {
        Tags { "Queue"="Background" "RenderType"="Background" "PreviewType"="Skybox" }
        Cull Off ZWrite Off

        Pass
        {
            CGPROGRAM
            #pragma target 2.0
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            sampler2D _SkyGradient;

            struct Attributes
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct Varyings
            {
                float4 positionHCS : SV_POSITION;
                float2 uv : TEXCOORD0;
                UNITY_VERTEX_OUTPUT_STEREO
            };

            Varyings vert (Attributes IN)
            {
                Varyings OUT;
                UNITY_SETUP_INSTANCE_ID(IN);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                OUT.positionHCS = UnityObjectToClipPos(IN.vertex);
                OUT.uv = IN.uv;
                return OUT;
            }

            half4 frag (Varyings IN) : SV_Target
            {
                return tex2D(_SkyGradient, float2((IN.uv.y+1.0)*0.5,0));
            }
            ENDCG
        }
    }
}

So I wonder: Is it just not possible to use HLSL for Skybox shaders? Isn't HLSL supposed to replace CG in the new render pipelines?

Your HLSL shader works fine, you just need to assign actual texture to _SkyGradient. And assign your material to the skybox (Window->Rendering->Lighting, Skybox material)

The texture is being assigned via Shader.SetGlobalTexture and I am switching between both shaders in the assigned Sky material.
But this even happens in a shader that just outputs a simple color without any inputs.

Both shaders work in edit mode, non VR mode and Multi Pass rendering and show the correct gradient.
But with single pass instanced rendering the HLSL shader does not render at all.
The sky does not even get cleared, which means the foreground elements "paint" into the sky.
Also it does not display any errors.

The same happens for any shader created with Shader Graph which is why I started impementing it in HLSL in the first place. I thought there might be some stereo macro missing from the generated shader code.

I am using URP 10.6.0 with Unity 2020.3.17f1 btw.

Oh, I missed the "Single pass instancing" part. Hmm, it does reproduce, but not with all graphics APIs.

Try adding "ZClip False" to shader seems to make it work as expected.

ok, so the problem is "single pass instancing" needs different skybox scaling, but when simulating VR it chooses wrong path and scales skybox too much, it ends up outside clipping plane and gets ZClipped. So adding "ZClip False" to your shader will workaround it.

Wow I didn't even know it was possible to disable clipping with a ShaderLab command.
This might also help with my infinite grid renderer.
Glad that you could find the root of the cause.
Are there any plans to update the skybox shaders to use HLSL?
As from my understanding the CG shaders in URP are considered legacy?

Thanks!

https://docs.unity3d.com/Manual/SL-ZClip.html be aware that changing clipping to clamping can result in problems with z ordering.
I am not aware of any plans regarding skybox shaders.

Is it a logged bug regarding simulating VR choosing the wrong path?

[quote=“hippocoder”, post:8, topic: 870591]
Is it a logged bug regarding simulating VR choosing the wrong path?
[/quote]
I am creating a bug report. It only affects simulating VR with MockHMD. On real VR devices there is no problem.

1 Like

[quote=“rjonaitis”, post:9, topic: 870591]
I am creating a bug report. It only affects simulating VR with MockHMD. On real VR devices there is no problem.
[/quote]
This does not only effect MockHMD. It also happens with any OpenVR device I tested (Vive and Index) and I think my colleague also had the same problem on Oculus Quest

[quote=“peaj_metric1”, post:10, topic: 870591]
This does not only effect MockHMD. It also happens with any OpenVR device I tested (Vive and Index) and I think my colleague also had the same problem on Oculus Quest
[/quote]
Ok, got it, I’ll send to QA for more testing, I don’t have these devices on hand.

1 Like

Is there any information on whether this has been fixed yet?

I seem to be experiencing the same issue on a Quest 2 with OpenXR, OpenGL ES and URP in 2022.1. The skybox shadergraph renders fine in Multi-pass mode, but not with Single Pass Instanced. In Android builds it seems like the skybox is cleared with one colour while in editor play mode it is uninitialised instead.

Unfortunately, it does not seem like the ZClip False workaround does anything on my end so that may point towards this being a somewhat different issue with similar symptoms :(

4 Likes

[quote=“rjonaitis”, post:11, topic: 870591]
Ok, got it, I’ll send to QA for more testing,
[/quote]

Can confirm that I’m having this happen on Unity 2021.3.16 / URP 12.1.8 / Oculus runtime - on Quest2 and Index.

Do you have a link to the bug report that was filed so I can see if this was fixed in a later version?

3 Likes