minitracing/src/trace.rs
numzero c93ab42458 Don’t load envmap
It starts much faster now!
2025-03-29 00:04:13 +03:00

275 lines
6.7 KiB
Rust

use std::mem;
use bytemuck::{bytes_of, cast_slice, Pod, Zeroable};
use glam::{vec2, Mat3, Vec2, Vec3};
use wgpu::util::DeviceExt;
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct Params {
pub max_reflections: u32,
pub min_strength: f32,
pub sphere_count: u32,
pub seed: u32,
}
#[derive(Debug, Clone, Copy)]
pub struct Viewport {
pub corner: Vec3,
}
#[derive(Debug, Clone, Copy)]
pub struct Aperture {
pub radius: f32,
pub focal_distance: f32, // from 0 (exclusive) to +∞ (inclusive)
pub glare_strength: f32,
pub glare_radius: f32, // at distance 1
}
#[derive(Debug, Clone, Copy)]
pub struct CameraLocation {
pub eye: Vec3,
pub view: Mat3,
}
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
#[repr(C)]
struct ParamsData {
params: Params,
camera: CameraData,
}
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
#[repr(C)]
struct CameraData {
u: Vec3,
width: f32,
v: Vec3,
height: f32,
w: Vec3,
aperture: f32,
eye: Vec3,
antifocal: f32,
glare_strength: f32,
glare_radius: f32,
}
impl From<(Viewport, CameraLocation, Aperture)> for CameraData {
fn from(value: (Viewport, CameraLocation, Aperture)) -> Self {
CameraData {
u: value.1.view.x_axis,
v: value.1.view.y_axis,
w: value.1.view.z_axis,
eye: value.1.eye,
width: value.0.corner.x / value.0.corner.z,
height: value.0.corner.y / value.0.corner.z,
aperture: value.2.radius,
antifocal: 1. / value.2.focal_distance,
glare_strength: value.2.glare_strength,
glare_radius: value.2.glare_radius,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Sphere {
pub center: Vec3,
pub radius: f32,
pub emit_color: Vec3,
pub reflect_color: Vec3,
pub glossiness: f32,
}
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
#[repr(C)]
struct Vertex {
pub screen: Vec2,
}
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
#[repr(C)]
struct SphereData {
center: Vec3,
radius: f32,
emit_color: Vec3,
pad1: f32,
reflect_color: Vec3,
glossiness: f32,
}
impl From<&Sphere> for SphereData {
fn from(s: &Sphere) -> Self {
SphereData {
center: s.center,
radius: s.radius,
emit_color: s.emit_color,
pad1: 0.0,
reflect_color: s.reflect_color,
glossiness: s.glossiness,
}
}
}
pub struct Tracer {
view_buf: wgpu::Buffer,
pipeline: wgpu::RenderPipeline,
}
pub struct TracerData {
bindings: wgpu::BindGroup,
}
static SHADER: &str = include_str!("trace.wgsl");
impl Tracer {
pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self {
let screen_coord = [vec2(-1., -1.), vec2(1., -1.), vec2(-1., 1.), vec2(1., 1.)];
let view_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None,
contents: cast_slice(&screen_coord),
usage: wgpu::BufferUsages::VERTEX,
});
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Wgsl(SHADER.into()),
});
let spheres_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}],
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[&spheres_bgl],
push_constant_ranges: &[wgpu::PushConstantRange {
stages: wgpu::ShaderStages::FRAGMENT,
range: 0..mem::size_of::<ParamsData>() as u32,
}],
});
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: None,
compilation_options: wgpu::PipelineCompilationOptions::default(),
buffers: &[wgpu::VertexBufferLayout {
array_stride: size_of::<Vertex>() as u64,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[wgpu::VertexAttribute {
shader_location: 0,
offset: 0,
format: wgpu::VertexFormat::Float32x2,
}],
}],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleStrip,
..Default::default()
},
depth_stencil: None,
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: None,
compilation_options: wgpu::PipelineCompilationOptions::default(),
targets: &[Some(wgpu::ColorTargetState {
format,
blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::One,
dst_factor: wgpu::BlendFactor::One,
operation: wgpu::BlendOperation::Add,
},
alpha: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::One,
dst_factor: wgpu::BlendFactor::One,
operation: wgpu::BlendOperation::Add,
},
}),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
cache: None,
});
Self { view_buf, pipeline }
}
pub fn prepare<'encoder>(
&self,
encoder: &'encoder mut wgpu::CommandEncoder,
target: &wgpu::TextureView,
) -> wgpu::RenderPass<'encoder> {
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &target,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
timestamp_writes: None,
})
}
pub fn render(
&self,
pass: &mut wgpu::RenderPass,
data: &TracerData,
params: Params,
viewport: Viewport,
aperture: Aperture,
camera: CameraLocation,
) {
pass.set_pipeline(&self.pipeline);
pass.set_push_constants(
wgpu::ShaderStages::FRAGMENT,
0,
bytes_of(&ParamsData {
params,
camera: (viewport, camera, aperture).into(),
}),
);
pass.set_vertex_buffer(0, self.view_buf.slice(..));
pass.set_bind_group(0, &data.bindings, &[]);
pass.draw(0..4, 0..1);
}
}
impl TracerData {
pub fn new(device: &wgpu::Device, tracer: &Tracer, spheres: &[Sphere]) -> Self {
let spheres: Vec<_> = spheres.iter().map(SphereData::from).collect();
let spheres_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None,
contents: cast_slice(&spheres),
usage: wgpu::BufferUsages::STORAGE,
});
let bindings = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &tracer.pipeline.get_bind_group_layout(0),
entries: &[wgpu::BindGroupEntry {
binding: 1,
resource: spheres_buf.as_entire_binding(),
}],
});
Self { bindings }
}
}