const PI: f32 = 3.1415926535897932384626433832795; struct Face { forward: vec3, up: vec3, right: vec3, } @group(0) @binding(0) var src: texture_2d; @group(0) @binding(1) var dst: texture_storage_2d_array; @compute @workgroup_size(16, 16, 1) fn compute_equirect_to_cubemap( @builtin(global_invocation_id) gid: vec3, ) { // If texture size is not divisible by 32 we // need to make sure we don't try to write to // pixels that don't exist. if gid.x >= u32(textureDimensions(dst).x) { return; } var FACES: array = array( // FACES +X Face( vec3(1.0, 0.0, 0.0), // forward vec3(0.0, 1.0, 0.0), // up vec3(0.0, 0.0, -1.0), // right ), // FACES -X Face ( vec3(-1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0), ), // FACES +Y Face ( vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(1.0, 0.0, 0.0), ), // FACES -Y Face ( vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, -1.0), vec3(1.0, 0.0, 0.0), ), // FACES +Z Face ( vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 0.0), vec3(1.0, 0.0, 0.0), ), // FACES -Z Face ( vec3(0.0, 0.0, -1.0), vec3(0.0, 1.0, 0.0), vec3(-1.0, 0.0, 0.0), ), ); // Get texture coords relative to cubemap face let dst_dimensions = vec2(textureDimensions(dst)); let cube_uv = vec2(gid.xy) / dst_dimensions * 2.0 - 1.0; // Get spherical coordinate from cube_uv let face = FACES[gid.z]; let spherical = normalize(face.forward + face.right * cube_uv.x + face.up * cube_uv.y); // Get coordinate on the equirectangular texture let inv_atan = vec2(0.1591, 0.3183); let eq_uv = vec2(atan2(spherical.z, spherical.x), asin(spherical.y)) * inv_atan + 0.5; let eq_pixel = vec2(eq_uv * vec2(textureDimensions(src))); // We use textureLoad() as textureSample() is not allowed in compute shaders var sample = textureLoad(src, eq_pixel, 0); textureStore(dst, gid.xy, gid.z, sample); }