renderling/skybox/
shader.rs

1//! Skybox shaders.
2
3use crabslab::{Id, Slab};
4use glam::{Mat3, Mat4, Vec2, Vec3, Vec4, Vec4Swizzles};
5use spirv_std::{
6    image::{Cubemap, Image2d},
7    spirv, Sampler,
8};
9
10#[allow(unused_imports)]
11use spirv_std::num_traits::Float;
12
13use crate::{
14    camera::shader::CameraDescriptor,
15    math::{self, IsVector},
16};
17
18const INV_ATAN: Vec2 = Vec2::new(0.1591, core::f32::consts::FRAC_1_PI);
19
20/// Takes a unit direction and converts it to a uv lookup in an equirectangular
21/// map.
22pub fn direction_to_equirectangular_uv(dir: Vec3) -> Vec2 {
23    let mut uv = Vec2::new(f32::atan2(dir.z, dir.x), f32::asin(dir.y));
24    uv *= INV_ATAN;
25    uv += 0.5;
26    uv
27}
28
29/// Vertex shader for a skybox.
30#[spirv(vertex)]
31pub fn skybox_vertex(
32    #[spirv(instance_index)] camera_index: u32,
33    #[spirv(vertex_index)] vertex_index: u32,
34    #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] slab: &[u32],
35    local_pos: &mut Vec3,
36    #[spirv(position)] clip_pos: &mut Vec4,
37) {
38    let camera_id = Id::<CameraDescriptor>::from(camera_index);
39    let camera = slab.read(camera_id);
40    let point = math::CUBE[vertex_index as usize];
41    *local_pos = point;
42    let camera_view_without_translation = Mat3::from_mat4(camera.view());
43    let rot_view = Mat4::from_mat3(camera_view_without_translation);
44    let position = camera.projection() * rot_view * point.extend(1.0);
45    *clip_pos = position.xyww();
46}
47
48/// Colors a skybox using a cubemap texture.
49#[spirv(fragment)]
50pub fn skybox_cubemap_fragment(
51    #[spirv(descriptor_set = 0, binding = 1)] texture: &Cubemap,
52    #[spirv(descriptor_set = 0, binding = 2)] sampler: &Sampler,
53    local_pos: Vec3,
54    out_color: &mut Vec4,
55) {
56    let env_color: Vec3 = texture.sample(*sampler, local_pos.alt_norm_or_zero()).xyz();
57    *out_color = env_color.extend(1.0);
58}
59
60/// Vertex shader that draws a cubemap.
61///
62/// Uses the `instance_index` as the [`Id`] for a [`CameraDescriptor`].
63///
64/// Used to create a cubemap from an equirectangular image as well as cubemap
65/// convolutions.
66#[spirv(vertex)]
67pub fn skybox_cubemap_vertex(
68    #[spirv(instance_index)] camera_id: Id<CameraDescriptor>,
69    #[spirv(vertex_index)] vertex_index: u32,
70    #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] slab: &[u32],
71    local_pos: &mut Vec3,
72    #[spirv(position)] gl_pos: &mut Vec4,
73) {
74    let camera = slab.read(camera_id);
75    let pos = crate::math::CUBE[vertex_index as usize];
76    *local_pos = pos;
77    *gl_pos = camera.view_projection() * pos.extend(1.0);
78}
79
80/// Fragment shader that colors a skybox using an equirectangular texture.
81#[spirv(fragment)]
82pub fn skybox_equirectangular_fragment(
83    #[spirv(descriptor_set = 0, binding = 1)] texture: &Image2d,
84    #[spirv(descriptor_set = 0, binding = 2)] sampler: &Sampler,
85    local_pos: Vec3,
86    out_color: &mut Vec4,
87) {
88    let uv = direction_to_equirectangular_uv(local_pos.alt_norm_or_zero());
89    let env_color: Vec3 = texture.sample(*sampler, uv).xyz();
90    *out_color = env_color.extend(1.0);
91}