Compare commits

..

No commits in common. "texture" and "master" have entirely different histories.

10 changed files with 50 additions and 766 deletions

1
Cargo.lock generated
View File

@ -3049,7 +3049,6 @@ dependencies = [
"flo_canvas", "flo_canvas",
"flo_draw", "flo_draw",
"glam 0.27.0", "glam 0.27.0",
"image",
"itertools 0.13.0", "itertools 0.13.0",
"itertools-num", "itertools-num",
"pollster", "pollster",

View File

@ -27,7 +27,6 @@ itertools = "0.13.0"
wgpu = "22.1.0" wgpu = "22.1.0"
bytemuck = { version = "1.18.0", features = ["derive"] } bytemuck = { version = "1.18.0", features = ["derive"] }
pollster = "0.3.0" pollster = "0.3.0"
image = {version = "0.23", default-features = false, features = ["png"] }
[dev-dependencies] [dev-dependencies]
approx = "0.5.1" approx = "0.5.1"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

View File

@ -1,5 +1,6 @@
# Blender 3.6.5 # Blender 3.6.5
# www.blender.org # www.blender.org
mtllib spacecraft2.mtl
o Cube o Cube
v -1.000000 0.000000 3.000000 v -1.000000 0.000000 3.000000
v -2.000000 0.000000 2.000000 v -2.000000 0.000000 2.000000
@ -59,68 +60,50 @@ vn 0.3487 -0.9300 -0.1162
vn -0.4444 -0.8889 -0.1111 vn -0.4444 -0.8889 -0.1111
vn -0.3015 -0.9045 0.3015 vn -0.3015 -0.9045 0.3015
vn 0.2182 -0.4364 0.8729 vn 0.2182 -0.4364 0.8729
vt 0.342147 0.420195 vt 0.000000 0.000000
vt 0.509380 0.482116
vt 0.342147 0.586861
vt 0.513220 0.668431
vt 0.680454 0.813685
vt 0.000000 0.840390
vt 0.684294 1.000000
vt 0.855367 0.914903
vt 0.855367 0.748236
vt 0.684294 0.666667
vt 0.594917 0.481234
vt 0.680454 0.147018
vt 0.769831 0.415785
vt 0.769830 0.582451
vt 0.513220 0.335097
vt 0.000000 0.173723
vt 0.684294 0.000000
vt 0.855367 0.248236
vt 0.855367 0.081569
vt 0.684294 0.333333
s 0 s 0
f 8/1/1 18/2/1 16/3/1 usemtl Material
f 16/3/2 18/2/2 15/4/2 f 8/1/1 18/1/1 16/1/1
f 18/2/3 19/5/3 15/4/3 f 16/1/2 18/1/2 15/1/2
f 15/4/4 19/5/4 14/6/4 f 18/1/3 19/1/3 15/1/3
f 14/6/5 19/5/5 13/7/5 f 15/1/4 19/1/4 14/1/4
f 13/7/6 19/5/6 12/8/6 f 14/1/5 19/1/5 13/1/5
f 12/8/7 19/5/7 11/9/7 f 13/1/6 19/1/6 12/1/6
f 10/10/8 11/9/8 19/5/8 f 12/1/7 19/1/7 11/1/7
f 20/11/9 18/2/9 17/12/9 f 10/1/8 11/1/8 19/1/8
f 1/13/7 9/14/7 20/11/7 f 20/1/9 18/1/9 17/1/9
f 9/14/10 10/10/10 20/11/10 f 1/1/7 9/1/7 20/1/7
f 20/11/11 10/10/11 19/5/11 f 9/1/10 10/1/10 20/1/10
f 6/15/12 18/2/12 8/1/12 f 20/1/11 10/1/11 19/1/11
f 18/2/13 6/15/13 17/12/13 f 6/1/12 18/1/12 8/1/12
f 6/15/14 7/16/14 17/12/14 f 18/1/13 6/1/13 17/1/13
f 5/17/15 17/12/15 7/16/15 f 6/1/14 7/1/14 17/1/14
f 3/18/7 17/12/7 4/19/7 f 5/1/15 17/1/15 7/1/15
f 4/19/8 17/12/8 5/17/8 f 3/1/7 17/1/7 4/1/7
f 2/20/6 17/12/6 3/18/6 f 4/1/8 17/1/8 5/1/8
f 1/13/16 20/11/16 2/20/16 f 2/1/6 17/1/6 3/1/6
f 17/12/17 2/20/17 20/11/17 f 1/1/16 20/1/16 2/1/16
f 20/11/9 19/5/9 18/2/9 f 17/1/17 2/1/17 20/1/17
f 8/1/18 16/3/18 22/2/18 f 20/1/9 19/1/9 18/1/9
f 16/3/19 15/4/19 22/2/19 f 8/1/18 16/1/18 22/1/18
f 22/2/20 15/4/20 23/5/20 f 16/1/19 15/1/19 22/1/19
f 15/4/21 14/6/21 23/5/21 f 22/1/20 15/1/20 23/1/20
f 14/6/22 13/7/22 23/5/22 f 15/1/21 14/1/21 23/1/21
f 13/7/23 12/8/23 23/5/23 f 14/1/22 13/1/22 23/1/22
f 12/8/24 11/9/24 23/5/24 f 13/1/23 12/1/23 23/1/23
f 10/10/25 23/5/25 11/9/25 f 12/1/24 11/1/24 23/1/24
f 24/11/26 21/12/26 22/2/26 f 10/1/25 23/1/25 11/1/25
f 1/13/24 24/11/24 9/14/24 f 24/1/26 21/1/26 22/1/26
f 9/14/27 24/11/27 10/10/27 f 1/1/24 24/1/24 9/1/24
f 24/11/28 23/5/28 10/10/28 f 9/1/27 24/1/27 10/1/27
f 6/15/29 8/1/29 22/2/29 f 24/1/28 23/1/28 10/1/28
f 22/2/30 21/12/30 6/15/30 f 6/1/29 8/1/29 22/1/29
f 6/15/31 21/12/31 7/16/31 f 22/1/30 21/1/30 6/1/30
f 5/17/32 7/16/32 21/12/32 f 6/1/31 21/1/31 7/1/31
f 3/18/24 4/19/24 21/12/24 f 5/1/32 7/1/32 21/1/32
f 4/19/25 5/17/25 21/12/25 f 3/1/24 4/1/24 21/1/24
f 2/20/23 3/18/23 21/12/23 f 4/1/25 5/1/25 21/1/25
f 1/13/33 2/20/33 24/11/33 f 2/1/23 3/1/23 21/1/23
f 21/12/34 24/11/34 2/20/34 f 1/1/33 2/1/33 24/1/33
f 24/11/26 22/2/26 23/5/26 f 21/1/34 24/1/34 2/1/34
f 24/1/26 22/1/26 23/1/26

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

View File

@ -1,162 +0,0 @@
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) base: vec3<f32>,
@location(1) dir: vec3<f32>,
};
const dist: f32 = 40.0;
const off: f32 = 10.0;
@vertex
fn vs_persp(@builtin(vertex_index) vn: u32) -> VertexOutput {
var out = VertexOutput(vec4(0.0), vec3(0.0), vec3(0.0));
switch (vn) {
case 0u: { out = VertexOutput(vec4(-1.0, 1.0, 0.0, 1.0), vec3(0.0, 0.0, -dist), vec3(-off, -off, dist)); }
case 1u: { out = VertexOutput(vec4(1.0, 1.0, 0.0, 1.0), vec3(0.0, 0.0, -dist), vec3(off, -off, dist)); }
case 2u: { out = VertexOutput(vec4(-1.0, -1.0, 0.0, 1.0), vec3(0.0, 0.0, -dist), vec3(-off, off, dist)); }
case 3u: { out = VertexOutput(vec4(1.0, -1.0, 0.0, 1.0), vec3(0.0, 0.0, -dist), vec3(off, off, dist)); }
default: { }
}
let PI = 3.1416;
let m_view = ypr_to_mat(vec3(135.0 * PI / 180.0, -30.0 * PI / 180.0, 0.0));
let m_camera = transpose(m_view);
out.base = m_camera * out.base;
out.dir = m_camera * out.dir;
return out;
}
@vertex
fn vs_ortho(@builtin(vertex_index) vn: u32) -> VertexOutput {
switch (vn) {
case 0u: { return VertexOutput(vec4(-1.0, 1.0, 0.0, 1.0), vec3(-off, -off, dist), vec3(0.0, 0.0, 1.0)); }
case 1u: { return VertexOutput(vec4(1.0, 1.0, 0.0, 1.0), vec3(off, -off, dist), vec3(0.0, 0.0, 1.0)); }
case 2u: { return VertexOutput(vec4(-1.0, -1.0, 0.0, 1.0), vec3(-off, off, dist), vec3(0.0, 0.0, 1.0)); }
case 3u: { return VertexOutput(vec4(1.0, -1.0, 0.0, 1.0), vec3(off, off, dist), vec3(0.0, 0.0, 1.0)); }
default: { return VertexOutput(vec4(0.0), vec3(0.0), vec3(0.0)); }
}
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
let ret = trace(Ray(in.base, in.dir));
if (ret.have_hit) {
return vec4(ret.tc, 0.0, 1.0);
} else {
return vec4(0.0, 0.0, 0.5, 1.0);
}
// return textureSample(t_traced, s_traced, in.rel_coords);
}
struct Ray {
base: vec3<f32>,
dir: vec3<f32>,
}
struct Face {
vertices: array<vec3<f32>, 3>,
tex_coords: mat3x2<f32>,
}
struct Input {
n_faces: u32,
n_rays: u32,
}
// @group(0) @binding(0)
// var<uniform> input: Input;
@group(0) @binding(0)
var<storage, read> faces: array<Face>;
// @group(0) @binding(1)
// var<storage, read> rays: array<Ray>;
// @compute @workgroup_size(64)
// fn main(
// @builtin(global_invocation_id)
// global_id : vec3u,
// ) {
// if (global_id.x >= input.n_rays) {
// return;
// }
// let ray = rays[global_id.x];
// // output[global_id.x] = f32(global_id.x) * 1000. + f32(local_id.x);
// }
struct TraceResult {
have_hit: bool,
face_id: u32,
tc: vec2<f32>,
}
fn trace(ray: Ray) -> TraceResult {
var have_hit = false;
var hit_i_face: u32;
var hit_rel: vec3<f32>;
for (var i_face = 0u; i_face < arrayLength(&faces); i_face++) {
let f = faces[i_face];
let fs = vec3(
edge_dist(f.vertices[0], f.vertices[1], ray),
edge_dist(f.vertices[1], f.vertices[2], ray),
edge_dist(f.vertices[2], f.vertices[0], ray),
);
if (any(fs < vec3(0.0))) {
continue;
}
let m1 = mat3x3(f.vertices[1] - f.vertices[0], f.vertices[2] - f.vertices[0], -ray.dir);
let m = inverse(m1);
let rel = m * (ray.base - f.vertices[0]);
if (!have_hit || rel.z < hit_rel.z) {
have_hit = true;
hit_i_face = i_face;
hit_rel = rel;
}
}
if (!have_hit) {
return TraceResult(false, 0, vec2(0.0));
}
let f = faces[hit_i_face];
let w = vec3(1. - hit_rel.x - hit_rel.y, hit_rel.x, hit_rel.y);
let tc = f.tex_coords * w;
return TraceResult(
true,
hit_i_face,
tc,
);
}
fn edge_dist(a: vec3<f32>, b: vec3<f32>, ray: Ray) -> f32 {
return determinant(mat3x3(b - a, ray.base - a, -ray.dir));
}
fn inverse(m: mat3x3<f32>) -> mat3x3<f32> {
let tmp0 = cross(m.y, m.z);
let tmp1 = cross(m.z, m.x);
let tmp2 = cross(m.x, m.y);
let det = dot(m.z, tmp2);
// glam_assert!(det != 0.0);
let inv_det = 1.0 / det;
return transpose(inv_det * mat3x3(tmp0, tmp1, tmp2));
}
fn ypr_to_mat(ypr: vec3<f32>) -> mat3x3<f32> {
let yaw = ypr.x;
let pitch = ypr.y;
let roll = ypr.z;
let m_roll = mat3x3(
vec3(cos(roll), sin(roll), 0.0),
vec3(-sin(roll), cos(roll), 0.0),
vec3(0.0, 0.0, 1.0),
);
let m_yaw = mat3x3(
vec3(cos(yaw), 0.0, sin(yaw)),
vec3(0.0, 1.0, 0.0),
vec3(-sin(yaw), 0.0, cos(yaw)),
);
let m_pitch = mat3x3(
vec3(1.0, 0.0, 0.0),
vec3(0.0, cos(pitch), -sin(pitch)),
vec3(0.0, sin(pitch), cos(pitch)),
);
return m_roll * m_pitch * m_yaw;
}

