Skip to content
Closed
13 changes: 6 additions & 7 deletions crates/renderide/shaders/materials/proceduralskybox.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ struct VertexOutput {
@location(0) ground_color: vec3<f32>,
@location(1) sky_color: vec3<f32>,
@location(2) sun_color: vec3<f32>,
@location(3) fragment_ray: vec3<f32>,
@location(4) sky_ground_factor: f32,
@location(3) ray: vec3<f32>,
}

@vertex
Expand All @@ -51,12 +50,13 @@ fn vs_main(

var out: VertexOutput;
out.clip_pos = vp * world_p;
let terms = ps::visible_vertex_terms(psmat::params(), mv::model_vector(d, pos.xyz));
let ps_params = psmat::params();
let scattering_params = ps::scattering_parameters(ps_params);
let terms = ps::visible_vertex_terms(ps_params, scattering_params, mv::model_vector(d, pos.xyz));
out.ground_color = terms.ground_color;
out.sky_color = terms.sky_color;
out.sun_color = terms.sun_color;
out.fragment_ray = terms.fragment_ray;
out.sky_ground_factor = terms.sky_ground_factor;
out.ray = terms.ray;
return out;
}

Expand All @@ -67,8 +67,7 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
in.ground_color,
in.sky_color,
in.sun_color,
in.fragment_ray,
in.sky_ground_factor,
in.ray,
);
return rg::retain_globals_additive(vec4<f32>(
ps::visible_fragment_color(psmat::params(), terms),
Expand Down
2 changes: 2 additions & 0 deletions crates/renderide/shaders/modules/core/math.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

const EPSILON: f32 = 1e-6;

const MIN_FLOAT: f32 = 0x1p-126f;

fn saturate(v: f32) -> f32 {
return clamp(v, 0.0, 1.0);
}
Expand Down
60 changes: 17 additions & 43 deletions crates/renderide/shaders/modules/skybox/common.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@

#define_import_path renderide::skybox::common

#import renderide::frame::globals as rg

struct SkyboxView {
view_x_left: vec4<f32>,
view_y_left: vec4<f32>,
view_z_left: vec4<f32>,
view_x_right: vec4<f32>,
view_y_right: vec4<f32>,
view_z_right: vec4<f32>,
view_left: mat4x4<f32>,
view_right: mat4x4<f32>,
clear_color: vec4<f32>,
/// `.x`: ndc Y sign passed to the fragment shader (1.0 normal, -1.0 for offscreen-RT views).
/// Offscreen-RT views pre-multiply a clip-space Y flip into the world view-projection so the
Expand All @@ -20,30 +18,18 @@ struct SkyboxView {
ndc_y_sign_pad: vec4<f32>,
}

fn fullscreen_clip_pos(vertex_index: u32) -> vec4<f32> {
let x = f32((vertex_index << 1u) & 2u);
let y = f32(vertex_index & 2u);
return vec4<f32>(x * 2.0 - 1.0, 1.0 - y * 2.0, 0.0, 1.0);
fn select_view_proj(view: SkyboxView, view_idx: u32) -> mat4x4<f32> {
if (view_idx == 0u) {
return view.view_left;
}
return view.view_right;
}

fn fullscreen_quad_clip_pos(vertex_index: u32) -> vec4<f32> {
let i = vertex_index % 6u;
if (i == 0u) {
return vec4<f32>(-1.0, 1.0, 0.0, 1.0);
}
if (i == 1u) {
return vec4<f32>(1.0, 1.0, 0.0, 1.0);
}
if (i == 2u) {
return vec4<f32>(-1.0, -1.0, 0.0, 1.0);
}
if (i == 3u) {
return vec4<f32>(-1.0, -1.0, 0.0, 1.0);
}
if (i == 4u) {
return vec4<f32>(1.0, 1.0, 0.0, 1.0);
}
return vec4<f32>(1.0, -1.0, 0.0, 1.0);
fn ndc_from_clip(clip_pos: vec4<f32>) -> vec2<f32> {
return vec2<f32>(
clip_pos.x / f32(rg::frame.viewport_width),
1 - clip_pos.y / f32(rg::frame.viewport_height),
) * 2.0 - 1.0;
}

fn view_is_orthographic(sky: SkyboxView, view_layer: u32) -> bool {
Expand All @@ -60,22 +46,10 @@ fn view_ray_from_ndc(ndc: vec2<f32>, proj_params: vec4<f32>, orthographic: bool)
}
// Asymmetric OpenXR projections store skew on the Z column. With sky rays fixed at z = -1
// and clip_w = -view_z, inverse projection uses ndc + skew.
let view_x = (ndc.x + proj_params.z) / max(abs(proj_params.x), 0.000001);
let view_y = (ndc.y + proj_params.w) / max(abs(proj_params.y), 0.000001);
return normalize(vec3<f32>(view_x, view_y, -1.0));
return vec3<f32>((ndc.xy + proj_params.zw) / max(abs(proj_params.xy), vec2<f32>(0.000001)), -1.0);
}

fn world_ray_from_view_ray(view_ray: vec3<f32>, sky: SkyboxView, view_layer: u32) -> vec3<f32> {
if (view_layer == 0u) {
return normalize(
view_ray.x * sky.view_x_left.xyz +
view_ray.y * sky.view_y_left.xyz +
view_ray.z * sky.view_z_left.xyz
);
}
return normalize(
view_ray.x * sky.view_x_right.xyz +
view_ray.y * sky.view_y_right.xyz +
view_ray.z * sky.view_z_right.xyz
);
let view_matrix = select_view_proj(sky, view_layer);
return normalize(view_matrix * vec4<f32>(view_ray, 0.0)).xyz;
}
100 changes: 55 additions & 45 deletions crates/renderide/shaders/modules/skybox/procedural.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const INNER_RADIUS: f32 = 1.0;
const OUTER_RADIUS_SQ: f32 = OUTER_RADIUS * OUTER_RADIUS;
const INNER_RADIUS_SQ: f32 = INNER_RADIUS * INNER_RADIUS;
const CAMERA_HEIGHT: f32 = 0.0001;
const CAMERA_POS_HEIGHT: f32 = INNER_RADIUS +CAMERA_HEIGHT;
const KMIE: f32 = 0.0010;
const KSUN_BRIGHTNESS: f32 = 20.0;
const KMAX_SCATTER: f32 = 50.0;
Expand All @@ -17,7 +18,7 @@ const KKM_4PI: f32 = KMIE * 4.0 * PI;
const KSCALE: f32 = 1.0 / (OUTER_RADIUS - 1.0);
const KSCALE_DEPTH: f32 = 0.25;
const KSCALE_OVER_SCALE_DEPTH: f32 = (1.0 / (OUTER_RADIUS - 1.0)) / 0.25;
const KSAMPLES: f32 = 2.0;
const KSAMPLES: u32 = 2u;
const MIE_G: f32 = -0.990;
const MIE_G2: f32 = 0.9801;
const SKY_GROUND_THRESHOLD: f32 = 0.02;
Expand All @@ -41,8 +42,12 @@ struct ProceduralSkyVisibleTerms {
ground_color: vec3<f32>,
sky_color: vec3<f32>,
sun_color: vec3<f32>,
fragment_ray: vec3<f32>,
sky_ground_factor: f32,
ray: vec3<f32>,
}

struct ScatteringParameters {
kkr_in: vec3<f32>,
kkr_scatter: vec3<f32>,
}

struct ScatteringStep {
Expand Down Expand Up @@ -95,8 +100,7 @@ fn scattering_inscatter_step(
sample_point: vec3<f32>,
eye_ray: vec3<f32>,
sun_dir: vec3<f32>,
inv_wavelength: vec3<f32>,
kkr_4pi: f32,
kkr_scatter: vec3<f32>,
start_offset: f32,
scaled_length: f32,
) -> ScatteringStep {
Expand All @@ -105,64 +109,58 @@ fn scattering_inscatter_step(
let light_angle = dot(sun_dir, sample_point) / h;
let camera_angle = dot(eye_ray, sample_point) / h;
let scatter = start_offset + depth * (scale_factor(light_angle) - scale_factor(camera_angle));
let attenuate = exp(-clamp(scatter, 0.0, KMAX_SCATTER) * (inv_wavelength * kkr_4pi + KKM_4PI));
let attenuate = exp(-clamp(scatter, 0.0, KMAX_SCATTER) * kkr_scatter);
return ScatteringStep(attenuate * (depth * scaled_length), attenuate);
}

fn ground_inscatter_step(
sample_point: vec3<f32>,
inv_wavelength: vec3<f32>,
kkr_4pi: f32,
kkr_scatter: vec3<f32>,
temp: f32,
camera_offset: f32,
scaled_length: f32,
) -> ScatteringStep {
let h = length(sample_point);
let depth = exp(KSCALE_OVER_SCALE_DEPTH * (INNER_RADIUS - h));
let scatter = depth * temp - camera_offset;
let attenuate = exp(-clamp(scatter, 0.0, KMAX_SCATTER) * (inv_wavelength * kkr_4pi + KKM_4PI));
let attenuate = exp(-clamp(scatter, 0.0, KMAX_SCATTER) * kkr_scatter);
return ScatteringStep(attenuate * (depth * scaled_length), attenuate);
}

fn evaluate_scattering(
eye_ray: vec3<f32>,
sun_dir: vec3<f32>,
inv_wavelength: vec3<f32>,
krayleigh: f32,
params: ProceduralSkyParams,
scattering_params: ScatteringParameters,
) -> ScatteringOutput {
let kkr_esun = krayleigh * KSUN_BRIGHTNESS;
let kkr_4pi = krayleigh * 4.0 * PI;
let camera_pos = vec3<f32>(0.0, INNER_RADIUS + CAMERA_HEIGHT, 0.0);
let camera_pos = vec3<f32>(0.0, CAMERA_POS_HEIGHT, 0.0);

var c_in: vec3<f32>;
var c_out: vec3<f32>;

if (eye_ray.y >= 0.0) {
let far = sqrt(OUTER_RADIUS_SQ + INNER_RADIUS_SQ * eye_ray.y * eye_ray.y - INNER_RADIUS_SQ)
- INNER_RADIUS * eye_ray.y;
let height = INNER_RADIUS + CAMERA_HEIGHT;
let height = CAMERA_POS_HEIGHT;
let depth_init = exp(KSCALE_OVER_SCALE_DEPTH * (-CAMERA_HEIGHT));
let start_angle = dot(eye_ray, camera_pos) / height;
let start_offset = depth_init * scale_factor(start_angle);

let sample_length = far / KSAMPLES;
let sample_length = far / f32(KSAMPLES);
let scaled_length = sample_length * KSCALE;
let sample_ray = eye_ray * sample_length;
var sample_point = camera_pos + sample_ray * 0.5;

var front_color = vec3<f32>(0.0);
let s0 = scattering_inscatter_step(
sample_point, eye_ray, sun_dir, inv_wavelength, kkr_4pi, start_offset, scaled_length,
);
front_color = front_color + s0.contribution;
sample_point = sample_point + sample_ray;

let s1 = scattering_inscatter_step(
sample_point, eye_ray, sun_dir, inv_wavelength, kkr_4pi, start_offset, scaled_length,
);
front_color = front_color + s1.contribution;
for (var i = 0u; i < KSAMPLES; i++) {
let step = scattering_inscatter_step(
sample_point, eye_ray, sun_dir, scattering_params.kkr_scatter, start_offset, scaled_length,
);
front_color = front_color + step.contribution;
sample_point = sample_point + sample_ray;
}

c_in = front_color * (inv_wavelength * kkr_esun);
c_in = front_color * (scattering_params.kkr_in);
c_out = front_color * KKM_ESUN;
} else {
let far = (-CAMERA_HEIGHT) / min(-0.001, eye_ray.y);
Expand All @@ -175,17 +173,18 @@ fn evaluate_scattering(
let camera_offset = depth * camera_scale;
let temp = light_scale + camera_scale;

let sample_length = far / KSAMPLES;
let sample_length = far / f32(KSAMPLES);
let scaled_length = sample_length * KSCALE;
let sample_ray = eye_ray * sample_length;
let sample_point = camera_pos + sample_ray * 0.5;

let g = ground_inscatter_step(
sample_point, inv_wavelength, kkr_4pi, temp, camera_offset, scaled_length,
sample_point, scattering_params.kkr_scatter, temp, camera_offset, scaled_length,
);
let front_color = g.contribution;

c_in = front_color * (inv_wavelength * kkr_esun + KKM_ESUN);
let sun_contrib = select(KKM_ESUN, 0, sun_disk_mode_none(params.sun_disk_mode));
c_in = front_color * (scattering_params.kkr_in + sun_contrib);
c_out = clamp(g.attenuate, vec3<f32>(0.0), vec3<f32>(1.0));
}

Expand All @@ -209,45 +208,54 @@ fn sun_disk_mode_high_quality(mode: f32) -> bool {
return mode > 1.5;
}

fn visible_vertex_terms(params: ProceduralSkyParams, input_eye_ray: vec3<f32>) -> ProceduralSkyVisibleTerms {
let eye_ray = safe_normalize(input_eye_ray, vec3<f32>(0.0, 1.0, 0.0));
let sun_dir = safe_normalize(params.sun_direction, vec3<f32>(0.0, 1.0, 0.0));
fn scattering_parameters(params: ProceduralSkyParams) -> ScatteringParameters {
let scattering_wavelength = scattering_wavelength_from_tint(params.sky_tint);
let inv_wavelength = 1.0 / pow(scattering_wavelength, vec3<f32>(4.0));
let krayleigh = mix(0.0, 0.0025, pow(max(params.atmosphere_thickness, 0.0), 2.5));
let scattering = evaluate_scattering(eye_ray, sun_dir, inv_wavelength, krayleigh);
let kkr_esun = krayleigh * KSUN_BRIGHTNESS;
let kkr_4pi = krayleigh * 4.0 * PI;
let kkr_in = inv_wavelength * kkr_esun;
let kkr_scatter = inv_wavelength * kkr_4pi + KKM_4PI;
return ScatteringParameters(kkr_in, kkr_scatter);
}

fn visible_vertex_terms(
params: ProceduralSkyParams,
scattering_params: ScatteringParameters,
input_eye_ray: vec3<f32>
) -> ProceduralSkyVisibleTerms {
let eye_ray = safe_normalize(input_eye_ray, vec3<f32>(0.0, 1.0, 0.0));
let sun_dir = safe_normalize(params.sun_direction, vec3<f32>(0.0, 1.0, 0.0));
let scattering = evaluate_scattering(eye_ray, sun_dir, params, scattering_params);

let ground_color = params.exposure * (scattering.c_in + params.ground_color * scattering.c_out);
let sky_color = params.exposure * (scattering.c_in * rayleigh_phase(sun_dir, -eye_ray));
let sun_color = params.exposure * (scattering.c_out * params.sun_color);
let fragment_ray = -eye_ray;
let sky_ground_factor = fragment_ray.y / SKY_GROUND_THRESHOLD;
return ProceduralSkyVisibleTerms(
ground_color,
sky_color,
sun_color,
fragment_ray,
sky_ground_factor,
-eye_ray,
);
}

fn visible_fragment_color(params: ProceduralSkyParams, terms: ProceduralSkyVisibleTerms) -> vec3<f32> {
let sun_dir = safe_normalize(params.sun_direction, vec3<f32>(0.0, 1.0, 0.0));
let fragment_ray = safe_normalize(terms.fragment_ray, vec3<f32>(0.0, -1.0, 0.0));
let sky_ground_factor = terms.ray.y / SKY_GROUND_THRESHOLD;
var color = mix(
terms.sky_color,
terms.ground_color,
clamp(terms.sky_ground_factor, 0.0, 1.0),
clamp(sky_ground_factor, 0.0, 1.0),
);

if (!sun_disk_mode_none(params.sun_disk_mode) && terms.sky_ground_factor < 0.0) {
if (!sun_disk_mode_none(params.sun_disk_mode) && sky_ground_factor < 0.0) {
let sun_dir = safe_normalize(params.sun_direction, vec3<f32>(0.0, 1.0, 0.0));
let sun_size = clamp(params.sun_size, 1.0e-4, 1.0);
var mie: f32;
if (sun_disk_mode_high_quality(params.sun_disk_mode)) {
let eye_cos = dot(sun_dir, fragment_ray);
let eye_cos = dot(sun_dir, terms.ray);
mie = mie_phase(eye_cos, eye_cos * eye_cos, sun_size);
} else {
mie = calc_sun_spot(sun_dir, -fragment_ray, sun_size);
mie = calc_sun_spot(sun_dir, -terms.ray, sun_size);
}
color = color + mie * terms.sun_color;
}
Expand All @@ -256,5 +264,7 @@ fn visible_fragment_color(params: ProceduralSkyParams, terms: ProceduralSkyVisib
}

fn sample(params: ProceduralSkyParams, input_eye_ray: vec3<f32>) -> vec3<f32> {
return visible_fragment_color(params, visible_vertex_terms(params, input_eye_ray));
let scattering_params = scattering_parameters(params);
let vertex_terms = visible_vertex_terms(params, scattering_params, input_eye_ray);
return visible_fragment_color(params, vertex_terms);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Fullscreen GradientSkybox sky draw.

#import renderide::frame::globals as rg
#import renderide::core::fullscreen as fs
#import renderide::skybox::common as skybox
#import renderide::skybox::gradient as skygrad

Expand All @@ -18,8 +19,7 @@ struct GradientSkyboxMaterial {

struct VertexOutput {
@builtin(position) clip_pos: vec4<f32>,
@location(0) ndc: vec2<f32>,
@location(1) @interpolate(flat) view_layer: u32,
@location(0) @interpolate(flat) view_layer: u32,
}

@vertex
Expand All @@ -29,10 +29,9 @@ fn vs_main(
@builtin(view_index) view_idx: u32,
#endif
) -> VertexOutput {
let clip = skybox::fullscreen_clip_pos(vertex_index);
let clip = fs::fullscreen_clip_pos(vertex_index);
var out: VertexOutput;
out.clip_pos = clip;
out.ndc = vec2<f32>(clip.x, clip.y * view.ndc_y_sign_pad.x);
#ifdef MULTIVIEW
out.view_layer = view_idx;
#else
Expand All @@ -41,11 +40,13 @@ fn vs_main(
return out;
}

//#pass type=forward blend=off zwrite=off ztest=main
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
let ndc = skybox::ndc_from_clip(in.clip_pos);
let proj_params = select(rg::frame.proj_params_left, rg::frame.proj_params_right, in.view_layer != 0u);
let view_ray = skybox::view_ray_from_ndc(
in.ndc,
ndc,
proj_params,
skybox::view_is_orthographic(view, in.view_layer),
);
Expand Down
Loading
Loading