Getting Cameras View Direction in Custom Post Process Shader

Hello,

this issue has already cost me several days, so I hope someone here can get me on the right track. I’m currently trying to follow a YouTube series from the wonderful Sebastian Lague on creating a universe with gravity, generated planets including atmosphere. He seems to have done it with the built-in renderer, for several reasons I’m trying to follow the tutorial in HDRP.

I’m stuck creating atmospheres in a custom Post Process, making use of a raySphere function provided by Sebastian. You can see the bit I’m currently working on in his video up until 2:40:

I was able to implement the atmosphere functionality with the following shader (most parts obviously copied from Sebastians GitHub repo):

Shader "Hidden/Shader/Atmosphere"
{
    HLSLINCLUDE

    #pragma target 4.5
    #pragma only_renderers d3d11 playstation xboxone vulkan metal switch
  
    #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
    #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"
    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/FXAA.hlsl"
    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/RTUpscale.hlsl"
   
    float3 _WorldSpaceCameraMainPos;
    matrix _InvCamProjMatrix;

    struct Attributes
    {
        uint vertexID : SV_VertexID;
        float2 uv : TEXCOORD0;
        UNITY_VERTEX_INPUT_INSTANCE_ID
    };

    struct Varyings
    {
        float4 positionCS : SV_POSITION;
        float2 texcoord   : TEXCOORD0;
        float3 viewVector : TEXCOORD1;
        float2 uv   : TEXCOORD2;   
    };

    Varyings Vert(Attributes input)
    {
        Varyings output;
        UNITY_SETUP_INSTANCE_ID(input);
        UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
        output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID);
        output.texcoord = GetFullScreenTriangleTexCoord(input.vertexID);
        output.uv = input.uv;
        output.viewVector = mul( _InvProjMatrix, float4(input.uv * 2 - 1, 0, -1));
        output.viewVector = mul(UNITY_MATRIX_M, float4(output.viewVector,0));
        return output;
    }

    float LinearEyeDepth(float z)
                {
                    return 1.0 / (_ZBufferParams.z * z + _ZBufferParams.w);
                }

    static const float maxFloat = 0;//3.402823466e+38;
   

    // Returns vector (dstToSphere, dstThroughSphere)
    // If ray origin is inside sphere, dstToSphere = 0
    // If ray misses sphere, dstToSphere = maxValue; dstThroughSphere = 0
    float2 raySphere(float3 sphereCentre, float sphereRadius, float3 rayOrigin, float3 rayDir) {
        float3 offset = rayOrigin - sphereCentre;
        float a = dot(rayDir, rayDir);// if rayDir might not be normalized
        float b = 2 * dot(offset, rayDir);
        float c = dot(offset, offset) - sphereRadius * sphereRadius;
        float d = (b * b) - (4 * a * c); // Discriminant from quadratic formula

        // Number of intersections: 0 when d < 0; 1 when d = 0; 2 when d > 0
        if (d > 0) {
            float s = sqrt(d);
            float dstToSphereNear = -b - s;
            float dstToSphereFar = -b + s;

            // Ignore intersections that occur behind the ray
            if (dstToSphereFar < 0) {
                dstToSphereFar = -1;
            }

            if (dstToSphereNear < 0) {
                dstToSphereNear = -1;   
            }

            dstToSphereNear = dstToSphereNear / (2 * a);
            dstToSphereFar = dstToSphereFar / (2 * a);

            return float2(dstToSphereNear, dstToSphereFar - dstToSphereNear);

        }
        // Ray did not intersect sphere
        return float2(-1, 0);
    }

    // List of properties to control your post process effect
    float _Intensity;
    TEXTURE2D_X(_InputTexture);

    float3 planetCentre;
    float atmosphereRadius;
    float planetRadius;

    float4 CustomPostProcess(Varyings input) : SV_Target
    {
        // scenedepth
        float sceneDepthNonLinear = SampleCameraDepth(input.uv);
        float sceneDepth = LinearEyeDepth(sceneDepthNonLinear) * length(input.viewVector);

        // viewdirection
        PositionInputs posInput = GetPositionInput(input.positionCS.xy, _ScreenSize.zw, sceneDepth, UNITY_MATRIX_I_VP, UNITY_MATRIX_V);
        float3 viewDirection = GetWorldSpaceNormalizeViewDir(posInput.positionWS);//posInput.positionWS);
       
        // rayorigin
        uint2 positionSS = input.texcoord * _ScreenSize.xy;
        float  deviceDepth = LoadCameraDepth(positionSS);
        float2 positionNDC = positionSS * _ScreenSize.zw + (0.5 * _ScreenSize.zw);
        float3 positionWS = ComputeWorldSpacePosition(positionNDC, deviceDepth, UNITY_MATRIX_I_VP);
        
        // raysphere                                  
        float3 rayOrigin = GetAbsolutePositionWS(positionWS);
        float3 rayDir = normalize(viewDirection);

        float2 hitInfo = raySphere(planetCentre, atmosphereRadius, rayOrigin, rayDir);
        float dstToAtmosphere = hitInfo.x;
        float dstThroughAtmosphere = min(hitInfo.y, sceneDepth - dstToAtmosphere);
             
        // add to color
        float4 outColor = LOAD_TEXTURE2D_X(_InputTexture, positionSS);                    
        return outColor + (dstThroughAtmosphere / (atmosphereRadius * 2));
    }
   

    ENDHLSL

    SubShader
    {
        Pass
        {
            Name "Atmosphere"

            ZWrite Off
            ZTest Always
            Blend Off
            Cull Off

            HLSLPROGRAM
                #pragma fragment CustomPostProcess
                #pragma vertex Vert
               
            ENDHLSL
        }
    }
    Fallback Off
}

which renders the correct output, looking directly at the planet:


This is wonderful and exactly what I want (and Sebastian does in his video). Strange things start to happen when I turn the camera exactly 180 degrees, having the planet in my back. The effect still renders:


Also, when returning to scene view, the effect generates strange artifacts:

It seems like the outgoing ray goes forwards and backwards, therefore rendering the effect even when looking away from the planet. I can’t wrap my head around the problem in the scene. I tweaked several parts of the shader code, but was not able to track the error down. Does somebody have an idea?

This is on Unity 2020.1.11f1, HDRP, HLSL.

Do you have a minimal Custom Post Process script for the shader, if anyone wants to take a look at this?

What if you check through your post processing script, if the viewing direction is correct. The same as with tracking UI elements on an object for example. I think it is the z axis on the view direction.

Yup, Erathor is right.
I’ve fixed a similar issue in the water shader by Sebastian Lague mixed with plehmann1991’s HDRP version by diverting the ray direction vector.

Works great!