Compare commits
No commits in common. "texture" and "master" have entirely different histories.
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -3049,7 +3049,6 @@ dependencies = [
|
|||
"flo_canvas",
|
||||
"flo_draw",
|
||||
"glam 0.27.0",
|
||||
"image",
|
||||
"itertools 0.13.0",
|
||||
"itertools-num",
|
||||
"pollster",
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ itertools = "0.13.0"
|
|||
wgpu = "22.1.0"
|
||||
bytemuck = { version = "1.18.0", features = ["derive"] }
|
||||
pollster = "0.3.0"
|
||||
image = {version = "0.23", default-features = false, features = ["png"] }
|
||||
|
||||
[dev-dependencies]
|
||||
approx = "0.5.1"
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 38 KiB |
|
|
@ -1,5 +1,6 @@
|
|||
# Blender 3.6.5
|
||||
# www.blender.org
|
||||
mtllib spacecraft2.mtl
|
||||
o Cube
|
||||
v -1.000000 0.000000 3.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.3015 -0.9045 0.3015
|
||||
vn 0.2182 -0.4364 0.8729
|
||||
vt 0.342147 0.420195
|
||||
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
|
||||
vt 0.000000 0.000000
|
||||
s 0
|
||||
f 8/1/1 18/2/1 16/3/1
|
||||
f 16/3/2 18/2/2 15/4/2
|
||||
f 18/2/3 19/5/3 15/4/3
|
||||
f 15/4/4 19/5/4 14/6/4
|
||||
f 14/6/5 19/5/5 13/7/5
|
||||
f 13/7/6 19/5/6 12/8/6
|
||||
f 12/8/7 19/5/7 11/9/7
|
||||
f 10/10/8 11/9/8 19/5/8
|
||||
f 20/11/9 18/2/9 17/12/9
|
||||
f 1/13/7 9/14/7 20/11/7
|
||||
f 9/14/10 10/10/10 20/11/10
|
||||
f 20/11/11 10/10/11 19/5/11
|
||||
f 6/15/12 18/2/12 8/1/12
|
||||
f 18/2/13 6/15/13 17/12/13
|
||||
f 6/15/14 7/16/14 17/12/14
|
||||
f 5/17/15 17/12/15 7/16/15
|
||||
f 3/18/7 17/12/7 4/19/7
|
||||
f 4/19/8 17/12/8 5/17/8
|
||||
f 2/20/6 17/12/6 3/18/6
|
||||
f 1/13/16 20/11/16 2/20/16
|
||||
f 17/12/17 2/20/17 20/11/17
|
||||
f 20/11/9 19/5/9 18/2/9
|
||||
f 8/1/18 16/3/18 22/2/18
|
||||
f 16/3/19 15/4/19 22/2/19
|
||||
f 22/2/20 15/4/20 23/5/20
|
||||
f 15/4/21 14/6/21 23/5/21
|
||||
f 14/6/22 13/7/22 23/5/22
|
||||
f 13/7/23 12/8/23 23/5/23
|
||||
f 12/8/24 11/9/24 23/5/24
|
||||
f 10/10/25 23/5/25 11/9/25
|
||||
f 24/11/26 21/12/26 22/2/26
|
||||
f 1/13/24 24/11/24 9/14/24
|
||||
f 9/14/27 24/11/27 10/10/27
|
||||
f 24/11/28 23/5/28 10/10/28
|
||||
f 6/15/29 8/1/29 22/2/29
|
||||
f 22/2/30 21/12/30 6/15/30
|
||||
f 6/15/31 21/12/31 7/16/31
|
||||
f 5/17/32 7/16/32 21/12/32
|
||||
f 3/18/24 4/19/24 21/12/24
|
||||
f 4/19/25 5/17/25 21/12/25
|
||||
f 2/20/23 3/18/23 21/12/23
|
||||
f 1/13/33 2/20/33 24/11/33
|
||||
f 21/12/34 24/11/34 2/20/34
|
||||
f 24/11/26 22/2/26 23/5/26
|
||||
usemtl Material
|
||||
f 8/1/1 18/1/1 16/1/1
|
||||
f 16/1/2 18/1/2 15/1/2
|
||||
f 18/1/3 19/1/3 15/1/3
|
||||
f 15/1/4 19/1/4 14/1/4
|
||||
f 14/1/5 19/1/5 13/1/5
|
||||
f 13/1/6 19/1/6 12/1/6
|
||||
f 12/1/7 19/1/7 11/1/7
|
||||
f 10/1/8 11/1/8 19/1/8
|
||||
f 20/1/9 18/1/9 17/1/9
|
||||
f 1/1/7 9/1/7 20/1/7
|
||||
f 9/1/10 10/1/10 20/1/10
|
||||
f 20/1/11 10/1/11 19/1/11
|
||||
f 6/1/12 18/1/12 8/1/12
|
||||
f 18/1/13 6/1/13 17/1/13
|
||||
f 6/1/14 7/1/14 17/1/14
|
||||
f 5/1/15 17/1/15 7/1/15
|
||||
f 3/1/7 17/1/7 4/1/7
|
||||
f 4/1/8 17/1/8 5/1/8
|
||||
f 2/1/6 17/1/6 3/1/6
|
||||
f 1/1/16 20/1/16 2/1/16
|
||||
f 17/1/17 2/1/17 20/1/17
|
||||
f 20/1/9 19/1/9 18/1/9
|
||||
f 8/1/18 16/1/18 22/1/18
|
||||
f 16/1/19 15/1/19 22/1/19
|
||||
f 22/1/20 15/1/20 23/1/20
|
||||
f 15/1/21 14/1/21 23/1/21
|
||||
f 14/1/22 13/1/22 23/1/22
|
||||
f 13/1/23 12/1/23 23/1/23
|
||||
f 12/1/24 11/1/24 23/1/24
|
||||
f 10/1/25 23/1/25 11/1/25
|
||||
f 24/1/26 21/1/26 22/1/26
|
||||
f 1/1/24 24/1/24 9/1/24
|
||||
f 9/1/27 24/1/27 10/1/27
|
||||
f 24/1/28 23/1/28 10/1/28
|
||||
f 6/1/29 8/1/29 22/1/29
|
||||
f 22/1/30 21/1/30 6/1/30
|
||||
f 6/1/31 21/1/31 7/1/31
|
||||
f 5/1/32 7/1/32 21/1/32
|
||||
f 3/1/24 4/1/24 21/1/24
|
||||
f 4/1/25 5/1/25 21/1/25
|
||||
f 2/1/23 3/1/23 21/1/23
|
||||
f 1/1/33 2/1/33 24/1/33
|
||||
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 |
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ use std::io;
|
|||
struct ObjVertex {
|
||||
vertex: usize,
|
||||
normal: usize,
|
||||
tex_coord: usize,
|
||||
// tex_coord: usize,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
@ -39,7 +39,7 @@ impl ObjMesh {
|
|||
assert_eq!(tokens.len(), 3);
|
||||
ObjVertex {
|
||||
vertex: tokens[0],
|
||||
tex_coord: tokens[1],
|
||||
// tex_coord: tokens[1],
|
||||
normal: tokens[2],
|
||||
}
|
||||
}
|
||||
|
|
@ -80,7 +80,6 @@ impl ObjMesh {
|
|||
.iter()
|
||||
.map(|face| Face {
|
||||
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],
|
||||
})
|
||||
.collect()
|
||||
|
|
@ -90,7 +89,6 @@ impl ObjMesh {
|
|||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Face {
|
||||
pub vertices: [Vec3; 3],
|
||||
pub tex_coords: [Vec2; 3],
|
||||
pub normal: Vec3,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
use crate::mesh_loader::Face;
|
||||
use glam::{mat3, vec3, Vec2, Vec3};
|
||||
use glam::{mat3, Vec3};
|
||||
|
||||
pub type Mesh = [Face];
|
||||
|
||||
pub struct TraceResult {
|
||||
pub distance: f32,
|
||||
pub tex_coords: Vec2,
|
||||
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 = m.inverse();
|
||||
let rel = m * (base - f.vertices[0]);
|
||||
let w = vec3(1. - rel.x - rel.y, rel.x, rel.y);
|
||||
Some(TraceResult {
|
||||
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,
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user