View File

@ -1,504 +0,0 @@
use bind_group::{BindGroup, BindGroupLayout};
use bytemuck::{Pod, Zeroable};
use glam::*;
use glam::{vec3, Vec2, Vec3};
use image::RgbImage;
use refraction::mesh_loader::load_mesh;
use refraction::mesh_tracer::{trace_to_mesh, Mesh};
use std::env;
use std::error::Error;
use std::f32::consts::PI;
use std::fs::File;
use std::io::BufReader;
use std::marker::PhantomData;
use std::process::exit;
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
use winit::keyboard::{KeyCode, PhysicalKey};
use winit::{
event::*,
event_loop::EventLoop,
window::{Window, WindowBuilder},
};
const W: u32 = 320;
const H: u32 = 240;
#[derive(Copy, Clone)]
struct Color(u8, u8, u8);
struct Image {
w: u32,
h: u32,
data: Vec<u8>,
}
impl Image {
fn data(&self) -> &[u8] {
self.data.as_slice()
}
fn put_pixel(&mut self, x: u32, y: u32, color: Color) {
if x >= self.w || y >= self.h {
return;
}
let index = 4 * (x + self.w * y) as usize;
self.data[index] = color.0;
self.data[index + 1] = color.1;
self.data[index + 2] = color.2;
self.data[index + 3] = 255;
}
}
fn ypr_to_mat(ypr: Vec3) -> Mat3 {
let Vec3 {
x: yaw,
y: pitch,
z: roll,
} = ypr;
let m_roll = mat3(
vec3(roll.cos(), roll.sin(), 0.0),
vec3(-roll.sin(), roll.cos(), 0.0),
vec3(0.0, 0.0, 1.0),
);
let m_yaw = mat3(
vec3(yaw.cos(), 0.0, yaw.sin()),
vec3(0.0, 1.0, 0.0),
vec3(-yaw.sin(), 0.0, yaw.cos()),
);
let m_pitch = mat3(
vec3(1.0, 0.0, 0.0),
vec3(0.0, pitch.cos(), -pitch.sin()),
vec3(0.0, pitch.sin(), pitch.cos()),
);
m_roll * m_pitch * m_yaw
}
fn render(mesh: &Mesh, texture: &RgbImage, camera: impl Fn(Vec2) -> (Vec3, Vec3)) -> Image {
let bkg = vec3(0.0, 0.0, 0.0);
let mut img = Image {
w: W,
h: H,
data: vec![0; (4 * W * H) as usize],
};
let img_size = vec2(W as f32, H as f32);
for y in 0..H {
for x in 0..W {
let img_coords = vec2(x as f32, y as f32);
let off = (img_coords - img_size * 0.5) / img_size.y;
let (base, ray) = camera(off);
let color = if let Some(r) = trace_to_mesh(mesh, base, ray.normalize()) {
// to_vec3(0.45) * dot(r.normal, normalize(vec3(-1.0, 1.0, -1.0))) + 0.50
let x = (r.tex_coords.x * texture.width() as f32) as u32;
let y = (r.tex_coords.y * texture.height() as f32) as u32;
texture.get_pixel(x, y).0.map(|channel| channel as f32 / 255.).into()
} else {
bkg
};
let color = (color * 255.0).as_ivec3().clamp(IVec3::splat(0), IVec3::splat(255));
img.put_pixel(x, y, Color(color.x as u8, color.y as u8, color.z as u8));
}
}
img
}
fn persp(dist: f32, off: Vec2) -> (Vec3, Vec3) {
(vec3(0., 0., -dist), vec3(off.x, off.y, dist))
}
fn ortho(dist: f32, off: Vec2) -> (Vec3, Vec3) {
(vec3(off.x, off.y, -dist), vec3(0., 0., 1.))
}
#[test]
fn test_projs() {
fn check(f: fn(dist: f32, off: Vec2) -> (Vec3, Vec3), x: f32, y: f32, z: f32) {
let (base, ray) = f(z, vec2(x, y));
let at_dist = base + ray * (z / ray.z);
assert_eq!(at_dist, vec3(x, y, 0.));
}
check(persp, 1., 2., 3.);
check(ortho, 1., 2., 3.);
check(persp, 5., 3., 7.);
check(ortho, 9., 1., 8.);
}
struct TracedBindGroup<'a> {
t_traced: &'a wgpu::TextureView,
s_traced: &'a wgpu::Sampler,
}
impl<'a> BindGroup for TracedBindGroup<'a> {
const LAYOUT: wgpu::BindGroupLayoutDescriptor<'static> = wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: true },
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
],
label: Some("TracedBindGroup"),
};
fn create(self, device: &wgpu::Device, layout: &BindGroupLayout<Self>) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(self.t_traced),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(self.s_traced),
},
],
label: Some("TracedBindGroup"),
})
}
}
struct HitMeshBindGroup<'a> {
buf_faces: wgpu::BufferBinding<'a>,
}
impl<'a> BindGroup for HitMeshBindGroup<'a> {
const LAYOUT: wgpu::BindGroupLayoutDescriptor<'static> = wgpu::BindGroupLayoutDescriptor {
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
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,
}],
label: Some("HitMeshBindGroup"),
};
fn create(self, device: &wgpu::Device, layout: &BindGroupLayout<Self>) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(self.buf_faces),
}],
label: Some("HitMeshBindGroup"),
})
}
}
// add_event_handler wants 'static + Send. Let it be so.
static PROJ_INDEX: AtomicUsize = AtomicUsize::new(0);
static PROJS: [fn(dist: f32, off: Vec2) -> (Vec3, Vec3); 2] = [persp, ortho];
fn main() -> Result<(), Box<dyn Error>> {
let args: Vec<String> = env::args().collect();
if args.len() != 3 {
println!("Usage: {} path/to/model.obj path/to/texture.png", args[0]);
exit(1);
}
let mesh = {
let f = File::open(&args[1])?;
let mut f = BufReader::new(f);
load_mesh(&mut f)?
};
let texture = image::io::Reader::open(&args[2])?.decode()?.into_rgb8();
let event_loop = EventLoop::new().unwrap();
let window = &WindowBuilder::new()
.with_title("Refraction: Textured")
.build(&event_loop)
.unwrap();
let (device, queue, surface) = pollster::block_on(init_gpu(window))?;
let traced_size = wgpu::Extent3d {
width: W,
height: H,
depth_or_array_layers: 1,
};
let traced_texture = device.create_texture(&wgpu::TextureDescriptor {
size: traced_size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
label: Some("tracing output"),
view_formats: &[],
});
let traced_view = traced_texture.create_view(&wgpu::TextureViewDescriptor::default());
let traced_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Nearest,
min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default()
});
let traced_bind_group_layout = BindGroupLayout::<TracedBindGroup>::create(&device);
let traced_bind_group = TracedBindGroup {
t_traced: &traced_view,
s_traced: &traced_sampler,
}
.create(&device, &traced_bind_group_layout);
let present_shader = device.create_shader_module(wgpu::include_wgsl!("present.wgsl"));
let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"),
bind_group_layouts: &[&traced_bind_group_layout],
push_constant_ranges: &[],
});
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: &present_shader,
entry_point: "vs_main",
buffers: &[],
compilation_options: wgpu::PipelineCompilationOptions::default(),
},
fragment: Some(wgpu::FragmentState {
module: &present_shader,
entry_point: "fs_main",
targets: &[Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::Bgra8UnormSrgb,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
compilation_options: wgpu::PipelineCompilationOptions::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleStrip,
..Default::default()
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
cache: None,
});
#[repr(C, align(16))]
#[derive(Clone, Copy, Pod, Zeroable)]
struct RawFace {
vertices: [[f32; 4]; 3],
tex_coords: [[f32; 2]; 3],
pad: [u32; 2],
}
let faces_buf = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: (mesh.len() * size_of::<RawFace>()) as u64,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let faces_content: Vec<_> = mesh
.iter()
.map(|face| RawFace {
vertices: face.vertices.map(|v| [v.x, v.y, v.z, 1.0]),
tex_coords: face.tex_coords.map(|v| v.into()),
pad: [0; 2],
})
.collect();
queue.write_buffer(&faces_buf, 0, bytemuck::cast_slice(&faces_content));
queue.submit([]);
let hit_mesh_bind_group_layout = BindGroupLayout::<HitMeshBindGroup>::create(&device);
let hit_mesh_bind_group = HitMeshBindGroup {
buf_faces: faces_buf.as_entire_buffer_binding(),
}
.create(&device, &hit_mesh_bind_group_layout);
let hit_mesh_shader = device.create_shader_module(wgpu::include_wgsl!("hit_mesh.wgsl"));
let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"),
bind_group_layouts: &[&hit_mesh_bind_group_layout],
push_constant_ranges: &[],
});
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: &hit_mesh_shader,
entry_point: "vs_persp",
buffers: &[],
compilation_options: wgpu::PipelineCompilationOptions::default(),
},
fragment: Some(wgpu::FragmentState {
module: &hit_mesh_shader,
entry_point: "fs_main",
targets: &[Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::Bgra8UnormSrgb,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
compilation_options: wgpu::PipelineCompilationOptions::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleStrip,
..Default::default()
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
cache: None,
});
let mut surface_configured = false;
let mut phi = 0;
event_loop.run(move |event, control_flow| match event {
Event::WindowEvent { ref event, window_id } if window_id == window.id() => match event {
WindowEvent::KeyboardInput {
device_id: _,
event,
is_synthetic: _,
} => {
if event.physical_key == PhysicalKey::Code(KeyCode::Tab) {
PROJ_INDEX.store((PROJ_INDEX.load(Relaxed) + 1) % PROJS.len(), Relaxed);
}
}
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,
},
);
surface_configured = true;
}
WindowEvent::RedrawRequested => {
window.request_redraw();
if !surface_configured {
return;
}
let proj = PROJS[PROJ_INDEX.load(Relaxed)];
let m_view = ypr_to_mat(vec3((135.0 + phi as f32) * PI / 180.0, -30.0 * PI / 180.0, 0.0f32));
let m_camera = m_view.transpose();
let img = render(mesh.as_slice(), &texture, |off| {
let (base, ray) = proj(40., 20. * off);
(m_camera * base, m_camera * ray)
});
phi += 1;
phi %= 360;
queue.write_texture(
wgpu::ImageCopyTexture {
texture: &traced_texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
&img.data,
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(4 * img.w),
rows_per_image: Some(img.h),
},
traced_size,
);
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: Some("Render CommandEncoder"),
});
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("RenderPass"),
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,
});
render_pass.set_pipeline(&render_pipeline);
render_pass.set_bind_group(0, &hit_mesh_bind_group, &[]);
render_pass.draw(0..4, 0..1);
drop(render_pass);
queue.submit(std::iter::once(encoder.finish()));
output.present();
}
_ => {}
},
_ => {}
})?;
Ok(())
}
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))
}
mod bind_group {
use std::{marker::PhantomData, ops::Deref};
pub struct BindGroupLayout<T: BindGroup + ?Sized>(wgpu::BindGroupLayout, PhantomData<*const T>);
impl<T: BindGroup + ?Sized> BindGroupLayout<T> {
pub fn create(device: &wgpu::Device) -> Self {
Self(device.create_bind_group_layout(&T::LAYOUT), PhantomData::default())
}
}
impl<T: BindGroup + ?Sized> Deref for BindGroupLayout<T> {
type Target = wgpu::BindGroupLayout;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub trait BindGroup {
const LAYOUT: wgpu::BindGroupLayoutDescriptor<'_>;
fn create(self, device: &wgpu::Device, layout: &BindGroupLayout<Self>) -> wgpu::BindGroup;
}
}

View File

@ -1,26 +0,0 @@
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) rel_coords: vec2<f32>,
};
@vertex
fn vs_main(
@builtin(vertex_index) in_vertex_index: u32,
) -> VertexOutput {
var out: VertexOutput;
let x = f32(in_vertex_index & 1u);
let y = f32((in_vertex_index & 2u) >> 1);
out.rel_coords = vec2(x, y);
out.clip_position = vec4<f32>(2.0 * x - 1.0, 1.0 - 2.0 * y, 0.0, 1.0);
return out;
}
@group(0) @binding(0)
var t_traced: texture_2d<f32>;
@group(0) @binding(1)
var s_traced: sampler;
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
return textureSample(t_traced, s_traced, in.rel_coords);
}

