Constant PARTICLE_WGSL
Source pub const PARTICLE_WGSL: &str = r#"
struct Frame {
view_proj: mat4x4f, eye: vec4f, right: vec4f, up: vec4f, forward: vec4f,
lens: vec4f, sun: vec4f, sun_color: vec4f, extent: vec4f,
volume_lo: vec4f, volume_hi: vec4f, volume_color: vec4f, volume_control: vec4f,
}
struct Shadow {
splits: vec4f, filter_params: vec4f, sun: vec4f, texels: vec4f,
view_proj_0: mat4x4f, view_proj_1: mat4x4f, view_proj_2: mat4x4f,
}
struct Particle {
position_size: vec4f, velocity_alpha: vec4f, color_rotation: vec4f, detail: vec4f,
}
@group(0) @binding(0) var<uniform> frame: Frame;
@group(0) @binding(1) var scene_depth: texture_2d<f32>;
@group(0) @binding(2) var<storage, read> particles: array<Particle>;
@group(0) @binding(3) var shadow_atlas: texture_depth_2d_array;
@group(0) @binding(4) var<uniform> shadow: Shadow;
struct Out { @builtin(position) clip: vec4f, @location(0) uv: vec2f, @location(1) color: vec4f, @location(2) distance: f32, @location(3) kind: f32, @location(4) world: vec3f, @location(5) lit: f32, }
@vertex fn vs_main(@builtin(vertex_index) vertex: u32, @builtin(instance_index) index: u32) -> Out {
let p = particles[index];
let corners = array<vec2f, 6>(vec2f(-1.0,-1.0),vec2f(1.0,-1.0),vec2f(-1.0,1.0),vec2f(-1.0,1.0),vec2f(1.0,-1.0),vec2f(1.0,1.0));
let corner = corners[vertex];
let spin = p.color_rotation.w;
var axis_x = frame.right.xyz * cos(spin) + frame.up.xyz * sin(spin);
var axis_y = frame.up.xyz * cos(spin) - frame.right.xyz * sin(spin);
if (p.detail.x > 0.5 && p.detail.x < 1.5 && p.detail.y > 0.02) {
let v = normalize(p.velocity_alpha.xyz - frame.forward.xyz * dot(p.velocity_alpha.xyz, frame.forward.xyz));
axis_y = v;
axis_x = cross(axis_y, frame.forward.xyz);
}
let tumble = select(1.0, 0.45 + 0.55 * abs(sin(spin * 0.67)), p.detail.x < 0.5);
let world = p.position_size.xyz + (axis_x * corner.x * tumble + axis_y * corner.y) * p.position_size.w;
var out: Out;
out.clip = frame.view_proj * vec4f(world, 1.0);
out.uv = corner;
out.color = vec4f(p.color_rotation.xyz, p.velocity_alpha.w);
out.distance = length(world - frame.eye.xyz);
out.kind = p.detail.x;
out.world = world;
out.lit = p.detail.w;
return out;
}
fn sunlight(world: vec3f, distance: f32) -> f32 {
if (shadow.splits.w <= 0.0) { return 1.0; }
var layer = 0;
var matrix = shadow.view_proj_0;
if (distance > shadow.splits.y) { layer = 1; matrix = shadow.view_proj_1; }
if (distance > shadow.splits.z) { layer = 2; matrix = shadow.view_proj_2; }
let clip = matrix * vec4f(world, 1.0);
let point = clip.xyz / clip.w;
let uv = point.xy * vec2f(0.5, -0.5) + vec2f(0.5);
if (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0)) || point.z < 0.0 || point.z > 1.0) { return 1.0; }
let size = textureDimensions(shadow_atlas);
let at = clamp(vec2i(uv * vec2f(size)), vec2i(0), vec2i(size) - vec2i(1));
let depth = textureLoad(shadow_atlas, at, layer, 0);
return select(0.0, 1.0, point.z - shadow.sun.w <= depth);
}
@fragment fn fs_main(in: Out) -> @location(0) vec4f {
let pixel = vec2i(in.clip.xy);
let depth = textureLoad(scene_depth, pixel, 0).x;
let soft = clamp((depth - in.distance) * 4.0, 0.0, 1.0);
let r = length(in.uv);
let petal = smoothstep(1.0, 0.7, length(vec2f(in.uv.x, in.uv.y * 1.35)));
let dust = exp(-r * r * 5.0) * (0.7 + 0.3 * pow(max(0.0, 1.0 - r), 16.0));
let steam = exp(-r * r * 3.0);
let shape = select(select(petal, dust, in.kind > 0.5), steam, in.kind > 1.5);
if (in.lit > 0.5) {
let glint = max(1.0 - r, 0.0) * select(0.0, 1.0, depth > dot(in.world - frame.eye.xyz, frame.forward.xyz));
return vec4f(in.color.rgb * frame.sun_color.rgb * frame.sun_color.w * sunlight(in.world, in.distance) * glint, 0.0);
}
let alpha = in.color.a * shape * soft;
return vec4f(in.color.rgb * alpha, alpha);
}
"#;