Skip to content

Commit 74e6a07

Browse files
authored
Merge pull request #8 from LykenSol/lsKcDD
Ported "Soft Shadow Variation" (https://www.shadertoy.com/view/lsKcDD).
2 parents 107d3b4 + afb1ca9 commit 74e6a07

File tree

2 files changed

+238
-0
lines changed

2 files changed

+238
-0
lines changed

shaders/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub mod phantom_star;
1919
pub mod playing_marble;
2020
pub mod protean_clouds;
2121
pub mod seascape;
22+
pub mod soft_shadow_variation;
2223
pub mod tileable_water_caustic;
2324
pub mod two_tweets;
2425

@@ -124,6 +125,7 @@ pub fn fs(constants: &ShaderConstants, mut frag_coord: Vec2) -> Vec4 {
124125
mouse,
125126
})
126127
.main_image(&mut color, frag_coord),
128+
14 => soft_shadow_variation::Inputs { resolution, time }.main_image(&mut color, frag_coord),
127129
_ => {}
128130
}
129131
pow(color.truncate(), 2.2).extend(color.w)

shaders/src/soft_shadow_variation.rs

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
//! Ported to Rust from <https://www.shadertoy.com/view/lsKcDD>
2+
//!
3+
//! Original comment:
4+
//! ```glsl
5+
//! //
6+
//! // Testing Sebastian Aaltonen's soft shadow improvement
7+
//! //
8+
//! // The technique is based on estimating a better closest point in ray
9+
//! // at each step by triangulating from the previous march step.
10+
//! //
11+
//! // More info about the technique at slide 39 of this presentation:
12+
//! // https://www.dropbox.com/s/s9tzmyj0wqkymmz/Claybook_Simulation_Raytracing_GDC18.pptx?dl=0
13+
//! //
14+
//! // Traditional technique: http://iquilezles.org/www/articles/rmshadows/rmshadows.htm
15+
//! //
16+
//! // Go to lines 54 to compare both.
17+
//! ```
18+
19+
use shared::*;
20+
use spirv_std::glam::{vec2, vec3, Mat3, Vec2, Vec2Swizzles, Vec3, Vec3Swizzles, Vec4};
21+
22+
// Note: This cfg is incorrect on its surface, it really should be "are we compiling with std", but
23+
// we tie #[no_std] above to the same condition, so it's fine.
24+
#[cfg(target_arch = "spirv")]
25+
use spirv_std::num_traits::Float;
26+
27+
pub struct Inputs {
28+
pub resolution: Vec3,
29+
pub time: f32,
30+
}
31+
32+
// make this 1 is your machine is too slow
33+
const AA: usize = 2;
34+
35+
//------------------------------------------------------------------
36+
37+
fn sd_plane(p: Vec3) -> f32 {
38+
p.y
39+
}
40+
41+
fn sd_box(p: Vec3, b: Vec3) -> f32 {
42+
let d: Vec3 = p.abs() - b;
43+
d.x.max(d.y.max(d.z)).min(0.0) + d.max(Vec3::zero()).length()
44+
}
45+
46+
//------------------------------------------------------------------
47+
48+
fn map(pos: Vec3) -> f32 {
49+
let qos: Vec3 = vec3((pos.x + 0.5).gl_fract() - 0.5, pos.y, pos.z);
50+
return sd_plane(pos - vec3(0.0, 0.00, 0.0))
51+
.min(sd_box(qos - vec3(0.0, 0.25, 0.0), vec3(0.2, 0.5, 0.2)));
52+
}
53+
54+
//------------------------------------------------------------------
55+
56+
fn calc_softshadow(ro: Vec3, rd: Vec3, mint: f32, tmax: f32, technique: i32) -> f32 {
57+
let mut res: f32 = 1.0;
58+
let mut t: f32 = mint;
59+
let mut ph: f32 = 1e10; // big, such that y = 0 on the first iteration
60+
let mut i = 0;
61+
while i < 32 {
62+
let h: f32 = map(ro + rd * t);
63+
64+
// traditional technique
65+
if technique == 0 {
66+
res = res.min(10.0 * h / t);
67+
}
68+
// improved technique
69+
else {
70+
// use this if you are getting artifact on the first iteration, or unroll the
71+
// first iteration out of the loop
72+
//float y = (i==0) ? 0.0 : h*h/(2.0*ph);
73+
74+
let y: f32 = h * h / (2.0 * ph);
75+
let d: f32 = (h * h - y * y).sqrt();
76+
res = res.min(10.0 * d / (t - y).max(0.0));
77+
ph = h;
78+
}
79+
t += h;
80+
81+
if res < 0.0001 || t > tmax {
82+
break;
83+
}
84+
i += 1;
85+
}
86+
res.clamp(0.0, 1.0)
87+
}
88+
89+
fn calc_normal(pos: Vec3) -> Vec3 {
90+
let e: Vec2 = vec2(1.0, -1.0) * 0.5773 * 0.0005;
91+
(e.xyy() * map(pos + e.xyy())
92+
+ e.yyx() * map(pos + e.yyx())
93+
+ e.yxy() * map(pos + e.yxy())
94+
+ e.xxx() * map(pos + e.xxx()))
95+
.normalize()
96+
}
97+
98+
fn cast_ray(ro: Vec3, rd: Vec3) -> f32 {
99+
let mut tmin: f32 = 1.0;
100+
let mut tmax: f32 = 20.0;
101+
102+
if true {
103+
// bounding volume
104+
let tp1: f32 = (0.0 - ro.y) / rd.y;
105+
if tp1 > 0.0 {
106+
tmax = tmax.min(tp1);
107+
}
108+
let tp2: f32 = (1.0 - ro.y) / rd.y;
109+
if tp2 > 0.0 {
110+
if ro.y > 1.0 {
111+
tmin = tmin.max(tp2);
112+
} else {
113+
tmax = tmax.min(tp2);
114+
}
115+
}
116+
}
117+
let mut t: f32 = tmin;
118+
let mut i = 0;
119+
while i < 64 {
120+
let precis: f32 = 0.0005 * t;
121+
let res: f32 = map(ro + rd * t);
122+
if res < precis || t > tmax {
123+
break;
124+
}
125+
t += res;
126+
i += 1;
127+
}
128+
129+
if t > tmax {
130+
t = -1.0;
131+
}
132+
t
133+
}
134+
135+
fn calc_ao(pos: Vec3, nor: Vec3) -> f32 {
136+
let mut occ: f32 = 0.0;
137+
let mut sca: f32 = 1.0;
138+
let mut i = 0;
139+
while i < 5 {
140+
let h: f32 = 0.001 + 0.15 * i as f32 / 4.0;
141+
let d: f32 = map(pos + h * nor);
142+
occ += (h - d) * sca;
143+
sca *= 0.95;
144+
i += 1;
145+
}
146+
(1.0 - 1.5 * occ).clamp(0.0, 1.0)
147+
}
148+
149+
fn render(ro: Vec3, rd: Vec3, technique: i32) -> Vec3 {
150+
let mut col: Vec3 = Vec3::zero();
151+
let t: f32 = cast_ray(ro, rd);
152+
153+
if t > -0.5 {
154+
let pos: Vec3 = ro + t * rd;
155+
let nor: Vec3 = calc_normal(pos);
156+
157+
// material
158+
let mate: Vec3 = Vec3::splat(0.3);
159+
// key light
160+
let lig: Vec3 = vec3(-0.1, 0.3, 0.6).normalize();
161+
let hal: Vec3 = (lig - rd).normalize();
162+
let dif: f32 =
163+
nor.dot(lig).clamp(0.0, 1.0) * calc_softshadow(pos, lig, 0.01, 3.0, technique);
164+
165+
let spe: f32 = nor.dot(hal).clamp(0.0, 1.0).powf(16.0)
166+
* dif
167+
* (0.04 + 0.96 * (1.0 + hal.dot(rd)).clamp(0.0, 1.0).powf(5.0));
168+
169+
col = mate * 4.0 * dif * vec3(1.00, 0.70, 0.5);
170+
col += 12.0 * spe * vec3(1.00, 0.70, 0.5);
171+
172+
// ambient light
173+
let occ: f32 = calc_ao(pos, nor);
174+
let amb: f32 = (0.5 + 0.5 * nor.y).clamp(0.0, 1.0);
175+
col += mate * amb * occ * vec3(0.0, 0.08, 0.1);
176+
177+
// fog
178+
col *= (-0.0005 * t * t * t).exp();
179+
}
180+
181+
col
182+
}
183+
184+
fn set_camera(ro: Vec3, ta: Vec3, cr: f32) -> Mat3 {
185+
let cw: Vec3 = (ta - ro).normalize();
186+
let cp: Vec3 = vec3(cr.sin(), cr.cos(), 0.0);
187+
let cu: Vec3 = cw.cross(cp).normalize();
188+
let cv: Vec3 = cu.cross(cw).normalize();
189+
Mat3::from_cols(cu, cv, cw)
190+
}
191+
192+
impl Inputs {
193+
pub fn main_image(&mut self, frag_color: &mut Vec4, frag_coord: Vec2) {
194+
// camera
195+
let an: f32 = 12.0 - (0.1 * self.time).sin();
196+
let ro: Vec3 = vec3(3.0 * (0.1 * an).cos(), 1.0, -3.0 * (0.1 * an).sin());
197+
let ta: Vec3 = vec3(0.0, -0.4, 0.0);
198+
// camera-to-world transformation
199+
let ca: Mat3 = set_camera(ro, ta, 0.0);
200+
201+
let technique: i32 = if (self.time / 2.0).gl_fract() > 0.5 {
202+
1
203+
} else {
204+
0
205+
};
206+
207+
let mut tot: Vec3 = Vec3::zero();
208+
209+
let mut m = 0;
210+
while m < AA {
211+
let mut n = 0;
212+
while n < AA {
213+
// pixel coordinates
214+
let o: Vec2 = vec2(m as f32, n as f32) / AA as f32 - Vec2::splat(0.5);
215+
let p: Vec2 = (-self.resolution.xy() + 2.0 * (frag_coord + o)) / self.resolution.y;
216+
217+
// ray direction
218+
let rd: Vec3 = ca * p.extend(2.0).normalize();
219+
220+
// render
221+
let mut col: Vec3 = render(ro, rd, technique);
222+
223+
// gamma
224+
col = col.powf(0.4545);
225+
226+
tot += col;
227+
228+
n += 1;
229+
}
230+
m += 1;
231+
}
232+
tot /= (AA * AA) as f32;
233+
234+
*frag_color = tot.extend(1.0);
235+
}
236+
}

0 commit comments

Comments
 (0)