Tracing works!!!
This commit is contained in:
commit
cb741c5360
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/target
|
||||||
18
.kateproject.build
Normal file
18
.kateproject.build
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"Auto_generated": "This file is auto-generated. Any extra tags or formatting will be lost",
|
||||||
|
"target_sets": [
|
||||||
|
{
|
||||||
|
"cmake_config": "",
|
||||||
|
"directory": "%B",
|
||||||
|
"loaded_via_cmake": false,
|
||||||
|
"name": "Cargo",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"build_cmd": "cargo build",
|
||||||
|
"name": "build",
|
||||||
|
"run_cmd": "cargo run"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
2226
Cargo.lock
generated
Normal file
2226
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "raytracing3"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bytemuck = { version = "1.21.0", features = ["derive"] }
|
||||||
|
pollster = "0.4.0"
|
||||||
|
wgpu = "23.0.1"
|
||||||
|
winit = "0.30.6"
|
||||||
2
rustfmt.toml
Normal file
2
rustfmt.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
hard_tabs = true
|
||||||
|
max_width = 120
|
||||||
152
src/main.rs
Normal file
152
src/main.rs
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use trace::{Sphere, Tracer, Vertex};
|
||||||
|
use winit::{
|
||||||
|
event::{Event, WindowEvent},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
window::{Window, WindowAttributes},
|
||||||
|
};
|
||||||
|
|
||||||
|
mod trace;
|
||||||
|
|
||||||
|
fn make_viewport(w: u32, h: u32) -> [Vertex; 4] {
|
||||||
|
let w = w as f32;
|
||||||
|
let h = h as f32;
|
||||||
|
let (w, h) = (1.0f32.max(w / h), 1.0f32.max(h / w));
|
||||||
|
let r = 3.0f32;
|
||||||
|
let screen_coord = [[-h, -w], [h, -w], [-h, w], [h, w]];
|
||||||
|
let eye = [-r, 0.0, 0.0];
|
||||||
|
let world_coord = [[0.0, -1.0, -1.0], [0.0, 1.0, -1.0], [0.0, -1.0, 1.0], [0.0, 1.0, 1.0]];
|
||||||
|
[0, 1, 2, 3].map(|k| Vertex {
|
||||||
|
eye,
|
||||||
|
world: world_coord[k],
|
||||||
|
screen: screen_coord[k],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
|
let window = &event_loop
|
||||||
|
.create_window(WindowAttributes::new().with_title("Ray tracing reflection test"))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (device, queue, surface) = pollster::block_on(init_gpu(window)).unwrap();
|
||||||
|
|
||||||
|
let mut tracer = Tracer::new(&device);
|
||||||
|
tracer.set_params(
|
||||||
|
&queue,
|
||||||
|
trace::Params {
|
||||||
|
max_reflections: 3,
|
||||||
|
min_strength: 0.1,
|
||||||
|
sphere_count: 2,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
tracer.set_spheres(
|
||||||
|
&device,
|
||||||
|
&[
|
||||||
|
Sphere {
|
||||||
|
center: [0.0, 0.0, 0.0],
|
||||||
|
radius: 0.5,
|
||||||
|
emit_color: [0.0, 0.0, 0.5],
|
||||||
|
reflect_color: [1.0, 0.5, 0.0],
|
||||||
|
pad1: 0.,
|
||||||
|
pad2: 0.,
|
||||||
|
},
|
||||||
|
Sphere {
|
||||||
|
center: [-2.0, 0.0, 2.0],
|
||||||
|
radius: 0.5,
|
||||||
|
emit_color: [0.5, 0.5, 0.5],
|
||||||
|
reflect_color: [0.0, 0.5, 0.0],
|
||||||
|
pad1: 0.,
|
||||||
|
pad2: 0.,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut surface_configured = false;
|
||||||
|
#[allow(deprecated)]
|
||||||
|
event_loop
|
||||||
|
.run(move |event, control_flow| match event {
|
||||||
|
Event::WindowEvent { ref event, window_id } if window_id == window.id() => match event {
|
||||||
|
WindowEvent::CloseRequested => control_flow.exit(),
|
||||||
|
WindowEvent::Resized(physical_size) => {
|
||||||
|
surface.configure(
|
||||||
|
&device,
|
||||||
|
&wgpu::SurfaceConfiguration {
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST,
|
||||||
|
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||||
|
width: physical_size.width,
|
||||||
|
height: physical_size.height,
|
||||||
|
present_mode: wgpu::PresentMode::Fifo,
|
||||||
|
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
||||||
|
view_formats: vec![],
|
||||||
|
desired_maximum_frame_latency: 2,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
tracer.set_view(&queue, &make_viewport(physical_size.width, physical_size.height));
|
||||||
|
surface_configured = true;
|
||||||
|
}
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
window.request_redraw();
|
||||||
|
if !surface_configured {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let output = surface.get_current_texture().unwrap();
|
||||||
|
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||||
|
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
label: None,
|
||||||
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
view: &view,
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
tracer.render(&mut render_pass);
|
||||||
|
drop(render_pass);
|
||||||
|
queue.submit(std::iter::once(encoder.finish()));
|
||||||
|
output.present();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn init_gpu(wnd: &Window) -> Result<(wgpu::Device, wgpu::Queue, wgpu::Surface), Box<dyn Error>> {
|
||||||
|
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
||||||
|
backends: wgpu::Backends::PRIMARY,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
let surface = instance.create_surface(wnd)?;
|
||||||
|
let adapter = instance
|
||||||
|
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||||
|
power_preference: wgpu::PowerPreference::default(),
|
||||||
|
compatible_surface: None,
|
||||||
|
force_fallback_adapter: false,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let (device, queue) = adapter
|
||||||
|
.request_device(
|
||||||
|
&wgpu::DeviceDescriptor {
|
||||||
|
label: None,
|
||||||
|
required_features: wgpu::Features::empty(),
|
||||||
|
required_limits: wgpu::Limits::default(),
|
||||||
|
memory_hints: Default::default(),
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
Ok((device, queue, surface))
|
||||||
|
}
|
||||||
178
src/trace.rs
Normal file
178
src/trace.rs
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
use std::mem::{self, offset_of};
|
||||||
|
|
||||||
|
use bytemuck::{bytes_of, cast_slice, Pod, Zeroable};
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Sphere {
|
||||||
|
pub center: [f32; 3],
|
||||||
|
pub radius: f32,
|
||||||
|
pub emit_color: [f32; 3],
|
||||||
|
pub pad1: f32,
|
||||||
|
pub reflect_color: [f32; 3],
|
||||||
|
pub pad2: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Vertex {
|
||||||
|
pub eye: [f32; 3],
|
||||||
|
pub world: [f32; 3],
|
||||||
|
pub screen: [f32; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Tracer {
|
||||||
|
view_buf: wgpu::Buffer,
|
||||||
|
params_buf: wgpu::Buffer,
|
||||||
|
spheres_buf: wgpu::Buffer,
|
||||||
|
bindings: wgpu::BindGroup,
|
||||||
|
pipeline: wgpu::RenderPipeline,
|
||||||
|
}
|
||||||
|
|
||||||
|
static SHADER: &str = include_str!("trace.wgsl");
|
||||||
|
|
||||||
|
impl Tracer {
|
||||||
|
pub fn new(device: &wgpu::Device) -> Self {
|
||||||
|
let view_buf = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: (4 * mem::size_of::<Vertex>()) as u64,
|
||||||
|
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
let params_buf = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: mem::size_of::<Params>() as u64,
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
let spheres_buf = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: mem::size_of::<Sphere>() as u64,
|
||||||
|
usage: wgpu::BufferUsages::STORAGE,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
|
label: None,
|
||||||
|
source: wgpu::ShaderSource::Wgsl(SHADER.into()),
|
||||||
|
});
|
||||||
|
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: None,
|
||||||
|
layout: None,
|
||||||
|
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: offset_of!(Vertex, eye) as u64,
|
||||||
|
format: wgpu::VertexFormat::Float32x3,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
shader_location: 1,
|
||||||
|
offset: offset_of!(Vertex, world) as u64,
|
||||||
|
format: wgpu::VertexFormat::Float32x3,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
shader_location: 2,
|
||||||
|
offset: offset_of!(Vertex, screen) as u64,
|
||||||
|
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: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||||
|
blend: Some(wgpu::BlendState::REPLACE),
|
||||||
|
write_mask: wgpu::ColorWrites::ALL,
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
multiview: None,
|
||||||
|
cache: None,
|
||||||
|
});
|
||||||
|
let bindings = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: None,
|
||||||
|
layout: &pipeline.get_bind_group_layout(0),
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: params_buf.as_entire_binding(),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: spheres_buf.as_entire_binding(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
Self {
|
||||||
|
view_buf,
|
||||||
|
params_buf,
|
||||||
|
spheres_buf,
|
||||||
|
bindings,
|
||||||
|
pipeline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_params(&mut self, queue: &wgpu::Queue, params: Params) {
|
||||||
|
queue.write_buffer(&self.params_buf, 0, bytes_of(¶ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_view(&mut self, queue: &wgpu::Queue, vertices: &[Vertex; 4]) {
|
||||||
|
queue.write_buffer(&self.view_buf, 0, bytes_of(vertices));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_spheres(&mut self, device: &wgpu::Device, spheres: &[Sphere]) {
|
||||||
|
self.spheres_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: None,
|
||||||
|
contents: cast_slice(spheres),
|
||||||
|
usage: wgpu::BufferUsages::STORAGE,
|
||||||
|
});
|
||||||
|
self.bindings = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: None,
|
||||||
|
layout: &self.pipeline.get_bind_group_layout(0),
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: self.params_buf.as_entire_binding(),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: self.spheres_buf.as_entire_binding(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&self, pass: &mut wgpu::RenderPass) {
|
||||||
|
pass.set_pipeline(&self.pipeline);
|
||||||
|
pass.set_vertex_buffer(0, self.view_buf.slice(..));
|
||||||
|
pass.set_bind_group(0, &self.bindings, &[]);
|
||||||
|
pass.draw(0..4, 0..1);
|
||||||
|
}
|
||||||
|
}
|
||||||
93
src/trace.wgsl
Normal file
93
src/trace.wgsl
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
struct Params {
|
||||||
|
max_reflections: i32,
|
||||||
|
min_strength: f32,
|
||||||
|
sphere_count: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Sphere {
|
||||||
|
center: vec3f,
|
||||||
|
radius: f32,
|
||||||
|
emit_color: vec3f,
|
||||||
|
reflect_color: vec3f,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Vertex {
|
||||||
|
@location(0) eye: vec3f,
|
||||||
|
@location(1) world: vec3f,
|
||||||
|
@location(2) screen: vec2f,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Varying {
|
||||||
|
@location(0) eye: vec3f,
|
||||||
|
@location(1) world: vec3f,
|
||||||
|
@builtin(position) screen: vec4f,
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0) var<uniform> params: Params;
|
||||||
|
@group(0) @binding(1) var<storage, read> spheres: array<Sphere>;
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn on_vertex(in: Vertex) -> Varying {
|
||||||
|
return Varying(in.eye, in.world, vec4(in.screen, 0.0, 1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn on_fragment(in: Varying) -> @location(0) vec4f {
|
||||||
|
return trace_fragment(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sqr(v: vec3f) -> f32 {
|
||||||
|
return dot(v, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
var<private> pos: vec3f;
|
||||||
|
var<private> ray: vec3f;
|
||||||
|
|
||||||
|
fn to_sphere(center: vec3f, radius: f32, t: ptr<function, f32>) -> bool {
|
||||||
|
let c = sqr(pos - center) - radius * radius;
|
||||||
|
let b = 2 * dot(pos - center, ray);
|
||||||
|
let a = sqr(ray);
|
||||||
|
|
||||||
|
let D = b * b - 4 * a * c;
|
||||||
|
if (D <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*t = (- b - sqrt(D)) / (2 * a);
|
||||||
|
if (*t < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trace_fragment(in: Varying) -> vec4f {
|
||||||
|
var result = vec4(0.0, 0.0, 0.0, 0.0);
|
||||||
|
var color = vec3(1.0, 1.0, 1.0);
|
||||||
|
pos = in.eye;
|
||||||
|
ray = normalize(in.world - in.eye);
|
||||||
|
|
||||||
|
for (var k = 0; k < params.max_reflections; k++) {
|
||||||
|
var sphere = -1;
|
||||||
|
var t = 1.0e9;
|
||||||
|
for (var k = 0; k < params.sphere_count; k++) {
|
||||||
|
var t1: f32;
|
||||||
|
if (to_sphere(spheres[k].center, spheres[k].radius, &t1) && t1 < t) {
|
||||||
|
sphere = k;
|
||||||
|
t = t1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sphere == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let s = spheres[sphere];
|
||||||
|
pos += t * ray;
|
||||||
|
let normal = (pos - s.center) / s.radius;
|
||||||
|
result += vec4(color * s.emit_color * -dot(normal, ray), 0.0);
|
||||||
|
color *= s.reflect_color;
|
||||||
|
ray = reflect(ray, normal);
|
||||||
|
if (length(color) < params.min_strength) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return clamp(result, vec4(0.0), vec4(1.0));
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user