added smooth shading
This commit is contained in:
parent
182f7f2ba1
commit
0a3e4a8281
10
README.md
10
README.md
|
@ -2,12 +2,14 @@
|
||||||
Eruption is a vulkan based pathtracer. It is in an experimental state an may have performance issues
|
Eruption is a vulkan based pathtracer. It is in an experimental state an may have performance issues
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
* Oren-Nayar diffuse pathtracing
|
* flat and gouraud shading
|
||||||
* Specular reflection with roughness
|
* physically based rendering (PBR)
|
||||||
* Cosine weighted ray generation
|
* Oren-Nayar diffuse BRDF
|
||||||
|
* Schlick approximation for fresnel equations
|
||||||
|
* Cosine weighted sampling
|
||||||
* Temporal anti aliasing
|
* Temporal anti aliasing
|
||||||
* Realtime denoising
|
* Realtime denoising
|
||||||
* Tonemapping
|
* Reinhard jodie tonemapping
|
||||||
* Progressive sampling
|
* Progressive sampling
|
||||||
|
|
||||||
# Screenshots
|
# Screenshots
|
||||||
|
|
50
res/head.mtl
50
res/head.mtl
|
@ -1,7 +1,17 @@
|
||||||
# Blender 3.4.1 MTL File: 'None'
|
# Blender 3.4.1 MTL File: 'None'
|
||||||
# www.blender.org
|
# www.blender.org
|
||||||
|
|
||||||
newmtl light
|
newmtl glass
|
||||||
|
Ns 1000.000000
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.800000 0.800000 0.800000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.450000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl glossy
|
||||||
Ns 250.000000
|
Ns 250.000000
|
||||||
Ka 1.000000 1.000000 1.000000
|
Ka 1.000000 1.000000 1.000000
|
||||||
Kd 0.800000 0.800000 0.800000
|
Kd 0.800000 0.800000 0.800000
|
||||||
|
@ -11,15 +21,45 @@ Ni 1.450000
|
||||||
d 1.000000
|
d 1.000000
|
||||||
illum 2
|
illum 2
|
||||||
|
|
||||||
newmtl light_blue
|
newmtl green
|
||||||
Ns 250.000000
|
Ns 0.000000
|
||||||
Ka 1.000000 1.000000 1.000000
|
Ka 1.000000 1.000000 1.000000
|
||||||
Kd 0.800000 0.800000 0.800000
|
Kd 0.800000 0.800000 0.800000
|
||||||
Ks 0.500000 0.500000 0.500000
|
Ks 0.000000 0.000000 0.000000
|
||||||
Ke 0.000000 0.000000 0.000000
|
Ke 0.000000 0.000000 0.000000
|
||||||
Ni 1.450000
|
Ni 1.450000
|
||||||
d 1.000000
|
d 1.000000
|
||||||
illum 2
|
illum 1
|
||||||
|
|
||||||
|
newmtl light
|
||||||
|
Ns 0.000000
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.800000 0.800000 0.800000
|
||||||
|
Ks 0.000000 0.000000 0.000000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.450000
|
||||||
|
d 1.000000
|
||||||
|
illum 1
|
||||||
|
|
||||||
|
newmtl red
|
||||||
|
Ns 0.000000
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.800000 0.800000 0.800000
|
||||||
|
Ks 0.000000 0.000000 0.000000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.450000
|
||||||
|
d 1.000000
|
||||||
|
illum 1
|
||||||
|
|
||||||
|
newmtl white
|
||||||
|
Ns 0.000000
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.800000 0.800000 0.800000
|
||||||
|
Ks 0.000000 0.000000 0.000000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.450000
|
||||||
|
d 1.000000
|
||||||
|
illum 1
|
||||||
|
|
||||||
newmtl white
|
newmtl white
|
||||||
Ns 250.000000
|
Ns 250.000000
|
||||||
|
|
14473
res/head.obj
14473
res/head.obj
File diff suppressed because it is too large
Load Diff
|
@ -1 +0,0 @@
|
||||||
|
|
|
@ -17,8 +17,8 @@ impl Camera {
|
||||||
front: Vector3::new(0.0, 0.0, -1.0),
|
front: Vector3::new(0.0, 0.0, -1.0),
|
||||||
left: Vector3::new(1.0, 0.0, 0.0),
|
left: Vector3::new(1.0, 0.0, 0.0),
|
||||||
up: Vector3::new(0.0, -1.0, 0.0),
|
up: Vector3::new(0.0, -1.0, 0.0),
|
||||||
pos: Vector3::new(0.0, 0.0, 9.0),
|
pos: Vector3::new(0.0, 0.0, 3.9),
|
||||||
fov: 65.0f32
|
fov: 120.0f32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer};
|
use vulkano::buffer::{Buffer, BufferContents, BufferCreateInfo, BufferError, BufferUsage, Subbuffer};
|
||||||
use vulkano::buffer::allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo};
|
use vulkano::buffer::allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo};
|
||||||
use vulkano::command_buffer::allocator::StandardCommandBufferAllocator;
|
use vulkano::command_buffer::allocator::StandardCommandBufferAllocator;
|
||||||
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, PrimaryCommandBufferAbstract};
|
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, PrimaryCommandBufferAbstract};
|
||||||
|
@ -50,29 +50,40 @@ fn add_default_materials() {
|
||||||
albedo: Padded::from([1.0, 1.0, 1.0]),
|
albedo: Padded::from([1.0, 1.0, 1.0]),
|
||||||
emission: Padded::from([0.0, 0.0, 0.0]),
|
emission: Padded::from([0.0, 0.0, 0.0]),
|
||||||
specular_color: [0.0, 0.0, 0.0],
|
specular_color: [0.0, 0.0, 0.0],
|
||||||
roughness: 1.0,
|
roughness: 0.4,
|
||||||
specular: 0.0,
|
specular: 0.0,
|
||||||
transmission: 0.0,
|
transmission: 0.0,
|
||||||
ior: 1.0,
|
ior: 1.0,
|
||||||
metallic: false as u32,
|
metallic: false as u32,
|
||||||
});
|
});
|
||||||
|
|
||||||
material_collection.insert(String::from("glossy"), cs::Material {
|
material_collection.insert(String::from("mirror"), cs::Material {
|
||||||
albedo: Padded::from([1.0, 1.0, 1.0]),
|
albedo: Padded::from([1.0, 1.0, 1.0]),
|
||||||
emission: Padded::from([0.0, 0.0, 0.0]),
|
emission: Padded::from([0.0, 0.0, 0.0]),
|
||||||
specular_color: [0.0, 0.0, 0.0],
|
specular_color: [0.0, 0.0, 0.0],
|
||||||
roughness: 0.0,
|
roughness: 0.12,
|
||||||
specular: 0.8,
|
specular: 1.0,
|
||||||
transmission: 0.0,
|
transmission: 0.0,
|
||||||
ior: 1.0,
|
ior: 1.0,
|
||||||
metallic: false as u32,
|
metallic: true as u32,
|
||||||
|
});
|
||||||
|
|
||||||
|
material_collection.insert(String::from("gold"), cs::Material {
|
||||||
|
albedo: Padded::from([0.944, 0.776, 0.373]),
|
||||||
|
emission: Padded::from([0.0, 0.0, 0.0]),
|
||||||
|
specular_color: [0.0, 0.0, 0.0],
|
||||||
|
roughness: 0.4,
|
||||||
|
specular: 0.0,
|
||||||
|
transmission: 0.0,
|
||||||
|
ior: 1.0,
|
||||||
|
metallic: true as u32,
|
||||||
});
|
});
|
||||||
|
|
||||||
material_collection.insert(String::from("red"), cs::Material {
|
material_collection.insert(String::from("red"), cs::Material {
|
||||||
albedo: Padded::from([1.0, 0.0, 0.0]),
|
albedo: Padded::from([1.0, 0.0, 0.0]),
|
||||||
emission: Padded::from([0.0, 0.0, 0.0]),
|
emission: Padded::from([0.0, 0.0, 0.0]),
|
||||||
specular_color: [0.0, 0.0, 0.0],
|
specular_color: [0.0, 0.0, 0.0],
|
||||||
roughness: 1.0,
|
roughness: 0.4,
|
||||||
specular: 0.0,
|
specular: 0.0,
|
||||||
transmission: 0.0,
|
transmission: 0.0,
|
||||||
ior: 1.0,
|
ior: 1.0,
|
||||||
|
@ -83,7 +94,7 @@ fn add_default_materials() {
|
||||||
albedo: Padded::from([0.0, 1.0, 0.0]),
|
albedo: Padded::from([0.0, 1.0, 0.0]),
|
||||||
emission: Padded::from([0.0, 0.0, 0.0]),
|
emission: Padded::from([0.0, 0.0, 0.0]),
|
||||||
specular_color: [0.0, 0.0, 0.0],
|
specular_color: [0.0, 0.0, 0.0],
|
||||||
roughness: 1.0,
|
roughness: 0.4,
|
||||||
specular: 0.0,
|
specular: 0.0,
|
||||||
transmission: 0.0,
|
transmission: 0.0,
|
||||||
ior: 1.0,
|
ior: 1.0,
|
||||||
|
@ -94,7 +105,7 @@ fn add_default_materials() {
|
||||||
albedo: Padded::from([1.0, 1.0, 1.0]),
|
albedo: Padded::from([1.0, 1.0, 1.0]),
|
||||||
emission: Padded::from([0.0, 0.0, 0.0]),
|
emission: Padded::from([0.0, 0.0, 0.0]),
|
||||||
specular_color: [0.0, 0.0, 0.0],
|
specular_color: [0.0, 0.0, 0.0],
|
||||||
roughness: 1.5,
|
roughness: 0.0,
|
||||||
specular: 0.0,
|
specular: 0.0,
|
||||||
transmission: 1.0,
|
transmission: 1.0,
|
||||||
ior: 1.0,
|
ior: 1.0,
|
||||||
|
@ -105,7 +116,7 @@ fn add_default_materials() {
|
||||||
albedo: Padded::from([1.0, 1.0, 1.0]),
|
albedo: Padded::from([1.0, 1.0, 1.0]),
|
||||||
emission: Padded::from([1.0, 1.0, 1.0]),
|
emission: Padded::from([1.0, 1.0, 1.0]),
|
||||||
specular_color: [0.0, 0.0, 0.0],
|
specular_color: [0.0, 0.0, 0.0],
|
||||||
roughness: 1.5,
|
roughness: 1.0,
|
||||||
specular: 0.0,
|
specular: 0.0,
|
||||||
transmission: 0.0,
|
transmission: 0.0,
|
||||||
ior: 0.0,
|
ior: 0.0,
|
||||||
|
@ -116,7 +127,7 @@ fn add_default_materials() {
|
||||||
albedo: Padded::from([0.3, 0.3, 1.0]),
|
albedo: Padded::from([0.3, 0.3, 1.0]),
|
||||||
emission: Padded::from([0.3, 0.3, 1.0]),
|
emission: Padded::from([0.3, 0.3, 1.0]),
|
||||||
specular_color: [0.0, 0.0, 0.0],
|
specular_color: [0.0, 0.0, 0.0],
|
||||||
roughness: 1.5,
|
roughness: 1.0,
|
||||||
specular: 0.0,
|
specular: 0.0,
|
||||||
transmission: 0.0,
|
transmission: 0.0,
|
||||||
ior: 0.0,
|
ior: 0.0,
|
||||||
|
@ -127,7 +138,7 @@ fn add_default_materials() {
|
||||||
albedo: Padded::from([1.0, 0.3, 0.3]),
|
albedo: Padded::from([1.0, 0.3, 0.3]),
|
||||||
emission: Padded::from([1.0, 0.3, 0.3]),
|
emission: Padded::from([1.0, 0.3, 0.3]),
|
||||||
specular_color: [0.0, 0.0, 0.0],
|
specular_color: [0.0, 0.0, 0.0],
|
||||||
roughness: 1.5,
|
roughness: 1.0,
|
||||||
specular: 0.0,
|
specular: 0.0,
|
||||||
transmission: 0.0,
|
transmission: 0.0,
|
||||||
ior: 0.0,
|
ior: 0.0,
|
||||||
|
@ -138,12 +149,23 @@ fn add_default_materials() {
|
||||||
albedo: Padded::from([0.3, 1.0, 0.3]),
|
albedo: Padded::from([0.3, 1.0, 0.3]),
|
||||||
emission: Padded::from([0.3, 1.0, 0.3]),
|
emission: Padded::from([0.3, 1.0, 0.3]),
|
||||||
specular_color: [0.0, 0.0, 0.0],
|
specular_color: [0.0, 0.0, 0.0],
|
||||||
roughness: 1.5,
|
roughness: 1.0,
|
||||||
specular: 0.0,
|
specular: 0.0,
|
||||||
transmission: 0.0,
|
transmission: 0.0,
|
||||||
ior: 0.0,
|
ior: 0.0,
|
||||||
metallic: false as u32,
|
metallic: false as u32,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
material_collection.insert(String::from("glossy"), cs::Material {
|
||||||
|
albedo: Padded::from([1.0, 1.0, 1.0]),
|
||||||
|
emission: Padded::from([0.0, 0.0, 0.0]),
|
||||||
|
specular_color: [0.0, 0.0, 0.0],
|
||||||
|
roughness: 0.0,
|
||||||
|
specular: 1.0,
|
||||||
|
transmission: 0.0,
|
||||||
|
ior: 0.0,
|
||||||
|
metallic: false as u32,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PathtracerPipeline {
|
pub struct PathtracerPipeline {
|
||||||
|
@ -157,6 +179,7 @@ pub struct PathtracerPipeline {
|
||||||
uniform_buffer: Arc<SubbufferAllocator>,
|
uniform_buffer: Arc<SubbufferAllocator>,
|
||||||
vertex_buffer: Subbuffer<[[f32; 4]]>,
|
vertex_buffer: Subbuffer<[[f32; 4]]>,
|
||||||
index_buffer: Subbuffer<[u32]>,
|
index_buffer: Subbuffer<[u32]>,
|
||||||
|
normal_buffer: Subbuffer<[[f32; 4]]>,
|
||||||
material_buffer: Subbuffer<[cs::Material]>,
|
material_buffer: Subbuffer<[cs::Material]>,
|
||||||
camera: Camera,
|
camera: Camera,
|
||||||
frames: f32
|
frames: f32
|
||||||
|
@ -188,9 +211,9 @@ impl PathtracerPipeline {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let (vertices, indices, materials) = load_example_scene();
|
let (vertices, indices, normals, materials) = load_example_scene();
|
||||||
|
|
||||||
let (vertex_buffer, index_buffer, materials) = create_gpu_buffer(&vertices, &indices, &materials, &renderer.memory_allocator);
|
let (vertex_buffer, index_buffer, normal_buffer, materials) = create_gpu_buffer(&vertices, &indices, &normals, &materials, &renderer.memory_allocator);
|
||||||
|
|
||||||
return PathtracerPipeline {
|
return PathtracerPipeline {
|
||||||
compute_queue: compute_queue.clone(),
|
compute_queue: compute_queue.clone(),
|
||||||
|
@ -202,6 +225,7 @@ impl PathtracerPipeline {
|
||||||
uniform_buffer: Arc::new(uniform_buffer),
|
uniform_buffer: Arc::new(uniform_buffer),
|
||||||
vertex_buffer,
|
vertex_buffer,
|
||||||
index_buffer,
|
index_buffer,
|
||||||
|
normal_buffer,
|
||||||
material_buffer: materials,
|
material_buffer: materials,
|
||||||
seconds: Instant::now(),
|
seconds: Instant::now(),
|
||||||
camera: Camera::new(),
|
camera: Camera::new(),
|
||||||
|
@ -235,6 +259,7 @@ impl PathtracerPipeline {
|
||||||
let image = create_image(&self.memory_allocator, &self.compute_queue, size);
|
let image = create_image(&self.memory_allocator, &self.compute_queue, size);
|
||||||
|
|
||||||
self.image = image;
|
self.image = image;
|
||||||
|
self.frames = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the command for a dispatch.
|
/// Builds the command for a dispatch.
|
||||||
|
@ -268,7 +293,8 @@ impl PathtracerPipeline {
|
||||||
WriteDescriptorSet::buffer(2, subbuffer),
|
WriteDescriptorSet::buffer(2, subbuffer),
|
||||||
WriteDescriptorSet::buffer(3, self.vertex_buffer.clone()),
|
WriteDescriptorSet::buffer(3, self.vertex_buffer.clone()),
|
||||||
WriteDescriptorSet::buffer(4, self.index_buffer.clone()),
|
WriteDescriptorSet::buffer(4, self.index_buffer.clone()),
|
||||||
WriteDescriptorSet::buffer(5, self.material_buffer.clone())
|
WriteDescriptorSet::buffer(5, self.material_buffer.clone()),
|
||||||
|
WriteDescriptorSet::buffer(6, self.normal_buffer.clone())
|
||||||
],
|
],
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
|
@ -301,7 +327,7 @@ fn create_image(memory_allocator: &StandardMemoryAllocator, queue: &Arc<Queue>,
|
||||||
).unwrap()
|
).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_example_scene() -> (Vec<[f32; 4]>, Vec<u32>, Vec<cs::Material>) {
|
fn load_example_scene() -> (Vec<[f32; 4]>, Vec<u32>, Vec<[f32; 4]>, Vec<cs::Material>) {
|
||||||
let (mut models, materials) = tobj::load_obj("res/head.obj", &tobj::GPU_LOAD_OPTIONS).expect("unable to load scene from obj");
|
let (mut models, materials) = tobj::load_obj("res/head.obj", &tobj::GPU_LOAD_OPTIONS).expect("unable to load scene from obj");
|
||||||
|
|
||||||
// allocate some host memory
|
// allocate some host memory
|
||||||
|
@ -309,8 +335,10 @@ fn load_example_scene() -> (Vec<[f32; 4]>, Vec<u32>, Vec<cs::Material>) {
|
||||||
let mut indices:Vec<u32> = vec![];
|
let mut indices:Vec<u32> = vec![];
|
||||||
let mut shader_materials:Vec<cs::Material> = vec![];
|
let mut shader_materials:Vec<cs::Material> = vec![];
|
||||||
|
|
||||||
|
let mut normals:Vec<[f32; 4]> = vec![];
|
||||||
|
|
||||||
for model in models.iter_mut() {
|
for model in models.iter_mut() {
|
||||||
let offset = vertices.len() as u32;
|
let vertex_offset = vertices.len() as u32;
|
||||||
|
|
||||||
let material = model.mesh.material_id.unwrap_or(0);
|
let material = model.mesh.material_id.unwrap_or(0);
|
||||||
|
|
||||||
|
@ -327,20 +355,54 @@ fn load_example_scene() -> (Vec<[f32; 4]>, Vec<u32>, Vec<cs::Material>) {
|
||||||
|
|
||||||
// fill the index buffer
|
// fill the index buffer
|
||||||
for index in model.mesh.indices.iter() {
|
for index in model.mesh.indices.iter() {
|
||||||
indices.push(*index + offset);
|
indices.push(*index + vertex_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill the normal buffer
|
||||||
|
for normal_index in (0..model.mesh.normals.len()).step_by(3) {
|
||||||
|
normals.push([
|
||||||
|
model.mesh.normals[normal_index],
|
||||||
|
model.mesh.normals[normal_index + 1],
|
||||||
|
model.mesh.normals[normal_index + 2],
|
||||||
|
0.0 // padding
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("loaded vertices: {}", vertices.len());
|
||||||
|
println!("loaded indices: {}", indices.len());
|
||||||
|
println!("loaded normals: {}", normals.len());
|
||||||
|
|
||||||
let material_collection = &MATERIAL_COLLECTIO.lock().unwrap();
|
let material_collection = &MATERIAL_COLLECTIO.lock().unwrap();
|
||||||
for material in materials.unwrap().iter() {
|
for material in materials.unwrap().iter() {
|
||||||
shader_materials.push(*material_collection.get(&material.name).unwrap_or(&DEFAULT_MATERIAL));
|
shader_materials.push(*material_collection.get(&material.name).unwrap_or(&DEFAULT_MATERIAL));
|
||||||
}
|
}
|
||||||
|
|
||||||
(vertices, indices, shader_materials)
|
(vertices, indices, normals, shader_materials)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_gpu_buffer(vertices: &Vec<[f32; 4]>, indices: &Vec<u32>, materials: &Vec<cs::Material>, memory_allocator: &StandardMemoryAllocator) -> (Subbuffer<[[f32; 4]]>, Subbuffer<[u32]>, Subbuffer<[cs::Material]>) {
|
fn create_gpu_buffer(vertices: &Vec<[f32; 4]>, indices: &Vec<u32>, normals: &Vec<[f32; 4]>, materials: &Vec<cs::Material>, memory_allocator: &StandardMemoryAllocator) -> (Subbuffer<[[f32; 4]]>, Subbuffer<[u32]>, Subbuffer<[[f32; 4]]>, Subbuffer<[cs::Material]>) {
|
||||||
let vertex_buffer = Buffer::from_iter(
|
let vertex_buffer = create_subbuffer_from_host(memory_allocator, vertices)
|
||||||
|
.expect("Failed to create subbuffer for vertices");
|
||||||
|
|
||||||
|
let index_buffer = create_subbuffer_from_host(memory_allocator, indices)
|
||||||
|
.expect("Failed to create subbuffer for indices");
|
||||||
|
|
||||||
|
let normal_buffer = create_subbuffer_from_host(memory_allocator, normals)
|
||||||
|
.expect("Failed to create subbuffer for vertices");
|
||||||
|
|
||||||
|
let material_buffer = create_subbuffer_from_host(memory_allocator, materials)
|
||||||
|
.expect("Failed to create subbuffer for material");
|
||||||
|
|
||||||
|
(vertex_buffer, index_buffer, normal_buffer, material_buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_subbuffer_from_host<T, I>(memory_allocator: &StandardMemoryAllocator, host_data: &I) -> Result<Subbuffer<[T]>, BufferError> where
|
||||||
|
T: BufferContents,
|
||||||
|
I: IntoIterator<Item = T> + Clone,
|
||||||
|
I::IntoIter: ExactSizeIterator, {
|
||||||
|
|
||||||
|
Buffer::from_iter(
|
||||||
memory_allocator,
|
memory_allocator,
|
||||||
BufferCreateInfo {
|
BufferCreateInfo {
|
||||||
usage: BufferUsage::STORAGE_BUFFER,
|
usage: BufferUsage::STORAGE_BUFFER,
|
||||||
|
@ -350,34 +412,6 @@ fn create_gpu_buffer(vertices: &Vec<[f32; 4]>, indices: &Vec<u32>, materials: &V
|
||||||
usage: MemoryUsage::Upload,
|
usage: MemoryUsage::Upload,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
vertices.clone(),
|
host_data.clone(),
|
||||||
).unwrap();
|
)
|
||||||
|
|
||||||
let index_buffer = Buffer::from_iter(
|
|
||||||
memory_allocator,
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::STORAGE_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
usage: MemoryUsage::Upload,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
indices.clone(),
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
let material_buffer = Buffer::from_iter(
|
|
||||||
memory_allocator,
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::STORAGE_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
usage: MemoryUsage::Upload,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
materials.clone(),
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
(vertex_buffer, index_buffer, material_buffer)
|
|
||||||
}
|
}
|
|
@ -43,6 +43,16 @@ vec3 oren_nayar_diffuse(in vec3 lightDirection, in vec3 viewDirection, in vec3 s
|
||||||
return albedo * max(0.0, NdotL) * (A + B * s / t) / PI;
|
return albedo * max(0.0, NdotL) * (A + B * s / t) / PI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float schlick(in float cosTheta, in float R0) {
|
||||||
|
return R0 + (1.0 - R0) * pow(1.0 - cosTheta, 5.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
float R0(in float ior) {
|
||||||
|
float a = (ior - 1.0);
|
||||||
|
float b = (ior + 1.0);
|
||||||
|
return (a * a) / (b * b);
|
||||||
|
}
|
||||||
|
|
||||||
vec3 trace_direct(in Ray ray) {
|
vec3 trace_direct(in Ray ray) {
|
||||||
vec3 color = vec3(0);
|
vec3 color = vec3(0);
|
||||||
vec3 throughput = vec3(1);
|
vec3 throughput = vec3(1);
|
||||||
|
@ -58,19 +68,44 @@ vec3 trace_direct(in Ray ray) {
|
||||||
|
|
||||||
Material material = materials[hit.material_index];
|
Material material = materials[hit.material_index];
|
||||||
|
|
||||||
color += material.emission * 256.0 * throughput * pdf;
|
color += material.emission * 512.0 * throughput * pdf;
|
||||||
|
|
||||||
float fresnel = abs(dot(ray.direction, hit.normal));
|
float R0 = R0(1.45);
|
||||||
|
float fresnel = schlick(abs(dot(ray.direction, hit.normal)), R0);
|
||||||
|
|
||||||
if (random() * material.specular > fresnel) {
|
ray.origin = ray.origin + ray.direction * hit.depth;
|
||||||
|
|
||||||
|
// metalic
|
||||||
|
if (material.metallic) {
|
||||||
|
// reflection ray
|
||||||
|
ray.direction = generate_brdf_ray_direction(hit.normal, ray.direction, material.roughness);
|
||||||
|
|
||||||
|
throughput *= material.albedo;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// transmission
|
||||||
|
if (material.transmission > random()) {
|
||||||
|
|
||||||
|
if (random() < fresnel) {
|
||||||
|
// reflection ray
|
||||||
|
ray.direction = generate_brdf_ray_direction(hit.normal, ray.direction, material.roughness);
|
||||||
|
} else {
|
||||||
|
// refraction ray
|
||||||
|
ray.direction = generate_btdf_ray_direction(-hit.normal, ray.direction, material.roughness, material.ior);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// dielectric
|
||||||
|
if (random() < material.specular * fresnel) {
|
||||||
// reflection ray
|
// reflection ray
|
||||||
ray.origin = ray.origin + ray.direction * hit.depth;
|
|
||||||
ray.direction = generate_brdf_ray_direction(hit.normal, ray.direction, material.roughness);
|
ray.direction = generate_brdf_ray_direction(hit.normal, ray.direction, material.roughness);
|
||||||
} else {
|
} else {
|
||||||
vec3 incident = ray.direction;
|
vec3 incident = ray.direction;
|
||||||
|
|
||||||
// diffuse ray
|
// diffuse ray
|
||||||
ray.origin = ray.origin + ray.direction * hit.depth;
|
|
||||||
ray.direction = generate_brdf_ray_direction(hit.normal, ray.direction, 1.0);
|
ray.direction = generate_brdf_ray_direction(hit.normal, ray.direction, 1.0);
|
||||||
|
|
||||||
throughput *= oren_nayar_diffuse(ray.direction, incident, hit.normal, material.roughness, material.albedo);
|
throughput *= oren_nayar_diffuse(ray.direction, incident, hit.normal, material.roughness, material.albedo);
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
#ifdef _ONE_AT_A_TIME_
|
|
||||||
|
|
||||||
uint x;
|
|
||||||
|
|
||||||
void init_random_state_one_at_a_time(in float seed) {
|
|
||||||
x = ((gl_GlobalInvocationID.y << 16) | (gl_GlobalInvocationID.x)) + floatBitsToInt(seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// A single iteration of Bob Jenkins' One-At-A-Time hashing algorithm.
|
|
||||||
uint one_at_a_time_hash() {
|
|
||||||
x += (x << 10u);
|
|
||||||
x ^= (x >> 6u);
|
|
||||||
x += (x << 3u);
|
|
||||||
x ^= (x >> 11u);
|
|
||||||
x += (x << 15u);
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define HASH_FUNCTION one_at_a_time_hash
|
|
||||||
#define INIT_STATE_FUNCTION init_random_state_one_at_a_time
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,30 +1,32 @@
|
||||||
#ifndef __RANDOM_GLSL__
|
#ifndef __RANDOM_GLSL__
|
||||||
#define __RANDOM_GLSL__
|
#define __RANDOM_GLSL__
|
||||||
|
|
||||||
#define _ONE_AT_A_TIME_
|
// Gold Noise ©2015 dcerisano@standard3d.com
|
||||||
//#define _XOSHIRO_
|
// - based on the Golden Ratio
|
||||||
|
// - uniform normalized distribution
|
||||||
|
// - fastest static noise generator function (also runs at low precision)
|
||||||
|
// - use with indicated fractional seeding method.
|
||||||
|
|
||||||
#include "one-at-a-time.glsl"
|
const float PHI = 1.61803398874989484820459; // Φ = Golden Ratio
|
||||||
#include "xoshiro.glsl"
|
|
||||||
|
|
||||||
// Construct a float with half-open range [0:1] using low 23 bits.
|
// different for every pixel on the image
|
||||||
// All zeroes yields 0.0, all ones yields the next smallest representable value below 1.0.
|
vec2 xy;
|
||||||
float floatConstruct(in uint m) {
|
// different for every iteration and based on time
|
||||||
const uint ieeeMantissa = 0x007FFFFFu; // binary32 mantissa bitmask
|
float seed;
|
||||||
const uint ieeeOne = 0x3F800000u; // 1.0 in IEEE binary32
|
|
||||||
|
|
||||||
m &= ieeeMantissa; // Keep only mantissa bits (fractional part)
|
// based on https://www.shadertoy.com/view/ltB3zD
|
||||||
m |= ieeeOne; // Add fractional part to 1.0
|
float gold_noise(){
|
||||||
|
return fract(tan(distance(xy * PHI, xy) * seed)*xy.x);
|
||||||
return uintBitsToFloat(m) - 1.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_random_state(in float seed) {
|
void init_random_state(in float time) {
|
||||||
INIT_STATE_FUNCTION(seed);
|
xy = vec2(gl_GlobalInvocationID.xy);
|
||||||
|
seed = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
float random() {
|
float random() {
|
||||||
return floatConstruct(HASH_FUNCTION());
|
seed += 0.1;
|
||||||
|
return gold_noise();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
|
|
||||||
#ifdef _XOSHIRO_
|
|
||||||
|
|
||||||
uint rol(in uint x, in uint k) {
|
|
||||||
return (x << k) | (x >> (32 - k));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint XoshiroState[4];
|
|
||||||
|
|
||||||
void init_random_state_xhoshiro(in float seed) {
|
|
||||||
uint utime = floatBitsToUint(seed);
|
|
||||||
XoshiroState[0] = ((gl_GlobalInvocationID.x << 16) | gl_GlobalInvocationID.y) + utime;
|
|
||||||
XoshiroState[1] = XoshiroState[0] ^ utime;
|
|
||||||
XoshiroState[2] = 0x92abc32;
|
|
||||||
XoshiroState[3] = rol(utime, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint xoshiro_hash() {
|
|
||||||
uint result = rol(XoshiroState[1] * 5, 7) * 9;
|
|
||||||
uint t = XoshiroState[1] << 8;
|
|
||||||
|
|
||||||
XoshiroState[2] ^= XoshiroState[0];
|
|
||||||
XoshiroState[3] ^= XoshiroState[1];
|
|
||||||
XoshiroState[1] ^= XoshiroState[2];
|
|
||||||
XoshiroState[0] ^= XoshiroState[3];
|
|
||||||
|
|
||||||
XoshiroState[2] ^= t;
|
|
||||||
XoshiroState[3] = rol(XoshiroState[3], 22);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define HASH_FUNCTION xoshiro_hash
|
|
||||||
#define INIT_STATE_FUNCTION init_random_state_xhoshiro
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -41,9 +41,20 @@ vec3 cosine_weighted_hemisphere(in float radius) {
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 generate_brdf_ray_direction(in vec3 normal, in vec3 incident, in float roughness) {
|
vec3 generate_brdf_ray_direction(in vec3 normal, in vec3 incident, in float roughness) {
|
||||||
vec3 merged_normal = mix(normal, reflect(incident, normal), 1.0 - roughness);
|
vec3 merged_normal = mix(reflect(incident, normal), normal, roughness * roughness);
|
||||||
|
|
||||||
vec3 hemisphere = cosine_weighted_hemisphere(roughness);
|
vec3 hemisphere = cosine_weighted_hemisphere(roughness * roughness);
|
||||||
|
|
||||||
|
vec3 u, v, w;
|
||||||
|
construct_orthonormal_basis(merged_normal, u, v, w);
|
||||||
|
|
||||||
|
return u * hemisphere.x + v * hemisphere.y + w * hemisphere.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 generate_btdf_ray_direction(in vec3 normal, in vec3 incident, in float roughness, in float ior) {
|
||||||
|
vec3 merged_normal = mix(refract(incident, normal, ior), normal, roughness * roughness);
|
||||||
|
|
||||||
|
vec3 hemisphere = cosine_weighted_hemisphere(roughness * roughness);
|
||||||
|
|
||||||
vec3 u, v, w;
|
vec3 u, v, w;
|
||||||
construct_orthonormal_basis(merged_normal, u, v, w);
|
construct_orthonormal_basis(merged_normal, u, v, w);
|
||||||
|
|
|
@ -39,18 +39,22 @@ struct Material {
|
||||||
bool metallic;
|
bool metallic;
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(set = 0, binding = 3) buffer VertexBuffer {
|
layout(set = 0, binding = 3) readonly buffer VertexBuffer {
|
||||||
vec4 vertices[];
|
vec4 vertices[];
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(set = 0, binding = 4) buffer IndexBuffer {
|
layout(set = 0, binding = 4) readonly buffer IndexBuffer {
|
||||||
uint indices[];
|
uint indices[];
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(set = 0, binding = 5) buffer MaterialBuffer {
|
layout(set = 0, binding = 5) readonly buffer MaterialBuffer {
|
||||||
Material materials[];
|
Material materials[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
layout(set = 0, binding = 6) readonly buffer NormalBuffer {
|
||||||
|
vec4 normals[];
|
||||||
|
};
|
||||||
|
|
||||||
// from: https://iquilezles.org/articles/intersectors/ with a view modifications
|
// from: https://iquilezles.org/articles/intersectors/ with a view modifications
|
||||||
vec3 intersect_triangle(in Ray ray, in vec3 v0, in vec3 v1, in vec3 v2, out vec3 n) {
|
vec3 intersect_triangle(in Ray ray, in vec3 v0, in vec3 v1, in vec3 v2, out vec3 n) {
|
||||||
// triangle edges
|
// triangle edges
|
||||||
|
@ -83,9 +87,13 @@ Hit intersect_scene(in Ray ray) {
|
||||||
hit.intersected = false;
|
hit.intersected = false;
|
||||||
|
|
||||||
for (int i = 0; i < indices.length(); i += 3) {
|
for (int i = 0; i < indices.length(); i += 3) {
|
||||||
vec3 v0 = vertices[indices[i]].xyz;
|
uint index_0 = indices[i];
|
||||||
vec3 v1 = vertices[indices[i + 1]].xyz;
|
uint index_1 = indices[i + 1];
|
||||||
vec3 v2 = vertices[indices[i + 2]].xyz;
|
uint index_2 = indices[i + 2];
|
||||||
|
|
||||||
|
vec3 v0 = vertices[index_0].xyz;
|
||||||
|
vec3 v1 = vertices[index_1].xyz;
|
||||||
|
vec3 v2 = vertices[index_2].xyz;
|
||||||
|
|
||||||
vec3 normal;
|
vec3 normal;
|
||||||
vec3 result = intersect_triangle(ray, v0, v1, v2, normal);
|
vec3 result = intersect_triangle(ray, v0, v1, v2, normal);
|
||||||
|
@ -93,7 +101,12 @@ Hit intersect_scene(in Ray ray) {
|
||||||
if (result.x > ray.near && result.x < hit.depth) {
|
if (result.x > ray.near && result.x < hit.depth) {
|
||||||
hit.barycentric = result.yz;
|
hit.barycentric = result.yz;
|
||||||
hit.depth = result.x;
|
hit.depth = result.x;
|
||||||
hit.normal = normalize(normal);
|
|
||||||
|
vec2 smoother_barycentric = smoothstep(vec2(0.0), vec2(1.0), result.yz);
|
||||||
|
|
||||||
|
// gouraud shading: interpolate between vertex normals with barycentric coordinates
|
||||||
|
hit.normal = mix(mix(normals[index_0], normals[index_1], smoother_barycentric.x), normals[index_2], smoother_barycentric.y).xyz;
|
||||||
|
// flat shading only: use raw triangle normals: hit.normal = normalize(normal);
|
||||||
hit.material_index = uint(vertices[indices[i]].a);
|
hit.material_index = uint(vertices[indices[i]].a);
|
||||||
hit.intersected = true;
|
hit.intersected = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ mod device;
|
||||||
pub(crate) mod textured_quad;
|
pub(crate) mod textured_quad;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use vulkano::device::{Device};
|
use vulkano::device::{Device};
|
||||||
use vulkano::image::{ImageAccess, ImageUsage, SwapchainImage};
|
use vulkano::image::{ImageAccess, ImageUsage, SwapchainImage};
|
||||||
|
@ -19,6 +20,7 @@ use vulkano::pipeline::graphics::viewport::Viewport;
|
||||||
use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass};
|
use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass};
|
||||||
use vulkano::sync::{FlushError, GpuFuture};
|
use vulkano::sync::{FlushError, GpuFuture};
|
||||||
use winit::event::{Event, VirtualKeyCode, WindowEvent};
|
use winit::event::{Event, VirtualKeyCode, WindowEvent};
|
||||||
|
use winit::platform::run_return::EventLoopExtRunReturn;
|
||||||
use crate::shader::composite::TextureDrawPipeline;
|
use crate::shader::composite::TextureDrawPipeline;
|
||||||
use crate::shader::pathtracing::PathtracerPipeline;
|
use crate::shader::pathtracing::PathtracerPipeline;
|
||||||
|
|
||||||
|
@ -55,7 +57,7 @@ pub fn init() {
|
||||||
//
|
//
|
||||||
// This returns a `vulkano::swapchain::Surface` object that contains both a cross-platform
|
// This returns a `vulkano::swapchain::Surface` object that contains both a cross-platform
|
||||||
// winit window and a cross-platform Vulkan surface that represents the surface of the window.
|
// winit window and a cross-platform Vulkan surface that represents the surface of the window.
|
||||||
let event_loop = EventLoop::new();
|
let mut event_loop = EventLoop::new();
|
||||||
let surface = WindowBuilder::new()
|
let surface = WindowBuilder::new()
|
||||||
.build_vk_surface(&event_loop, instance.clone())
|
.build_vk_surface(&event_loop, instance.clone())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -108,7 +110,7 @@ pub fn init() {
|
||||||
//
|
//
|
||||||
// Since we need to draw to multiple images, we are going to create a different framebuffer for
|
// Since we need to draw to multiple images, we are going to create a different framebuffer for
|
||||||
// each image.
|
// each image.
|
||||||
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport, &mut pathtracer);
|
let mut framebuffers = window_size_dependent_setup(&images, &render_pass, &mut viewport, &mut pathtracer);
|
||||||
|
|
||||||
// Initialization is finally finished!
|
// Initialization is finally finished!
|
||||||
|
|
||||||
|
@ -133,173 +135,161 @@ pub fn init() {
|
||||||
|
|
||||||
let texture_drawer = TextureDrawPipeline::new(&renderer, &queue, &render_pass);
|
let texture_drawer = TextureDrawPipeline::new(&renderer, &queue, &render_pass);
|
||||||
|
|
||||||
let mut now_keys = [false; 255];
|
|
||||||
|
|
||||||
let mut start_frame = Instant::now();
|
let mut start_frame = Instant::now();
|
||||||
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
loop {
|
||||||
match event {
|
let (running, resized) = handle_events(&mut event_loop);
|
||||||
Event::WindowEvent {
|
|
||||||
event: WindowEvent::CloseRequested,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
*control_flow = ControlFlow::Exit;
|
|
||||||
}
|
|
||||||
Event::WindowEvent {
|
|
||||||
event: WindowEvent::Resized(_),
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
recreate_swapchain = true;
|
|
||||||
},
|
|
||||||
Event::WindowEvent {
|
|
||||||
// Note this deeply nested pattern match
|
|
||||||
event: WindowEvent::KeyboardInput {
|
|
||||||
input:winit::event::KeyboardInput {
|
|
||||||
// Which serves to filter out only events we actually want
|
|
||||||
virtual_keycode:Some(keycode),
|
|
||||||
state,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
..
|
|
||||||
},
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
// It also binds these handy variable names!
|
|
||||||
match state {
|
|
||||||
winit::event::ElementState::Pressed => {
|
|
||||||
// VirtualKeycode is an enum with a defined representation
|
|
||||||
now_keys[keycode as usize] = true;
|
|
||||||
},
|
|
||||||
winit::event::ElementState::Released => {
|
|
||||||
now_keys[keycode as usize] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Event::RedrawEventsCleared => {
|
|
||||||
let elapsed = (Instant::now() - start_frame).as_secs_f32();
|
|
||||||
|
|
||||||
// Do not draw the frame when the screen dimensions are zero. On Windows, this can
|
if !running {
|
||||||
// occur when minimizing the application.
|
break;
|
||||||
let window = surface.object().unwrap().downcast_ref::<Window>().unwrap();
|
}
|
||||||
let dimensions = window.inner_size();
|
|
||||||
if dimensions.width == 0 || dimensions.height == 0 {
|
if resized || recreate_swapchain {
|
||||||
|
let (new_swapchain, new_images) =
|
||||||
|
match swapchain.recreate(SwapchainCreateInfo {
|
||||||
|
//image_extent: dimensions.into(),
|
||||||
|
..swapchain.create_info()
|
||||||
|
}) {
|
||||||
|
Ok(r) => r,
|
||||||
|
// This error tends to happen when the user is manually resizing the
|
||||||
|
// window. Simply restarting the loop is the easiest way to fix this
|
||||||
|
// issue.
|
||||||
|
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
|
||||||
|
Err(e) => panic!("failed to recreate swapchain: {}", e),
|
||||||
|
};
|
||||||
|
|
||||||
|
swapchain = new_swapchain;
|
||||||
|
|
||||||
|
// Because framebuffers contains a reference to the old swapchain, we need to
|
||||||
|
// recreate framebuffers as well.
|
||||||
|
framebuffers = window_size_dependent_setup(
|
||||||
|
&new_images,
|
||||||
|
&render_pass,
|
||||||
|
&mut viewport,
|
||||||
|
&mut pathtracer
|
||||||
|
);
|
||||||
|
recreate_swapchain = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let elapsed = (Instant::now() - start_frame).as_secs_f32();
|
||||||
|
|
||||||
|
// Do not draw the frame when the screen dimensions are zero. On Windows, this can
|
||||||
|
// occur when minimizing the application.
|
||||||
|
let window = surface.object().unwrap().downcast_ref::<Window>().unwrap();
|
||||||
|
let dimensions = window.inner_size();
|
||||||
|
if dimensions.width == 0 || dimensions.height == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It is important to call this function from time to time, otherwise resources
|
||||||
|
// will keep accumulating and you will eventually reach an out of memory error.
|
||||||
|
// Calling this function polls various fences in order to determine what the GPU
|
||||||
|
// has already processed, and frees the resources that are no longer needed.
|
||||||
|
previous_frame_end.as_mut().unwrap().cleanup_finished();
|
||||||
|
|
||||||
|
// Before we can draw on the output, we have to *acquire* an image from the
|
||||||
|
// swapchain. If no image is available (which happens if you submit draw commands
|
||||||
|
// too quickly), then the function will block. This operation returns the index of
|
||||||
|
// the image that we are allowed to draw upon.
|
||||||
|
//
|
||||||
|
// This function can block if no image is available. The parameter is an optional
|
||||||
|
// timeout after which the function call will return an error.
|
||||||
|
let (image_index, suboptimal, acquire_future) =
|
||||||
|
match acquire_next_image(swapchain.clone(), None) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(AcquireError::OutOfDate) => {
|
||||||
|
recreate_swapchain = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Err(e) => panic!("failed to acquire next image: {}", e),
|
||||||
|
};
|
||||||
|
|
||||||
// It is important to call this function from time to time, otherwise resources
|
// `acquire_next_image` can be successful, but suboptimal. This means that the
|
||||||
// will keep accumulating and you will eventually reach an out of memory error.
|
// swapchain image will still work, but it may not display correctly. With some
|
||||||
// Calling this function polls various fences in order to determine what the GPU
|
// drivers this can be when the window resizes, but it may not cause the swapchain
|
||||||
// has already processed, and frees the resources that are no longer needed.
|
// to become out of date.
|
||||||
previous_frame_end.as_mut().unwrap().cleanup_finished();
|
if suboptimal {
|
||||||
|
recreate_swapchain = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Whenever the window resizes we need to recreate everything dependent on the
|
let future = pathtracer.compute();
|
||||||
// window size. In this example that includes the swapchain, the framebuffers and
|
|
||||||
// the dynamic state viewport.
|
|
||||||
if recreate_swapchain {
|
|
||||||
// Use the new dimensions of the window.
|
|
||||||
|
|
||||||
let (new_swapchain, new_images) =
|
let command_buffer = texture_drawer.draw(framebuffers[image_index as usize].clone(), &viewport, &pathtracer);
|
||||||
match swapchain.recreate(SwapchainCreateInfo {
|
|
||||||
image_extent: dimensions.into(),
|
|
||||||
..swapchain.create_info()
|
|
||||||
}) {
|
|
||||||
Ok(r) => r,
|
|
||||||
// This error tends to happen when the user is manually resizing the
|
|
||||||
// window. Simply restarting the loop is the easiest way to fix this
|
|
||||||
// issue.
|
|
||||||
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return,
|
|
||||||
Err(e) => panic!("failed to recreate swapchain: {}", e),
|
|
||||||
};
|
|
||||||
|
|
||||||
swapchain = new_swapchain;
|
let future = previous_frame_end
|
||||||
|
.take()
|
||||||
|
.unwrap()
|
||||||
|
.join(acquire_future)
|
||||||
|
.join(future)
|
||||||
|
.then_execute(queue.clone(), command_buffer)
|
||||||
|
.unwrap()
|
||||||
|
// The color output is now expected to contain our triangle. But in order to
|
||||||
|
// show it on the screen, we have to *present* the image by calling
|
||||||
|
// `then_swapchain_present`.
|
||||||
|
//
|
||||||
|
// This function does not actually present the image immediately. Instead it
|
||||||
|
// submits a present command at the end of the queue. This means that it will
|
||||||
|
// only be presented once the GPU has finished executing the command buffer
|
||||||
|
// that draws the triangle.
|
||||||
|
.then_swapchain_present(
|
||||||
|
queue.clone(),
|
||||||
|
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index),
|
||||||
|
)
|
||||||
|
.then_signal_fence_and_flush();
|
||||||
|
|
||||||
// Because framebuffers contains a reference to the old swapchain, we need to
|
match future {
|
||||||
// recreate framebuffers as well.
|
Ok(future) => {
|
||||||
framebuffers = window_size_dependent_setup(
|
previous_frame_end = Some(future.boxed());
|
||||||
&new_images,
|
|
||||||
render_pass.clone(),
|
|
||||||
&mut viewport,
|
|
||||||
&mut pathtracer
|
|
||||||
);
|
|
||||||
|
|
||||||
recreate_swapchain = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Before we can draw on the output, we have to *acquire* an image from the
|
|
||||||
// swapchain. If no image is available (which happens if you submit draw commands
|
|
||||||
// too quickly), then the function will block. This operation returns the index of
|
|
||||||
// the image that we are allowed to draw upon.
|
|
||||||
//
|
|
||||||
// This function can block if no image is available. The parameter is an optional
|
|
||||||
// timeout after which the function call will return an error.
|
|
||||||
let (image_index, suboptimal, acquire_future) =
|
|
||||||
match acquire_next_image(swapchain.clone(), None) {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(AcquireError::OutOfDate) => {
|
|
||||||
recreate_swapchain = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Err(e) => panic!("failed to acquire next image: {}", e),
|
|
||||||
};
|
|
||||||
|
|
||||||
// `acquire_next_image` can be successful, but suboptimal. This means that the
|
|
||||||
// swapchain image will still work, but it may not display correctly. With some
|
|
||||||
// drivers this can be when the window resizes, but it may not cause the swapchain
|
|
||||||
// to become out of date.
|
|
||||||
if suboptimal {
|
|
||||||
recreate_swapchain = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let future = pathtracer.compute();
|
|
||||||
|
|
||||||
let command_buffer = texture_drawer.draw(framebuffers[image_index as usize].clone(), &viewport, &pathtracer);
|
|
||||||
|
|
||||||
let future = previous_frame_end
|
|
||||||
.take()
|
|
||||||
.unwrap()
|
|
||||||
.join(acquire_future)
|
|
||||||
.join(future)
|
|
||||||
.then_execute(queue.clone(), command_buffer)
|
|
||||||
.unwrap()
|
|
||||||
// The color output is now expected to contain our triangle. But in order to
|
|
||||||
// show it on the screen, we have to *present* the image by calling
|
|
||||||
// `then_swapchain_present`.
|
|
||||||
//
|
|
||||||
// This function does not actually present the image immediately. Instead it
|
|
||||||
// submits a present command at the end of the queue. This means that it will
|
|
||||||
// only be presented once the GPU has finished executing the command buffer
|
|
||||||
// that draws the triangle.
|
|
||||||
.then_swapchain_present(
|
|
||||||
queue.clone(),
|
|
||||||
SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index),
|
|
||||||
)
|
|
||||||
.then_signal_fence_and_flush();
|
|
||||||
|
|
||||||
match future {
|
|
||||||
Ok(future) => {
|
|
||||||
previous_frame_end = Some(future.boxed());
|
|
||||||
}
|
|
||||||
Err(FlushError::OutOfDate) => {
|
|
||||||
recreate_swapchain = true;
|
|
||||||
previous_frame_end = Some(sync::now(device.clone()).boxed());
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
panic!("failed to flush future: {}", e);
|
|
||||||
// previous_frame_end = Some(sync::now(device.clone()).boxed());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
start_frame = Instant::now();
|
|
||||||
}
|
}
|
||||||
|
Err(FlushError::OutOfDate) => {
|
||||||
|
recreate_swapchain = true;
|
||||||
|
previous_frame_end = Some(sync::now(device.clone()).boxed());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
panic!("failed to flush future: {}", e);
|
||||||
|
// previous_frame_end = Some(sync::now(device.clone()).boxed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start_frame = Instant::now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Handles events and returns a `bool` indicating if we should quit.
|
||||||
|
fn handle_events(
|
||||||
|
event_loop: &mut EventLoop<()>,
|
||||||
|
) -> (bool, bool) {
|
||||||
|
let mut is_running = true;
|
||||||
|
let mut resize = false;
|
||||||
|
|
||||||
|
event_loop.run_return(|event, _, control_flow| {
|
||||||
|
*control_flow = ControlFlow::Wait;
|
||||||
|
|
||||||
|
match &event {
|
||||||
|
Event::WindowEvent { event, .. } => match event {
|
||||||
|
WindowEvent::CloseRequested => is_running = false,
|
||||||
|
WindowEvent::Resized(_) | WindowEvent::ScaleFactorChanged { .. } => {
|
||||||
|
resize = true;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
Event::MainEventsCleared => *control_flow = ControlFlow::Exit,
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pass event for the app to handle our inputs.
|
||||||
|
// app.handle_input(renderer.window_size(), &event);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
(is_running, resize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// This function is called once during initialization, then again whenever the window is resized.
|
/// This function is called once during initialization, then again whenever the window is resized.
|
||||||
fn window_size_dependent_setup(
|
fn window_size_dependent_setup(
|
||||||
images: &[Arc<SwapchainImage>],
|
images: &[Arc<SwapchainImage>],
|
||||||
render_pass: Arc<RenderPass>,
|
render_pass: &Arc<RenderPass>,
|
||||||
viewport: &mut Viewport,
|
viewport: &mut Viewport,
|
||||||
pathtracer: &mut PathtracerPipeline,
|
pathtracer: &mut PathtracerPipeline,
|
||||||
) -> Vec<Arc<Framebuffer>> {
|
) -> Vec<Arc<Framebuffer>> {
|
||||||
|
|
Reference in New Issue