View File

@ -5,7 +5,7 @@ use std::io;
struct ObjVertex { struct ObjVertex {
vertex: usize, vertex: usize,
normal: usize, normal: usize,
tex_coord: usize, // tex_coord: usize,
} }
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@ -39,7 +39,7 @@ impl ObjMesh {
assert_eq!(tokens.len(), 3); assert_eq!(tokens.len(), 3);
ObjVertex { ObjVertex {
vertex: tokens[0], vertex: tokens[0],
tex_coord: tokens[1], // tex_coord: tokens[1],
normal: tokens[2], normal: tokens[2],
} }
} }
@ -80,7 +80,6 @@ impl ObjMesh {
.iter() .iter()
.map(|face| Face { .map(|face| Face {
vertices: face.vertices.map(|iv| self.vertices[iv.vertex]), vertices: face.vertices.map(|iv| self.vertices[iv.vertex]),
tex_coords: face.vertices.map(|iv| self.tex_coords[iv.tex_coord]),
normal: self.normals[face.vertices[0].normal], normal: self.normals[face.vertices[0].normal],
}) })
.collect() .collect()
@ -90,7 +89,6 @@ impl ObjMesh {
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct Face { pub struct Face {
pub vertices: [Vec3; 3], pub vertices: [Vec3; 3],
pub tex_coords: [Vec2; 3],
pub normal: Vec3, pub normal: Vec3,
} }

View File

@ -1,11 +1,10 @@
use crate::mesh_loader::Face; use crate::mesh_loader::Face;
use glam::{mat3, vec3, Vec2, Vec3}; use glam::{mat3, Vec3};
pub type Mesh = [Face]; pub type Mesh = [Face];
pub struct TraceResult { pub struct TraceResult {
pub distance: f32, pub distance: f32,
pub tex_coords: Vec2,
pub normal: Vec3, pub normal: Vec3,
} }
@ -18,10 +17,8 @@ pub fn trace_to_mesh_all(mesh: &Mesh, base: Vec3, ray: Vec3) -> impl Iterator<It
let m = mat3(f.vertices[1] - f.vertices[0], f.vertices[2] - f.vertices[0], -ray); let m = mat3(f.vertices[1] - f.vertices[0], f.vertices[2] - f.vertices[0], -ray);
let m = m.inverse(); let m = m.inverse();
let rel = m * (base - f.vertices[0]); let rel = m * (base - f.vertices[0]);
let w = vec3(1. - rel.x - rel.y, rel.x, rel.y);
Some(TraceResult { Some(TraceResult {
distance: rel.z, distance: rel.z,
tex_coords: w.x * f.tex_coords[0] + w.y * f.tex_coords[1] + w.z * f.tex_coords[2],
normal: f.normal, normal: f.normal,
}) })
}) })