finished diffuse pathtracing
This commit is contained in:
parent
3ffffb9bf5
commit
b911f89e86
|
@ -310,6 +310,7 @@ name = "eruption"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cgmath",
|
"cgmath",
|
||||||
|
"lazy_static",
|
||||||
"tobj",
|
"tobj",
|
||||||
"vulkano",
|
"vulkano",
|
||||||
"vulkano-shaders",
|
"vulkano-shaders",
|
||||||
|
|
|
@ -14,4 +14,6 @@ winit = "0.28.3"
|
||||||
|
|
||||||
tobj = "3.2.5"
|
tobj = "3.2.5"
|
||||||
|
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
|
||||||
cgmath = "0.18.0"
|
cgmath = "0.18.0"
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
newmtl glass
|
newmtl glass
|
||||||
Ns 1000.000000
|
Ns 1000.000000
|
||||||
Ka 1.000000 1.000000 1.000000
|
Ka 0.000000 0.000000 0.000000
|
||||||
Kd 0.800000 0.800000 0.800000
|
Kd 0.800000 0.800000 0.800000
|
||||||
Ks 0.500000 0.500000 0.500000
|
Ks 0.500000 0.500000 0.500000
|
||||||
Ke 0.000000 0.000000 0.000000
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
@ -13,7 +13,7 @@ illum 2
|
||||||
|
|
||||||
newmtl green
|
newmtl green
|
||||||
Ns 250.000000
|
Ns 250.000000
|
||||||
Ka 1.000000 1.000000 1.000000
|
Ka 0.000000 0.000000 0.000000
|
||||||
Kd 0.006232 0.800000 0.014503
|
Kd 0.006232 0.800000 0.014503
|
||||||
Ks 0.500000 0.500000 0.500000
|
Ks 0.500000 0.500000 0.500000
|
||||||
Ke 0.000000 0.000000 0.000000
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
@ -33,7 +33,7 @@ illum 2
|
||||||
|
|
||||||
newmtl red
|
newmtl red
|
||||||
Ns 250.000000
|
Ns 250.000000
|
||||||
Ka 1.000000 1.000000 1.000000
|
Ka 0.000000 0.000000 0.000000
|
||||||
Kd 0.800000 0.006232 0.009300
|
Kd 0.800000 0.006232 0.009300
|
||||||
Ks 0.500000 0.500000 0.500000
|
Ks 0.500000 0.500000 0.500000
|
||||||
Ke 0.000000 0.000000 0.000000
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
@ -43,7 +43,7 @@ illum 2
|
||||||
|
|
||||||
newmtl white
|
newmtl white
|
||||||
Ns 250.000000
|
Ns 250.000000
|
||||||
Ka 1.000000 1.000000 1.000000
|
Ka 0.000000 0.000000 0.000000
|
||||||
Kd 0.800000 0.800000 0.800000
|
Kd 0.800000 0.800000 0.800000
|
||||||
Ks 0.500000 0.500000 0.500000
|
Ks 0.500000 0.500000 0.500000
|
||||||
Ke 0.000000 0.000000 0.000000
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
# Blender 3.5.0 MTL File: 'None'
|
||||||
|
# www.blender.org
|
||||||
|
|
||||||
|
newmtl light_blue
|
||||||
|
Ns 250.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 light_green
|
||||||
|
Ns 250.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 light_red
|
||||||
|
Ns 250.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 white
|
||||||
|
Ns 250.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
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,60 @@
|
||||||
|
|
||||||
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
// Copyright (c) 2018-2019 Michele Morrone
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// https://michelemorrone.eu - https://BrutPitt.com
|
||||||
|
//
|
||||||
|
// me@michelemorrone.eu - brutpitt@gmail.com
|
||||||
|
// twitter: @BrutPitt - github: BrutPitt
|
||||||
|
//
|
||||||
|
// https://github.com/BrutPitt/glslSmartDeNoise/
|
||||||
|
//
|
||||||
|
// This software is distributed under the terms of the BSD 2-Clause license
|
||||||
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
#define INV_SQRT_OF_2PI 0.39894228040143267793994605993439 // 1.0/SQRT_OF_2PI
|
||||||
|
#define INV_PI 0.31830988618379067153776752674503
|
||||||
|
|
||||||
|
// smartDeNoise - parameters
|
||||||
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// sampler2D tex - sampler image / texture
|
||||||
|
// vec2 uv - actual fragment coord
|
||||||
|
// float sigma > 0 - sigma Standard Deviation
|
||||||
|
// float kSigma >= 0 - sigma coefficient
|
||||||
|
// kSigma * sigma --> radius of the circular kernel
|
||||||
|
// float threshold - edge sharpening threshold
|
||||||
|
vec4 smart_de_noise(in sampler2D tex, vec2 uv, float sigma, float kSigma, float threshold)
|
||||||
|
{
|
||||||
|
float radius = round(kSigma*sigma);
|
||||||
|
float radQ = radius * radius;
|
||||||
|
|
||||||
|
float invSigmaQx2 = .5 / (sigma * sigma); // 1.0 / (sigma^2 * 2.0)
|
||||||
|
float invSigmaQx2PI = INV_PI * invSigmaQx2; // 1/(2 * PI * sigma^2)
|
||||||
|
|
||||||
|
float invThresholdSqx2 = .5 / (threshold * threshold); // 1.0 / (sigma^2 * 2.0)
|
||||||
|
float invThresholdSqrt2PI = INV_SQRT_OF_2PI / threshold; // 1.0 / (sqrt(2*PI) * sigma^2)
|
||||||
|
|
||||||
|
vec4 centrPx = texture(tex,uv);
|
||||||
|
|
||||||
|
float zBuff = 0.0;
|
||||||
|
vec4 aBuff = vec4(0.0);
|
||||||
|
vec2 size = vec2(textureSize(tex, 0));
|
||||||
|
|
||||||
|
vec2 d;
|
||||||
|
for (d.x=-radius; d.x <= radius; d.x++) {
|
||||||
|
float pt = sqrt(radQ-d.x*d.x); // pt = yRadius: have circular trend
|
||||||
|
for (d.y=-pt; d.y <= pt; d.y++) {
|
||||||
|
float blurFactor = exp( -dot(d , d) * invSigmaQx2 ) * invSigmaQx2PI;
|
||||||
|
|
||||||
|
vec4 walkPx = texture(tex,uv+d/size);
|
||||||
|
vec4 dC = walkPx-centrPx;
|
||||||
|
float deltaFactor = exp( -dot(dC, dC) * invThresholdSqx2) * invThresholdSqrt2PI * blurFactor;
|
||||||
|
|
||||||
|
zBuff += deltaFactor;
|
||||||
|
aBuff += deltaFactor*walkPx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return aBuff/zBuff;
|
||||||
|
}
|
|
@ -1,20 +1,32 @@
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(location = 0) in vec2 texture_coordinate;
|
#include "denoise.glsl"
|
||||||
|
|
||||||
|
layout(location = 0) noperspective in vec2 texture_coordinate;
|
||||||
|
|
||||||
layout(set = 0, binding = 0) uniform sampler2D image;
|
layout(set = 0, binding = 0) uniform sampler2D image;
|
||||||
|
|
||||||
layout(location = 0) out vec4 frag_color;
|
layout(location = 0) out vec4 frag_color;
|
||||||
|
|
||||||
|
float luminance(in vec3 color) {
|
||||||
|
return dot(color, vec3(0.2126f, 0.7152f, 0.0722f));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 reinhard_jodie(in vec3 v) {
|
||||||
|
float l = luminance(v);
|
||||||
|
vec3 tv = v / (1.0f + v);
|
||||||
|
return mix(v / (1.0f + l), tv, tv);
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
|
||||||
vec2 uv = texture_coordinate;
|
vec2 uv = texture_coordinate;
|
||||||
|
|
||||||
vec3 color = texture(image, uv).rgb;
|
vec3 color = texture(image, uv).rgb;
|
||||||
|
|
||||||
// TODO: tonemapping
|
color = smart_de_noise(image, uv, 5.0, 1.0, 0.400).rgb;
|
||||||
// TODO: denoising
|
|
||||||
// TODO: bloom
|
color = reinhard_jodie(color);
|
||||||
|
|
||||||
frag_color = vec4(color, 1.0);
|
frag_color = vec4(color, 1.0);
|
||||||
}
|
}
|
|
@ -1,11 +1,16 @@
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
|
// vertex position
|
||||||
layout(location = 0) in vec2 position;
|
layout(location = 0) in vec2 position;
|
||||||
|
// vertex texture coordinate
|
||||||
layout(location = 1) in vec2 texture;
|
layout(location = 1) in vec2 texture;
|
||||||
|
|
||||||
layout(location = 0) out vec2 texture_coordinate;
|
// no perspective required for filling the screen with a quad
|
||||||
|
layout(location = 0) noperspective out vec2 texture_coordinate;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
// pass texture coordinates straight to fragment shader
|
||||||
texture_coordinate = texture;
|
texture_coordinate = texture;
|
||||||
|
// no perspective transform required
|
||||||
gl_Position = vec4(position, 0.0, 1.0);
|
gl_Position = vec4(position, 0.0, 1.0);
|
||||||
}
|
}
|
|
@ -17,7 +17,7 @@ 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, 1.0, 9.0),
|
||||||
fov: 90.0f32
|
fov: 90.0f32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
mod camera;
|
mod camera;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer};
|
use vulkano::buffer::{Buffer, BufferCreateInfo, 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;
|
||||||
|
@ -27,6 +29,112 @@ pub(crate) mod cs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref MATERIAL_COLLECTIO: Mutex<HashMap<String, cs::Material>> = Mutex::new(HashMap::new());
|
||||||
|
static ref DEFAULT_MATERIAL: cs::Material = cs::Material {
|
||||||
|
albedo: 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],
|
||||||
|
roughness: 1.0,
|
||||||
|
transmission: 0.0,
|
||||||
|
ior: 1.0,
|
||||||
|
metallic: false as u32,
|
||||||
|
__padding: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_default_materials() {
|
||||||
|
let mut material_collection = MATERIAL_COLLECTIO.lock().unwrap();
|
||||||
|
|
||||||
|
material_collection.insert(String::from("white"), 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: 1.0,
|
||||||
|
transmission: 0.0,
|
||||||
|
ior: 1.0,
|
||||||
|
metallic: false as u32,
|
||||||
|
__padding: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
material_collection.insert(String::from("red"), cs::Material {
|
||||||
|
albedo: Padded::from([1.0, 0.0, 0.0]),
|
||||||
|
emission: Padded::from([0.0, 0.0, 0.0]),
|
||||||
|
specular_color: [0.0, 0.0, 0.0],
|
||||||
|
roughness: 1.0,
|
||||||
|
transmission: 0.0,
|
||||||
|
ior: 1.0,
|
||||||
|
metallic: false as u32,
|
||||||
|
__padding: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
material_collection.insert(String::from("green"), cs::Material {
|
||||||
|
albedo: Padded::from([0.0, 1.0, 0.0]),
|
||||||
|
emission: Padded::from([0.0, 0.0, 0.0]),
|
||||||
|
specular_color: [0.0, 0.0, 0.0],
|
||||||
|
roughness: 1.0,
|
||||||
|
transmission: 0.0,
|
||||||
|
ior: 1.0,
|
||||||
|
metallic: false as u32,
|
||||||
|
__padding: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
material_collection.insert(String::from("glass"), 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: 1.5,
|
||||||
|
transmission: 1.0,
|
||||||
|
ior: 1.0,
|
||||||
|
metallic: false as u32,
|
||||||
|
__padding: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
material_collection.insert(String::from("light"), cs::Material {
|
||||||
|
albedo: 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],
|
||||||
|
roughness: 1.5,
|
||||||
|
transmission: 0.0,
|
||||||
|
ior: 0.0,
|
||||||
|
metallic: false as u32,
|
||||||
|
__padding: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
material_collection.insert(String::from("light_blue"), cs::Material {
|
||||||
|
albedo: 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],
|
||||||
|
roughness: 1.5,
|
||||||
|
transmission: 0.0,
|
||||||
|
ior: 0.0,
|
||||||
|
metallic: false as u32,
|
||||||
|
__padding: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
material_collection.insert(String::from("light_red"), cs::Material {
|
||||||
|
albedo: 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],
|
||||||
|
roughness: 1.5,
|
||||||
|
transmission: 0.0,
|
||||||
|
ior: 0.0,
|
||||||
|
metallic: false as u32,
|
||||||
|
__padding: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
material_collection.insert(String::from("light_green"), cs::Material {
|
||||||
|
albedo: 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],
|
||||||
|
roughness: 1.5,
|
||||||
|
transmission: 0.0,
|
||||||
|
ior: 0.0,
|
||||||
|
metallic: false as u32,
|
||||||
|
__padding: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub struct PathtracerPipeline {
|
pub struct PathtracerPipeline {
|
||||||
compute_queue: Arc<Queue>,
|
compute_queue: Arc<Queue>,
|
||||||
compute_pipeline: Arc<ComputePipeline>,
|
compute_pipeline: Arc<ComputePipeline>,
|
||||||
|
@ -34,16 +142,19 @@ pub struct PathtracerPipeline {
|
||||||
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
||||||
memory_allocator: Arc<StandardMemoryAllocator>,
|
memory_allocator: Arc<StandardMemoryAllocator>,
|
||||||
image: Arc<ImageView<StorageImage>>,
|
image: Arc<ImageView<StorageImage>>,
|
||||||
raw_image: Subbuffer<[[f32; 4]]>,
|
|
||||||
seconds: Instant,
|
seconds: Instant,
|
||||||
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]>,
|
||||||
camera: Camera
|
material_buffer: Subbuffer<[cs::Material]>,
|
||||||
|
camera: Camera,
|
||||||
|
frames: f32
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PathtracerPipeline {
|
impl PathtracerPipeline {
|
||||||
pub fn new(renderer: &Renderer, compute_queue: &Arc<Queue>, size: [u32; 2]) -> Self {
|
pub fn new(renderer: &Renderer, compute_queue: &Arc<Queue>, size: [u32; 2]) -> Self {
|
||||||
|
add_default_materials();
|
||||||
|
|
||||||
let compute_pipeline = {
|
let compute_pipeline = {
|
||||||
let shader = cs::load(compute_queue.device().clone()).unwrap();
|
let shader = cs::load(compute_queue.device().clone()).unwrap();
|
||||||
|
|
||||||
|
@ -56,7 +167,7 @@ impl PathtracerPipeline {
|
||||||
).unwrap()
|
).unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
let (raw_image_buffer, image) = create_image(&renderer.memory_allocator, compute_queue, size);
|
let image = create_image(&renderer.memory_allocator, compute_queue, size);
|
||||||
|
|
||||||
let uniform_buffer = SubbufferAllocator::new(
|
let uniform_buffer = SubbufferAllocator::new(
|
||||||
renderer.memory_allocator.clone(),
|
renderer.memory_allocator.clone(),
|
||||||
|
@ -66,9 +177,9 @@ impl PathtracerPipeline {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let (vertices, indices) = load_example_scene();
|
let (vertices, indices, materials) = load_example_scene();
|
||||||
|
|
||||||
let (vertex_buffer, index_buffer) = create_gpu_buffer(&vertices, &indices, &renderer.memory_allocator);
|
let (vertex_buffer, index_buffer, materials) = create_gpu_buffer(&vertices, &indices, &materials, &renderer.memory_allocator);
|
||||||
|
|
||||||
return PathtracerPipeline {
|
return PathtracerPipeline {
|
||||||
compute_queue: compute_queue.clone(),
|
compute_queue: compute_queue.clone(),
|
||||||
|
@ -77,12 +188,13 @@ impl PathtracerPipeline {
|
||||||
descriptor_set_allocator: renderer.descriptor_set_allocator.clone(),
|
descriptor_set_allocator: renderer.descriptor_set_allocator.clone(),
|
||||||
memory_allocator: renderer.memory_allocator.clone(),
|
memory_allocator: renderer.memory_allocator.clone(),
|
||||||
image,
|
image,
|
||||||
raw_image: raw_image_buffer,
|
|
||||||
uniform_buffer: Arc::new(uniform_buffer),
|
uniform_buffer: Arc::new(uniform_buffer),
|
||||||
vertex_buffer,
|
vertex_buffer,
|
||||||
index_buffer,
|
index_buffer,
|
||||||
|
material_buffer: materials,
|
||||||
seconds: Instant::now(),
|
seconds: Instant::now(),
|
||||||
camera: Camera::new()
|
camera: Camera::new(),
|
||||||
|
frames: 0.0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,17 +221,18 @@ impl PathtracerPipeline {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize_image(&mut self, size: [u32; 2]) {
|
pub fn resize_image(&mut self, size: [u32; 2]) {
|
||||||
let (raw_image_buffer, 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.raw_image = raw_image_buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the command for a dispatch.
|
/// Builds the command for a dispatch.
|
||||||
fn dispatch(
|
fn dispatch(
|
||||||
&self,
|
&mut self,
|
||||||
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer,Arc<StandardCommandBufferAllocator>>
|
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer,Arc<StandardCommandBufferAllocator>>
|
||||||
) {
|
) {
|
||||||
|
self.frames += 1.0;
|
||||||
|
|
||||||
let camera = cs::Camera {
|
let camera = cs::Camera {
|
||||||
front: Padded::from(Into::<[f32; 3]>::into(self.camera.front)),
|
front: Padded::from(Into::<[f32; 3]>::into(self.camera.front)),
|
||||||
left: Padded::from(Into::<[f32; 3]>::into(self.camera.left)),
|
left: Padded::from(Into::<[f32; 3]>::into(self.camera.left)),
|
||||||
|
@ -140,17 +253,18 @@ impl PathtracerPipeline {
|
||||||
&self.descriptor_set_allocator,
|
&self.descriptor_set_allocator,
|
||||||
desc_layout.clone(),
|
desc_layout.clone(),
|
||||||
[
|
[
|
||||||
WriteDescriptorSet::buffer(0, self.raw_image.clone()),
|
|
||||||
WriteDescriptorSet::image_view(1, self.image.clone()),
|
WriteDescriptorSet::image_view(1, self.image.clone()),
|
||||||
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())
|
||||||
],
|
],
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
let push_constants = cs::PushConstants {
|
let push_constants = cs::PushConstants {
|
||||||
resolution: [size[0] as f32, size[1] as f32],
|
resolution: [size[0] as f32, size[1] as f32],
|
||||||
seconds: (Instant::now() - self.seconds).as_secs_f32()
|
seconds: (Instant::now() - self.seconds).as_secs_f32(),
|
||||||
|
frames: self.frames
|
||||||
};
|
};
|
||||||
|
|
||||||
builder
|
builder
|
||||||
|
@ -166,58 +280,55 @@ impl PathtracerPipeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_image(memory_allocator: &StandardMemoryAllocator, queue: &Arc<Queue>, size: [u32; 2]) -> (Subbuffer<[[f32; 4]]>, Arc<ImageView<StorageImage>>) {
|
fn create_image(memory_allocator: &StandardMemoryAllocator, queue: &Arc<Queue>, size: [u32; 2]) -> Arc<ImageView<StorageImage>> {
|
||||||
let raw_image = Buffer::from_iter(
|
StorageImage::general_purpose_image_view(
|
||||||
memory_allocator,
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::STORAGE_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
usage: MemoryUsage::Upload,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
vec![[0f32; 4]; (size[0] * size[1]) as usize],
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
let image = StorageImage::general_purpose_image_view(
|
|
||||||
memory_allocator,
|
memory_allocator,
|
||||||
queue.clone(),
|
queue.clone(),
|
||||||
size,
|
size,
|
||||||
Format::R8G8B8A8_UNORM,
|
Format::R32G32B32A32_SFLOAT,
|
||||||
ImageUsage::SAMPLED | ImageUsage::STORAGE | ImageUsage::TRANSFER_DST,
|
ImageUsage::SAMPLED | ImageUsage::STORAGE | ImageUsage::TRANSFER_DST,
|
||||||
).unwrap();
|
).unwrap()
|
||||||
|
|
||||||
(raw_image, image)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_example_scene() -> (Vec<[f32; 4]>, Vec<u32>) {
|
fn load_example_scene() -> (Vec<[f32; 4]>, Vec<u32>, Vec<cs::Material>) {
|
||||||
let (mut models, materials) = tobj::load_obj("res/example-scene.obj", &tobj::GPU_LOAD_OPTIONS).expect("unable to load scene from obj");
|
let (mut models, materials) = tobj::load_obj("res/example-scene.obj", &tobj::GPU_LOAD_OPTIONS).expect("unable to load scene from obj");
|
||||||
|
|
||||||
|
// allocate some host memory
|
||||||
let mut vertices:Vec<[f32; 4]> = vec![];
|
let mut vertices:Vec<[f32; 4]> = vec![];
|
||||||
let mut indices:Vec<u32> = vec![];
|
let mut indices:Vec<u32> = vec![];
|
||||||
|
let mut shader_materials:Vec<cs::Material> = vec![];
|
||||||
|
|
||||||
for model in models.iter_mut() {
|
for model in models.iter_mut() {
|
||||||
let offset = vertices.len() as u32;
|
let offset = vertices.len() as u32;
|
||||||
|
|
||||||
|
let material = model.mesh.material_id.unwrap_or(0);
|
||||||
|
|
||||||
|
// fill the vertex buffer
|
||||||
for vertex_index in (0..model.mesh.positions.len()).step_by(3) {
|
for vertex_index in (0..model.mesh.positions.len()).step_by(3) {
|
||||||
vertices.push([
|
vertices.push([
|
||||||
model.mesh.positions[vertex_index],
|
model.mesh.positions[vertex_index],
|
||||||
model.mesh.positions[vertex_index + 1],
|
model.mesh.positions[vertex_index + 1],
|
||||||
model.mesh.positions[vertex_index + 2],
|
model.mesh.positions[vertex_index + 2],
|
||||||
0.0
|
// padding will store material index to be used for this triangle
|
||||||
|
material as f32
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 + offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(vertices, indices)
|
let material_collection = &MATERIAL_COLLECTIO.lock().unwrap();
|
||||||
|
for material in materials.unwrap().iter() {
|
||||||
|
shader_materials.push(*material_collection.get(&material.name).unwrap_or(&DEFAULT_MATERIAL));
|
||||||
|
}
|
||||||
|
|
||||||
|
(vertices, indices, shader_materials)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_gpu_buffer(vertices: &Vec<[f32; 4]>, indices: &Vec<u32>, memory_allocator: &StandardMemoryAllocator) -> (Subbuffer<[[f32; 4]]>, Subbuffer<[u32]>) {
|
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]>) {
|
||||||
let vertex_buffer = Buffer::from_iter(
|
let vertex_buffer = Buffer::from_iter(
|
||||||
memory_allocator,
|
memory_allocator,
|
||||||
BufferCreateInfo {
|
BufferCreateInfo {
|
||||||
|
@ -225,7 +336,7 @@ fn create_gpu_buffer(vertices: &Vec<[f32; 4]>, indices: &Vec<u32>, memory_alloca
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
AllocationCreateInfo {
|
AllocationCreateInfo {
|
||||||
usage: MemoryUsage::Upload,
|
usage: MemoryUsage::DeviceOnly,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
vertices.clone(),
|
vertices.clone(),
|
||||||
|
@ -238,11 +349,24 @@ fn create_gpu_buffer(vertices: &Vec<[f32; 4]>, indices: &Vec<u32>, memory_alloca
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
AllocationCreateInfo {
|
AllocationCreateInfo {
|
||||||
usage: MemoryUsage::Upload,
|
usage: MemoryUsage::DeviceOnly,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
indices.clone(),
|
indices.clone(),
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
(vertex_buffer, index_buffer)
|
let material_buffer = Buffer::from_iter(
|
||||||
|
memory_allocator,
|
||||||
|
BufferCreateInfo {
|
||||||
|
usage: BufferUsage::STORAGE_BUFFER,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo {
|
||||||
|
usage: MemoryUsage::DeviceOnly,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
materials.clone(),
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
(vertex_buffer, index_buffer, material_buffer)
|
||||||
}
|
}
|
|
@ -3,17 +3,18 @@
|
||||||
#include "rand/random.glsl"
|
#include "rand/random.glsl"
|
||||||
#include "raytracing/raytracing.glsl"
|
#include "raytracing/raytracing.glsl"
|
||||||
#include "raytracing/camera.glsl"
|
#include "raytracing/camera.glsl"
|
||||||
#include "bsdf.glsl"
|
#include "raytracing/bsdf.glsl"
|
||||||
|
|
||||||
|
#define MAX_DEPTH 4
|
||||||
|
|
||||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||||
|
|
||||||
layout(set = 0, binding = 0) buffer LinearImage { vec4 pixels[]; };
|
layout(set = 0, binding = 1, rgba32f) uniform image2D image;
|
||||||
|
|
||||||
layout(set = 0, binding = 1, rgba8) uniform writeonly image2D image;
|
|
||||||
|
|
||||||
layout(push_constant) uniform PushConstants {
|
layout(push_constant) uniform PushConstants {
|
||||||
vec2 resolution;
|
vec2 resolution;
|
||||||
float seconds;
|
float seconds;
|
||||||
|
float frames;
|
||||||
} program_metadata;
|
} program_metadata;
|
||||||
|
|
||||||
vec2 get_viewport_coordinate() {
|
vec2 get_viewport_coordinate() {
|
||||||
|
@ -26,82 +27,44 @@ uint get_pixel_index() {
|
||||||
return gl_GlobalInvocationID.y * uint(program_metadata.resolution.x) + gl_GlobalInvocationID.x;
|
return gl_GlobalInvocationID.y * uint(program_metadata.resolution.x) + gl_GlobalInvocationID.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 project(in vec3 a, in vec3 b) {
|
vec3 trace_direct(in Ray ray) {
|
||||||
return a * (dot(a, b) / dot(a, a));
|
vec3 color = vec3(0);
|
||||||
}
|
vec3 throughput = vec3(1);
|
||||||
|
|
||||||
void construct_orthonormal_basis(in vec3 up, out vec3 u, out vec3 v, out vec3 w) {
|
|
||||||
u = normalize(up);
|
|
||||||
|
|
||||||
vec3 n2 = normalize(cross(up, vec3(0, 1.0, 1.0)));
|
|
||||||
vec3 n3 = cross(u, n2);
|
|
||||||
|
|
||||||
vec3 w2 = normalize(n2 - (project(u, n2)));
|
|
||||||
vec3 w3 = normalize(n3 - project(u, n3) - project(w2, n3));
|
|
||||||
|
|
||||||
v = w2;
|
|
||||||
w = w3;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 cosine_weighted_hemisphere() {
|
|
||||||
float u1 = random();
|
|
||||||
float u2 = random();
|
|
||||||
|
|
||||||
float r = sqrt(u1);
|
|
||||||
float theta = 2 * 3.1415926535 * u2;
|
|
||||||
|
|
||||||
float x = r * cos(theta);
|
|
||||||
float y = r * sin(theta);
|
|
||||||
|
|
||||||
return vec3(x, y, sqrt(1.0 - u1));
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 generate_diffuse_ray_direction(in vec3 nor) {
|
|
||||||
vec3 hemisphere = cosine_weighted_hemisphere();
|
|
||||||
|
|
||||||
vec3 u, v, w;
|
|
||||||
construct_orthonormal_basis(nor, u, v, w);
|
|
||||||
|
|
||||||
return normalize(u * hemisphere.x + v * hemisphere.y + w * hemisphere.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 trace_direct(in Ray direct_ray) {
|
|
||||||
Hit hit = intersect_scene(direct_ray);
|
|
||||||
|
|
||||||
if (hit.depth < direct_ray.far) {
|
|
||||||
|
|
||||||
Ray ray;
|
|
||||||
ray.origin = direct_ray.origin + direct_ray.direction * hit.depth;
|
|
||||||
ray.direction = generate_diffuse_ray_direction(hit.nor);
|
|
||||||
ray.near = 1e-3;
|
|
||||||
ray.far = 1.0;
|
|
||||||
|
|
||||||
|
for (uint depth = 0; depth < MAX_DEPTH; depth++) {
|
||||||
Hit hit = intersect_scene(ray);
|
Hit hit = intersect_scene(ray);
|
||||||
if (hit.depth == 1.0) {
|
|
||||||
return vec3(1);
|
if (!hit.intersected) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return vec3(0);
|
float pdf = abs(dot(ray.direction, hit.normal) / PI);
|
||||||
|
|
||||||
|
Material material = materials[hit.material_index];
|
||||||
|
|
||||||
|
color += material.emission * 64.0 * throughput * pdf;
|
||||||
|
|
||||||
|
throughput *= material.albedo;
|
||||||
|
|
||||||
|
ray.origin = ray.origin + ray.direction * hit.depth;
|
||||||
|
ray.direction = generate_brdf_ray_direction(hit.normal, ray.direction, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return vec3(0);
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
init_random_state(floatBitsToInt(program_metadata.seconds));
|
init_random_state(program_metadata.seconds);
|
||||||
|
|
||||||
vec2 uv = get_viewport_coordinate();
|
vec2 uv = get_viewport_coordinate();
|
||||||
|
|
||||||
Ray camera_ray = construct_camera_ray_pinhole(uv);
|
Ray camera_ray = construct_camera_ray_pinhole(uv);
|
||||||
|
|
||||||
vec3 color = trace_direct(camera_ray);
|
vec4 color = vec4(trace_direct(camera_ray), 1);
|
||||||
|
|
||||||
// index of the current pixel as array index
|
vec4 previous = imageLoad(image, ivec2(gl_GlobalInvocationID.xy));
|
||||||
uint pixel_index = get_pixel_index();
|
|
||||||
|
|
||||||
vec4 buffer_data = pixels[pixel_index] + vec4(color, 1);
|
vec4 merged = mix(previous, color, 1.0 / program_metadata.frames);
|
||||||
// contribute to the raw pixel buffer
|
|
||||||
pixels[pixel_index] = buffer_data;
|
|
||||||
|
|
||||||
imageStore(image, ivec2(gl_GlobalInvocationID.xy), vec4(buffer_data.rgb / buffer_data.a, 1));
|
imageStore(image, ivec2(gl_GlobalInvocationID.xy), merged);
|
||||||
}
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
#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,36 +1,30 @@
|
||||||
|
#ifndef __RANDOM_GLSL__
|
||||||
|
#define __RANDOM_GLSL__
|
||||||
|
|
||||||
uint state;
|
#define _ONE_AT_A_TIME_
|
||||||
|
//#define _XOSHIRO_
|
||||||
|
|
||||||
void init_random_state(uint seed) {
|
#include "one-at-a-time.glsl"
|
||||||
state = (gl_GlobalInvocationID.x << 16) | gl_GlobalInvocationID.y;
|
#include "xoshiro.glsl"
|
||||||
state += seed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A single iteration of Bob Jenkins' One-At-A-Time hashing algorithm.
|
|
||||||
uint hash(in uint x) {
|
|
||||||
x += ( x << 10u );
|
|
||||||
x ^= ( x >> 6u );
|
|
||||||
x += ( x << 3u );
|
|
||||||
x ^= ( x >> 11u );
|
|
||||||
x += ( x << 15u );
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct a float with half-open range [0:1] using low 23 bits.
|
// Construct a float with half-open range [0:1] using low 23 bits.
|
||||||
// All zeroes yields 0.0, all ones yields the next smallest representable value below 1.0.
|
// All zeroes yields 0.0, all ones yields the next smallest representable value below 1.0.
|
||||||
float floatConstruct( uint m ) {
|
float floatConstruct(in uint m) {
|
||||||
const uint ieeeMantissa = 0x007FFFFFu; // binary32 mantissa bitmask
|
const uint ieeeMantissa = 0x007FFFFFu; // binary32 mantissa bitmask
|
||||||
const uint ieeeOne = 0x3F800000u; // 1.0 in IEEE binary32
|
const uint ieeeOne = 0x3F800000u; // 1.0 in IEEE binary32
|
||||||
|
|
||||||
m &= ieeeMantissa; // Keep only mantissa bits (fractional part)
|
m &= ieeeMantissa; // Keep only mantissa bits (fractional part)
|
||||||
m |= ieeeOne; // Add fractional part to 1.0
|
m |= ieeeOne; // Add fractional part to 1.0
|
||||||
|
|
||||||
float f = uintBitsToFloat(m); // Range [1:2]
|
return uintBitsToFloat(m) - 1.0;
|
||||||
return f - 1.0; // Range [0:1]
|
}
|
||||||
|
|
||||||
|
void init_random_state(in float seed) {
|
||||||
|
INIT_STATE_FUNCTION(seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
float random() {
|
float random() {
|
||||||
state = hash(state);
|
return floatConstruct(HASH_FUNCTION());
|
||||||
|
|
||||||
return floatConstruct(state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
|
||||||
|
#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
|
|
@ -0,0 +1,52 @@
|
||||||
|
#include "rand/random.glsl"
|
||||||
|
|
||||||
|
const float PI = 3.1415926535;
|
||||||
|
|
||||||
|
// project vector b onto vector a
|
||||||
|
vec3 project(in vec3 a, in vec3 b) {
|
||||||
|
return a * (dot(a, b) / dot(a, a));
|
||||||
|
}
|
||||||
|
|
||||||
|
// construct a 3D coordinate system with the input up being the "upwards" facing vector
|
||||||
|
// which will be directly stored in w.
|
||||||
|
// Mathematically this function will create two non linear vectors of up and generate an orthonormal
|
||||||
|
// basis using gram-schmidt.
|
||||||
|
// This function assumes "up" being already normalized
|
||||||
|
void construct_orthonormal_basis(in vec3 up, out vec3 u, out vec3 v, out vec3 w) {
|
||||||
|
w = up;
|
||||||
|
|
||||||
|
vec3 n2 = normalize(cross(w, vec3(0, 1.0, 1.0))); // build perpendicular vector from w
|
||||||
|
vec3 n3 = cross(w, n2); // create 2nd vector perpendicular to w and n2
|
||||||
|
|
||||||
|
// gram schmidt
|
||||||
|
u = n2 - (project(w, n2));
|
||||||
|
v = n3 - project(w, n3) - project(u, n3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate a normalized vector within the bounds of the hemisphere with radius of 1.
|
||||||
|
// Z-Coordinate will be "upwards".
|
||||||
|
// radius determines the maximum radius the output vector will have.
|
||||||
|
// NOTE: shrinkin radius will still result in the output to be normalized
|
||||||
|
vec3 cosine_weighted_hemisphere(in float radius) {
|
||||||
|
float u1 = random() * radius;
|
||||||
|
float u2 = random();
|
||||||
|
|
||||||
|
float r = sqrt(u1);
|
||||||
|
float theta = 2 * PI * u2;
|
||||||
|
|
||||||
|
float x = r * cos(theta);
|
||||||
|
float y = r * sin(theta);
|
||||||
|
|
||||||
|
return vec3(x, y, sqrt(1.0 - u1));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 hemisphere = cosine_weighted_hemisphere(roughness);
|
||||||
|
|
||||||
|
vec3 u, v, w;
|
||||||
|
construct_orthonormal_basis(merged_normal, u, v, w);
|
||||||
|
|
||||||
|
return u * hemisphere.x + v * hemisphere.y + w * hemisphere.z;
|
||||||
|
}
|
|
@ -8,9 +8,38 @@ struct Ray {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Hit {
|
struct Hit {
|
||||||
vec3 nor;
|
// normal of the hit triangle
|
||||||
vec2 uv;
|
vec3 normal;
|
||||||
|
// barycentric coordinates of the hit point relative to the hit triangle vertices
|
||||||
|
vec2 barycentric;
|
||||||
|
// distance between the ray origin and the intersection
|
||||||
float depth;
|
float depth;
|
||||||
|
// index of the material of the intersection relative to the material buffer
|
||||||
|
uint material_index;
|
||||||
|
// whether we hit something
|
||||||
|
bool intersected;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Describes the properties of an objects surface and volume
|
||||||
|
struct Material {
|
||||||
|
// how much diffuse light is reflected by the surface
|
||||||
|
vec3 albedo;
|
||||||
|
// emitted light
|
||||||
|
vec3 emission;
|
||||||
|
// color of the specular reflection
|
||||||
|
vec3 specular_color;
|
||||||
|
// roughtness of the microfacets
|
||||||
|
float roughness;
|
||||||
|
// index of refraction (exclusive to metalic)
|
||||||
|
float ior;
|
||||||
|
// how transmissive the surface is (exclusive to metalic)
|
||||||
|
float transmission;
|
||||||
|
// whether the surface is metalic or not (exclusive to ior and transmission)
|
||||||
|
bool metallic;
|
||||||
|
// extra padding required for vulkano not properly padding the structs in the buffer
|
||||||
|
// the size of the useble data is 60 bytes, GLSL will add 4 additional bytes, to round up to 64.
|
||||||
|
// for compatibility I added the padding manually
|
||||||
|
bool __padding;
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(set = 0, binding = 3) buffer VertexBuffer {
|
layout(set = 0, binding = 3) buffer VertexBuffer {
|
||||||
|
@ -21,19 +50,31 @@ layout(set = 0, binding = 4) buffer IndexBuffer {
|
||||||
uint indices[];
|
uint indices[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
layout(set = 0, binding = 5) buffer MaterialBuffer {
|
||||||
|
Material materials[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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
|
||||||
vec3 v1v0 = v1 - v0;
|
vec3 v1v0 = v1 - v0;
|
||||||
vec3 v2v0 = v2 - v0;
|
vec3 v2v0 = v2 - v0;
|
||||||
|
|
||||||
vec3 rov0 = ray.origin - v0;
|
vec3 rov0 = ray.origin - v0;
|
||||||
|
// normal
|
||||||
n = cross(v1v0, v2v0);
|
n = cross(v1v0, v2v0);
|
||||||
|
|
||||||
vec3 q = cross(rov0, ray.direction);
|
vec3 q = cross(rov0, ray.direction);
|
||||||
float d = 1.0 / dot(ray.direction, n);
|
float d = 1.0 / dot(ray.direction, n);
|
||||||
float u = d * dot( -q, v2v0);
|
// barycentric coordinates
|
||||||
float v = d * dot( q, v1v0);
|
float u = d * dot(-q, v2v0);
|
||||||
float t = d * dot( -n, rov0);
|
float v = d * dot(q, v1v0);
|
||||||
|
// intersection distance
|
||||||
|
float t = d * dot(-n, rov0);
|
||||||
|
|
||||||
if(u < 0.0 || v < 0.0 || (u+v) > 1.0 || d > 0.0)
|
// test if the intersection lies outside of the triangle by checking the bounds of the barycentric coordinates
|
||||||
|
// also perform backface culling
|
||||||
|
if(u < 0.0 || v < 0.0 || (u + v) > 1.0 || d > 0.0)
|
||||||
t = -1.0;
|
t = -1.0;
|
||||||
|
|
||||||
return vec3(t, u, v);
|
return vec3(t, u, v);
|
||||||
|
@ -42,19 +83,22 @@ vec3 intersect_triangle(in Ray ray, in vec3 v0, in vec3 v1, in vec3 v2, out vec3
|
||||||
Hit intersect_scene(in Ray ray) {
|
Hit intersect_scene(in Ray ray) {
|
||||||
Hit hit;
|
Hit hit;
|
||||||
hit.depth = ray.far;
|
hit.depth = ray.far;
|
||||||
|
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;
|
vec3 v0 = vertices[indices[i]].xyz;
|
||||||
vec3 v1 = vertices[indices[i + 1]].xyz;
|
vec3 v1 = vertices[indices[i + 1]].xyz;
|
||||||
vec3 v2 = vertices[indices[i + 2]].xyz;
|
vec3 v2 = vertices[indices[i + 2]].xyz;
|
||||||
|
|
||||||
vec3 n;
|
vec3 normal;
|
||||||
vec3 result = intersect_triangle(ray, v0, v1, v2, n);
|
vec3 result = intersect_triangle(ray, v0, v1, v2, normal);
|
||||||
|
|
||||||
if (result.x > ray.near && result.x < hit.depth) {
|
if (result.x > ray.near && result.x < hit.depth) {
|
||||||
hit.uv = result.yz;
|
hit.barycentric = result.yz;
|
||||||
hit.depth = result.x;
|
hit.depth = result.x;
|
||||||
hit.nor = normalize(n);
|
hit.normal = normalize(normal);
|
||||||
|
hit.material_index = uint(vertices[indices[i]].a);
|
||||||
|
hit.intersected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,13 +85,6 @@ pub fn init() {
|
||||||
depth_range: 0.0..1.0,
|
depth_range: 0.0..1.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
// The render pass we created above only describes the layout of our framebuffers. Before we
|
|
||||||
// can draw we also need to create the actual framebuffers.
|
|
||||||
//
|
|
||||||
// Since we need to draw to multiple images, we are going to create a different framebuffer for
|
|
||||||
// each image.
|
|
||||||
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
|
|
||||||
|
|
||||||
// Before we can start creating and recording command buffers, we need a way of allocating
|
// Before we can start creating and recording command buffers, we need a way of allocating
|
||||||
// them. Vulkano provides a command buffer allocator, which manages raw Vulkan command pools
|
// them. Vulkano provides a command buffer allocator, which manages raw Vulkan command pools
|
||||||
// underneath and provides a safe interface for them.
|
// underneath and provides a safe interface for them.
|
||||||
|
@ -108,6 +101,15 @@ pub fn init() {
|
||||||
descriptor_set_allocator
|
descriptor_set_allocator
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut pathtracer = PathtracerPipeline::new(&renderer, &queue, [512, 512]);
|
||||||
|
|
||||||
|
// The render pass we created above only describes the layout of our framebuffers. Before we
|
||||||
|
// can draw we also need to create the actual framebuffers.
|
||||||
|
//
|
||||||
|
// Since we need to draw to multiple images, we are going to create a different framebuffer for
|
||||||
|
// each image.
|
||||||
|
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport, &mut pathtracer);
|
||||||
|
|
||||||
// Initialization is finally finished!
|
// Initialization is finally finished!
|
||||||
|
|
||||||
// In some situations, the swapchain will become invalid by itself. This includes for example
|
// In some situations, the swapchain will become invalid by itself. This includes for example
|
||||||
|
@ -130,7 +132,6 @@ pub fn init() {
|
||||||
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
|
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
|
||||||
|
|
||||||
let texture_drawer = TextureDrawPipeline::new(&renderer, &queue, &render_pass);
|
let texture_drawer = TextureDrawPipeline::new(&renderer, &queue, &render_pass);
|
||||||
let mut pathtracer = PathtracerPipeline::new(&renderer, &queue, [512, 512]);
|
|
||||||
|
|
||||||
let mut now_keys = [false; 255];
|
let mut now_keys = [false; 255];
|
||||||
|
|
||||||
|
@ -218,6 +219,7 @@ pub fn init() {
|
||||||
&new_images,
|
&new_images,
|
||||||
render_pass.clone(),
|
render_pass.clone(),
|
||||||
&mut viewport,
|
&mut viewport,
|
||||||
|
&mut pathtracer
|
||||||
);
|
);
|
||||||
|
|
||||||
recreate_swapchain = false;
|
recreate_swapchain = false;
|
||||||
|
@ -248,7 +250,7 @@ pub fn init() {
|
||||||
recreate_swapchain = true;
|
recreate_swapchain = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pathtracer.compute();
|
let future = pathtracer.compute();
|
||||||
|
|
||||||
let command_buffer = texture_drawer.draw(framebuffers[image_index as usize].clone(), &viewport, &pathtracer);
|
let command_buffer = texture_drawer.draw(framebuffers[image_index as usize].clone(), &viewport, &pathtracer);
|
||||||
|
|
||||||
|
@ -256,6 +258,7 @@ pub fn init() {
|
||||||
.take()
|
.take()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.join(acquire_future)
|
.join(acquire_future)
|
||||||
|
.join(future)
|
||||||
.then_execute(queue.clone(), command_buffer)
|
.then_execute(queue.clone(), command_buffer)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
// The color output is now expected to contain our triangle. But in order to
|
// The color output is now expected to contain our triangle. But in order to
|
||||||
|
@ -298,10 +301,13 @@ 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,
|
||||||
) -> Vec<Arc<Framebuffer>> {
|
) -> Vec<Arc<Framebuffer>> {
|
||||||
let dimensions = images[0].dimensions().width_height();
|
let dimensions = images[0].dimensions().width_height();
|
||||||
viewport.dimensions = [dimensions[0] as f32, dimensions[1] as f32];
|
viewport.dimensions = [dimensions[0] as f32, dimensions[1] as f32];
|
||||||
|
|
||||||
|
pathtracer.resize_image(dimensions);
|
||||||
|
|
||||||
images
|
images
|
||||||
.iter()
|
.iter()
|
||||||
.map(|image| {
|
.map(|image| {
|
||||||
|
@ -312,8 +318,7 @@ fn window_size_dependent_setup(
|
||||||
attachments: vec![view],
|
attachments: vec![view],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
).unwrap()
|
||||||
.unwrap()
|
|
||||||
}).collect::<Vec<_>>()
|
}).collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in New Issue