I am building a 3d renderer in C++ using Vulkan.hpp. I am trying to implement Spherical Harmonics (SH) coefficients for diffuse radiance as part of global illumination. To do this I am generating two cube maps: an image cube map and a depth cube map at the probe location. These cube maps are generated by directly rendering the scene 6 times at the probe location and copying the output image to the faces of the cube map. The depth cube map is rendered to a range of 0 to 1. I want to set up a compute shader to calculate the SH coefficients. I also want to do depth testing to ensure the radiance calculated from the SH coefficients doesn't cause and light bleeding and correctly produces ambient shadows. I have the start of the compute shader I want to generate and I have a starting function to incorporate the SH coefficients into the PBR lighting shader. I'm not sure if what I have started with is the right approach though. I have depth testing occurring in the compute shader where I may also need to do testing in the lighting fragment shader. Can you complete the below compute shader or modify it accordingly? Please make any adjustments required to ensure directional consistency and to address negative irradiance values. I have also included the extract from the lighting shader that can also be modified if needed.

Lighting Shader extract:

layout(scalar, set = 0, binding = 18) buffer SHBuffer {
    vec3 shCoeffs[];
} shBuffer;


vec3 evalDiffuseIrradiance(vec3 normal) {
    float x = normal.x;
    float y = normal.y;
    float z = normal.z;

    // SH basis functions - same logic as in compute shader, scalar only
    float shBasis[9];
    shBasis[0] = 0.282095;         // 0: Y0^0
    shBasis[1] = 0.488603 * y;     // 1: Y1^-1
    shBasis[2] = 0.488603 * z;     // 2: Y1^0
    shBasis[3] = 0.488603 * x;     // 3: Y1^1
    shBasis[4] = 1.092548 * x * y; // 4: Y2^-2
    shBasis[5] = 1.092548 * y * z; // 5: Y2^-1
    shBasis[6] = 0.315392 * (3.0 * z * z - 1.0); // 6: Y2^0
    shBasis[7] = 1.092548 * x * z; // 7: Y2^1
    shBasis[8] = 0.546274 * (x * x - y * y); // 8: Y2^2

    vec3 irradiance = vec3(0.0);

    for (int i = 0; i < 9; ++i) {
        irradiance += shBuffer.shCoeffs[i] * shBasis[i];
    }
    return irradiance;
}


    vec3 irradiance = evalDiffuseIrradiance(fragNormal);

    vec3 ambientDiffuse = irradiance * fragAlbedo * (1.0 - metallic);
    float subsurfaceStrength = (1.0 - metallic) * roughness;
if (subsurfaceStrength > 0.3) { // Threshold for SSS materials
    float wrap = subsurfaceStrength * 1.0; // Wrap factor for ambient
    vec3 wrappedIrradiance = evalDiffuseIrradiance(normalize(fragNormal + view_dir * wrap)).rgb;
    ambientDiffuse = wrappedIrradiance * fragAlbedo * (1.0 - metallic);
}


Start of the SH Coefficient Compute Shader:

#version 450
#extension GL_EXT_scalar_block_layout : enable

#define PI 3.1415926535897932384626433832795
#define SH_ORDER 2  // For second-order SH (9 coefficients)
#define NUM_COEFFS ((SH_ORDER + 1) * (SH_ORDER + 1))
#define NUM_SAMPLES 64  // Number of samples per direction

layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

layout(scalar, set = 0, binding = 0) buffer SHBuffer {
    vec3 shCoeffs[];
} shBuffer;

layout(set = 0, binding = 1) uniform samplerCube irradianceMap;
layout(set = 0, binding = 2) uniform samplerCube depthMap;
layout(set = 0, binding = 3) uniform ProbePosition {
    vec3 probePos;
};