#Why is my pre-rendered depth setup ignored in WebGL build?

1 messages · Page 1 of 1 (latest)

copper mist
#

I am working on a pre-rendered background setup in URP where I inject a pre-baked depth texture (a Raw/non linear.exr) into the camera's depth buffer. The goal is to have 3D geometry correctly cull against this static background. This exr is exported from Unity itself, therefore its captured in DX12, and has reversed z buffer

This setup works perfectly in the Unity Editor (Video 1), geometry behind the pre-rendered depth is occluded as expected. I am also additionally drawing a color pass after depth before opaque to display the pre rendered image.

However, in the WebGL build running on Itch.io, the depth injection seems to be ignored or incorrect (Video 2); the 3D geometry renders with no culling and even the color pass becomes invisible. I was only able to recreate this issue in editor when I intentionally made depth inject shader throw an error. If that is true question is why is it happening on webgl? How can I make my shader compatible?

#

and if you say it must be graphics quality settings, i have only one quality setting called "simple" which is being used by web too. I have tried almost everything and i still cant to figure out why its happening.

nimble island
#

For example, one platform might be using 0.0-1.0 values, while another -1.0-1.0. And the y axis might be inverted.

#

And remappijg might not be possible due to non linear scale.

copper mist
#

@nimble island but my shader already accounts for that. my pre rendered depth texture is non linear/Raw & captured in DX12, so it reversed z (near 1.0 & far 0.0). Using UNITY_UV_STARTS_AT_TOP & defined(UNITY_REVERSED_Z) i can make it compatible. Am i doing anything wrong here?

{
    Properties 
    { 
        _DepthTex("Depth", 2D) = "black" {} 
    }
    SubShader
    {
        Tags { "RenderPipeline" = "UniversalPipeline" }
        Pass
        {
            Name "InjectDepth"
            ZTest Always ZWrite On Cull Off

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            struct Varyings 
            { 
                float4 positionCS : SV_POSITION; 
                float2 uv : TEXCOORD0; 
            };

            TEXTURE2D(_DepthTex); SAMPLER(sampler_DepthTex);

            Varyings vert(uint vertexID : SV_VertexID) 
            {
                Varyings output;
                float2 uv = float2((vertexID << 1) & 2, vertexID & 2);
                
                output.positionCS = float4(uv * 2.0 - 1.0, UNITY_NEAR_CLIP_VALUE, 1.0);
                #if UNITY_UV_STARTS_AT_TOP
                    output.positionCS.y *= -1;
                #endif
                output.uv = uv;
                return output;
            }

            struct FragmentOutput 
            {       
                float depth : SV_Depth;
            };

            FragmentOutput frag(Varyings input)
            {
                FragmentOutput outData;

                float d = SAMPLE_TEXTURE2D(_DepthTex, sampler_DepthTex, input.uv).r;
                #if !defined(UNITY_REVERSED_Z) 
                    d = 1.0 - d;
                #endif
                outData.depth = d;
                return outData;
            }
            ENDHLSL
        }
    }
}```
nimble island
#

I'm not sure if that's enough. The simplest solution would be to bake it on the target platform once and then include it corresponding texture to each platform build.
If that doesn't work, then perhaps there is deeper problem.

Did you test in in a windows build BTW?

#

That would also help confirm if your reading logic is truly platform independent.

copper mist
solar dove
#

Writing to SV_Depth isn't supported on all platforms, this -might- be one of them.

Otherwise, assuming it is supported, you shouldn't need to bake depth per-platform, as long as you can (perfectly) reconstruct the linear to hardware depth conversion. (Depends on graphics API and whether reverse Z is used) Precision will be a challenge though, so will have to be 32-bit float texture in the 0 to 1 range for the best precision.

Eg if you store your depth as a normalized linear value from 0 to 1, and have the near/far range that it was originally captured with, you'd reconstruct the linear depth via "lerp (originalNear, originalFar, capturedValue)" and then converting to hardware depth is basically..

"(linearDepth * projectionMatrix[2,2] + projectionMatrix[2, 3]) / linearDepth".

Not 100% sure on the math here (Because it can vary a lot based on API and other things), but might work, otherwise you'll have to do a bit of digging into the exact conventions behind the graphics API. If you can make it work based on the unity-supplied projection matrix it should work for all APIs tho

#

A simple test case would be to place a cube at say, 1 meter away from the camera on the Z axis, and then capture this into a cubemap, and see if you can get mostly the same results from an actual render and from a depth cube render in a program like render doc.

copper mist
#

@solar dove thanks so much for responding!

Writing to SV_Depth isn't supported on all platforms, this -might- be one of them.

Otherwise, assuming it is supported, you shouldn't need to bake depth per-platform, as long as you can (perfectly) reconstruct the linear to hardware depth conversion. (Depends on graphics API and whether reverse Z is used) Precision will be a challenge though, so will have to be 32-bit float texture in the 0 to 1 range for the best precision.
Based on some google research i came to know WebGL 2.0 does support SV_Depth but is that incorrect? Precision of texture is 16bit float single channel, I though high precision could be bad for webgl. also its works good enough visually in editor as well.
Eg if you store your depth as a normalized linear value from 0 to 1, and have the near/far range that it was originally captured with, you'd reconstruct the linear depth via "lerp (originalNear, originalFar, capturedValue)" and then converting to hardware depth is basically..

"(linearDepth * projectionMatrix[2,2] + projectionMatrix[2, 3]) / linearDepth".
I thought depth is always stored in 0-1 but its their clip space that varies.
Not 100% sure on the math here (Because it can vary a lot based on API and other things), but might work, otherwise you'll have to do a bit of digging into the exact conventions behind the graphics API. If you can make it work based on the unity-supplied projection matrix it should work for all APIs tho
the graphics API im targetting as mentioned is WebGL, any suggestions on what should i do next
A simple test case would be to place a cube at say, 1 meter away from the camera on the Z axis, and then capture this into a cubemap, and see if you can get mostly the same results from an actual render and from a depth cube render in a program like render doc.
how do i capture into a cubemap? currently im capturing them into a texture 2D and injecting before opaques are rendered as shown in the gif.

copper mist
unreal geyser
#

Only problem I've had with WebGL and depth is inverting the depth because API differences but other than that I've been fine. Shader graph may provide more automation between APIs if anything

#

Hmm, but I do recall having to invert the depth comparisons in shader graph recently so may not be entirely true

copper mist
# unreal geyser Only problem I've had with WebGL and depth is inverting the depth because API di...

so any guesses what could be the issue in my case? as i mentioned before I only am able to recreate such scenario in editor when I intentionally make the shader code throw an error. type something random and save it. same scenario happens, so at least for now my best guess is on webgl build the shader is throwing error. But its not showing anything on web console (don’t know if it’s something that is logged). Assuming that’s the case just can’t figure out why?

copper mist
nimble island
nimble island