|
| 1 | +//! Ported to Rust from https://github.com/Tw1ddle/Sky-Shader/blob/master/src/shaders/glsl/sky.fragment |
| 2 | +
|
1 | 3 | #![no_std]
|
2 | 4 | #![feature(register_attr)]
|
3 | 5 | #![register_attr(spirv)]
|
4 | 6 |
|
| 7 | +use core::f32::consts::PI; |
5 | 8 | 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 | +} |
7 | 143 |
|
8 |
| -#[allow(unused_attributes)] |
9 | 144 | #[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)) |
12 | 170 | }
|
13 | 171 |
|
14 |
| -#[allow(unused_attributes)] |
15 | 172 | #[spirv(entry = "vertex")]
|
16 | 173 | 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>, |
21 | 178 | ) {
|
22 | 179 | out_pos.store(in_pos.load());
|
23 |
| - out_color.store(in_color.load()); |
| 180 | + out_color.store(in_pos.load()); |
24 | 181 | }
|
25 | 182 |
|
26 | 183 | #[panic_handler]
|
|
0 commit comments