Skip to content

Commit a06b2e4

Browse files
Jasper-BekkersVZoutkhyperia
authored
Branchless sky shader (#71)
* Branchless sky shader * Fullscreen quad * Fixed naming of the fragment shader * Add very stripped down version of glam-rs math routines to spirv-std * Update example to use math routines in spirv-std * Also use lerp from spirv-std * Attribution, small cleanups * Use extension trait for math, much nicer \o/ * Switch to repr(simd) for the Vec types * Move clamp into MathExt, make MathExt take Self instead of f32 Co-authored-by: VZout <[email protected]> Co-authored-by: khyperia <[email protected]>
1 parent 3eea3df commit a06b2e4

File tree

10 files changed

+2821
-29
lines changed

10 files changed

+2821
-29
lines changed

examples/example-runner/src/main.rs

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,7 @@ fn main() {
728728
})
729729
.collect();
730730

731-
let index_buffer_data = [0u32, 1, 2];
731+
let index_buffer_data = [0u32, 1, 2, 1, 2, 3];
732732
let index_buffer_info = vk::BufferCreateInfo::builder()
733733
.size(std::mem::size_of_val(&index_buffer_data) as u64)
734734
.usage(vk::BufferUsageFlags::INDEX_BUFFER)
@@ -772,8 +772,27 @@ fn main() {
772772
.bind_buffer_memory(index_buffer, index_buffer_memory, 0)
773773
.unwrap();
774774

775+
let vertices = [
776+
Vertex {
777+
pos: [-1.0, 1.0, 0.0, 1.0],
778+
color: [0.0, 1.0, 0.0, 1.0],
779+
},
780+
Vertex {
781+
pos: [1.0, 1.0, 0.0, 1.0],
782+
color: [0.0, 0.0, 1.0, 1.0],
783+
},
784+
Vertex {
785+
pos: [-1.0, -1.0, 0.0, 1.0],
786+
color: [1.0, 0.0, 0.0, 1.0],
787+
},
788+
Vertex {
789+
pos: [1.0, -1.0, 0.0, 1.0],
790+
color: [1.0, 1.0, 1.0, 1.0],
791+
},
792+
];
793+
775794
let vertex_input_buffer_info = vk::BufferCreateInfo {
776-
size: 3 * std::mem::size_of::<Vertex>() as u64,
795+
size: std::mem::size_of_val(&vertices) as u64 as u64,
777796
usage: vk::BufferUsageFlags::VERTEX_BUFFER,
778797
sharing_mode: vk::SharingMode::EXCLUSIVE,
779798
..Default::default()
@@ -806,21 +825,6 @@ fn main() {
806825
.allocate_memory(&vertex_buffer_allocate_info, None)
807826
.unwrap();
808827

809-
let vertices = [
810-
Vertex {
811-
pos: [-1.0, 1.0, 0.0, 1.0],
812-
color: [0.0, 1.0, 0.0, 1.0],
813-
},
814-
Vertex {
815-
pos: [1.0, 1.0, 0.0, 1.0],
816-
color: [0.0, 0.0, 1.0, 1.0],
817-
},
818-
Vertex {
819-
pos: [0.0, -1.0, 0.0, 1.0],
820-
color: [1.0, 0.0, 0.0, 1.0],
821-
},
822-
];
823-
824828
let vert_ptr = base
825829
.device
826830
.map_memory(
@@ -1061,7 +1065,7 @@ fn main() {
10611065
1,
10621066
0,
10631067
0,
1064-
1,
1068+
0,
10651069
);
10661070
// Or draw without the index buffer
10671071
// device.cmd_draw(draw_command_buffer, 3, 1, 0, 0);

examples/example-shader/src/lib.rs

Lines changed: 167 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,183 @@
1+
//! Ported to Rust from https://github.com/Tw1ddle/Sky-Shader/blob/master/src/shaders/glsl/sky.fragment
2+
13
#![no_std]
24
#![feature(register_attr)]
35
#![register_attr(spirv)]
46

7+
use core::f32::consts::PI;
58
use core::panic::PanicInfo;
6-
use spirv_std::{f32x4, Input, Output};
9+
use spirv_std::{f32x4, Input, Mat4, MathExt, Output, Vec3, Vec4};
10+
11+
const DEPOLARIZATION_FACTOR: f32 = 0.035;
12+
const LUMINANCE: f32 = 1.0;
13+
const MIE_COEFFICIENT: f32 = 0.005;
14+
const MIE_DIRECTIONAL_G: f32 = 0.8;
15+
const MIE_K_COEFFICIENT: Vec3 = Vec3::new(0.686, 0.678, 0.666);
16+
const MIE_V: f32 = 4.0;
17+
const MIE_ZENITH_LENGTH: f32 = 1.25e3;
18+
const NUM_MOLECULES: f32 = 2.542e25f32;
19+
const PRIMARIES: Vec3 = Vec3::new(6.8e-7f32, 5.5e-7f32, 4.5e-7f32);
20+
const RAYLEIGH: f32 = 1.0;
21+
const RAYLEIGH_ZENITH_LENGTH: f32 = 8.4e3;
22+
const REFRACTIVE_INDEX: f32 = 1.0003;
23+
const SUN_ANGULAR_DIAMETER_DEGREES: f32 = 0.0093333;
24+
const SUN_INTENSITY_FACTOR: f32 = 1000.0;
25+
const SUN_INTENSITY_FALLOFF_STEEPNESS: f32 = 1.5;
26+
const TONEMAP_WEIGHTING: Vec3 = Vec3::splat(9.50);
27+
const TURBIDITY: f32 = 2.0;
28+
29+
/// Based on: https://seblagarde.wordpress.com/2014/12/01/inverse-trigonometric-functions-gpu-optimization-for-amd-gcn-architecture/
30+
fn acos_approx(v: f32) -> f32 {
31+
let x = v.abs();
32+
let mut res = -0.155972 * x + 1.56467; // p(x)
33+
res *= (1.0f32 - x).sqrt();
34+
35+
let mask = (v >= 0.0) as u32 as f32;
36+
37+
// can't use if-statement so do oldskool shader masking instead to avoid conditional
38+
(res * mask) + ((1.0f32 - mask) * (PI - res))
39+
}
40+
41+
fn smoothstep(edge0: f32, edge1: f32, x: f32) -> f32 {
42+
// Scale, bias and saturate x to 0..1 range
43+
let x = ((x - edge0) / (edge1 - edge0)).clamp(0.0, 1.0);
44+
// Evaluate polynomial
45+
return x * x * (3.0 - 2.0 * x);
46+
}
47+
48+
fn total_rayleigh(lambda: Vec3) -> Vec3 {
49+
(8.0 * PI.pow(3.0)
50+
* (REFRACTIVE_INDEX.pow(2.0) - 1.0).pow(2.0)
51+
* (6.0 + 3.0 * DEPOLARIZATION_FACTOR))
52+
/ (3.0 * NUM_MOLECULES * lambda.pow(4.0) * (6.0 - 7.0 * DEPOLARIZATION_FACTOR))
53+
}
54+
55+
fn total_mie(lambda: Vec3, k: Vec3, t: f32) -> Vec3 {
56+
let c = 0.2 * t * 10e-18;
57+
0.434 * c * PI * ((2.0 * PI) / lambda).pow(MIE_V - 2.0) * k
58+
}
59+
60+
fn rayleigh_phase(cos_theta: f32) -> f32 {
61+
(3.0 / (16.0 * PI)) * (1.0 + cos_theta.pow(2.0))
62+
}
63+
64+
fn henyey_greenstein_phase(cos_theta: f32, g: f32) -> f32 {
65+
(1.0 / (4.0 * PI)) * ((1.0 - g.pow(2.0)) / (1.0 - 2.0 * g * cos_theta + g.pow(2.0)).pow(1.5))
66+
}
67+
68+
fn sun_intensity(zenith_angle_cos: f32) -> f32 {
69+
let cutoff_angle = PI / 1.95; // Earth shadow hack
70+
SUN_INTENSITY_FACTOR
71+
* 0.0f32.max(
72+
1.0 - (-((cutoff_angle - acos_approx(zenith_angle_cos))
73+
/ SUN_INTENSITY_FALLOFF_STEEPNESS))
74+
.exp(),
75+
)
76+
}
77+
78+
fn uncharted2_tonemap(w: Vec3) -> Vec3 {
79+
let a = Vec3::splat(0.15); // Shoulder strength
80+
let b = Vec3::splat(0.50); // Linear strength
81+
let c = Vec3::splat(0.10); // Linear angle
82+
let d = Vec3::splat(0.20); // Toe strength
83+
let e = Vec3::splat(0.02); // Toe numerator
84+
let f = Vec3::splat(0.30); // Toe denominator
85+
86+
((w * (a * w + c * b) + d * e) / (w * (a * w + b) + d * f)) - e / f
87+
}
88+
89+
fn sky(dir: Vec3, sun_position: Vec3) -> Vec3 {
90+
let up = Vec3::new(0.0, 1.0, 0.0);
91+
let sunfade = 1.0 - (1.0 - (sun_position.1 / 450000.0).exp()).clamp(0.0, 1.0);
92+
let rayleigh_coefficient = RAYLEIGH - (1.0 * (1.0 - sunfade));
93+
let beta_r = total_rayleigh(PRIMARIES) * rayleigh_coefficient;
94+
95+
// Mie coefficient
96+
let beta_m = total_mie(PRIMARIES, MIE_K_COEFFICIENT, TURBIDITY) * MIE_COEFFICIENT;
97+
98+
// Optical length, cutoff angle at 90 to avoid singularity
99+
let zenith_angle = acos_approx(up.dot(dir).max(0.0));
100+
let denom = (zenith_angle).cos() + 0.15 * (93.885 - ((zenith_angle * 180.0) / PI)).pow(-1.253);
101+
102+
let s_r = RAYLEIGH_ZENITH_LENGTH / denom;
103+
let s_m = MIE_ZENITH_LENGTH / denom;
104+
105+
// Combined extinction factor
106+
let fex = (-(beta_r * s_r + beta_m * s_m)).exp();
107+
108+
// In-scattering
109+
let sun_direction = sun_position.normalize();
110+
let cos_theta = dir.dot(sun_direction);
111+
let beta_r_theta = beta_r * rayleigh_phase(cos_theta * 0.5 + 0.5);
112+
113+
let beta_m_theta = beta_m * henyey_greenstein_phase(cos_theta, MIE_DIRECTIONAL_G);
114+
let sun_e = sun_intensity(sun_direction.dot(up));
115+
let mut lin =
116+
(sun_e * ((beta_r_theta + beta_m_theta) / (beta_r + beta_m)) * (Vec3::splat(1.0) - fex))
117+
.pow(1.5);
118+
lin *= Vec3::splat(1.0).lerp(
119+
(sun_e * ((beta_r_theta + beta_m_theta) / (beta_r + beta_m)) * fex).pow(0.5),
120+
((1.0 - up.dot(sun_direction)).pow(5.0)).clamp(0.0, 1.0),
121+
);
122+
123+
// Composition + solar disc
124+
let sun_angular_diameter_cos = SUN_ANGULAR_DIAMETER_DEGREES.cos();
125+
let sundisk = smoothstep(
126+
sun_angular_diameter_cos,
127+
sun_angular_diameter_cos + 0.00002,
128+
cos_theta,
129+
);
130+
let mut l0 = 0.1 * fex;
131+
l0 += sun_e * 19000.0 * fex * sundisk;
132+
let mut tex_color = lin + l0;
133+
tex_color *= Vec3::splat(0.04);
134+
tex_color += Vec3::new(0.0, 0.001, 0.0025) * 0.3;
135+
136+
// Tonemapping
137+
let white_scale = 1.0 / uncharted2_tonemap(TONEMAP_WEIGHTING);
138+
let curr = uncharted2_tonemap(((2.0 / LUMINANCE.pow(4.0)).log2()) * tex_color);
139+
let color = curr * white_scale;
140+
141+
color.pow(1.0 / (1.2 + (1.2 * sunfade)))
142+
}
7143

8-
#[allow(unused_attributes)]
9144
#[spirv(entry = "fragment")]
10-
pub fn main_fs(input: Input<f32x4>, mut output: Output<f32x4>) {
11-
output.store(input.load());
145+
pub fn main_fs(input: Input<Vec4>, mut output: Output<Vec4>) {
146+
let color = input.load();
147+
let mut dir = Vec3::new(color.0, color.1, 0.0);
148+
149+
// hard-code information because we can't bind buffers at the moment
150+
let eye_pos = Vec3(0.0, 0.0997, 0.2);
151+
let sun_pos = Vec3::new(0.0, 75.0, -1000.0);
152+
let clip_to_world = Mat4 {
153+
x_axis: Vec4(-0.5522849, 0.0, 0.0, 0.0),
154+
y_axis: Vec4(0.0, 0.4096309, -0.061444636, 0.0),
155+
z_axis: Vec4(0.0, 99.99999, 199.99998, 999.99994),
156+
w_axis: Vec4(0.0, -0.14834046, -0.98893654, 0.0),
157+
};
158+
159+
let cs_pos = Vec4(dir.0, -dir.1, 1.0, 1.0);
160+
let mut ws_pos = clip_to_world.mul_vec4(cs_pos);
161+
let ws_pos = Vec3(
162+
ws_pos.0 / ws_pos.3,
163+
ws_pos.1 / ws_pos.3,
164+
ws_pos.2 / ws_pos.3,
165+
);
166+
let dir = (ws_pos - eye_pos).normalize();
167+
let k = sky(dir, sun_pos);
168+
169+
output.store(k.extend(0.0))
12170
}
13171

14-
#[allow(unused_attributes)]
15172
#[spirv(entry = "vertex")]
16173
pub fn main_vs(
17-
in_pos: Input<f32x4>,
18-
in_color: Input<f32x4>,
19-
#[spirv(builtin = "position")] mut out_pos: Output<f32x4>,
20-
mut out_color: Output<f32x4>,
174+
in_pos: Input<Vec4>,
175+
in_color: Input<Vec4>,
176+
#[spirv(builtin = "position")] mut out_pos: Output<Vec4>,
177+
mut out_color: Output<Vec4>,
21178
) {
22179
out_pos.store(in_pos.load());
23-
out_color.store(in_color.load());
180+
out_color.store(in_pos.load());
24181
}
25182

26183
#[panic_handler]

spirv-std/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
#![no_std]
2-
#![feature(register_attr, repr_simd)]
2+
#![feature(register_attr, repr_simd, core_intrinsics)]
33
#![register_attr(spirv)]
44

5+
pub mod math;
6+
pub use crate::math::MathExt;
7+
pub use crate::math::*;
8+
59
macro_rules! pointer_addrspace_write {
610
(false) => {};
711
(true) => {

0 commit comments

Comments
 (0)