Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
11
third-party/vendor/metal/examples/raytracing/README.md
vendored
Normal file
11
third-party/vendor/metal/examples/raytracing/README.md
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
## Raytracing
|
||||
|
||||
A good showcase of Metal 3 raytracing features.
|
||||
|
||||

|
||||
|
||||
## To Run
|
||||
|
||||
```
|
||||
cargo run --example raytracing
|
||||
```
|
||||
20
third-party/vendor/metal/examples/raytracing/camera.rs
vendored
Normal file
20
third-party/vendor/metal/examples/raytracing/camera.rs
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
use glam::f32::Vec4;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Camera {
|
||||
pub position: Vec4,
|
||||
pub right: Vec4,
|
||||
pub up: Vec4,
|
||||
pub forward: Vec4,
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
position: Vec4::new(0.0, 3.0, 10.0, 0.0),
|
||||
right: Vec4::new(1.0, 0.0, 0.0, 0.0),
|
||||
up: Vec4::new(0.0, 1.0, 0.0, 0.0),
|
||||
forward: Vec4::new(0.0, 0.0, -1.0, 0.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
448
third-party/vendor/metal/examples/raytracing/geometry.rs
vendored
Normal file
448
third-party/vendor/metal/examples/raytracing/geometry.rs
vendored
Normal file
|
|
@ -0,0 +1,448 @@
|
|||
use std::{
|
||||
mem::{size_of, transmute},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use glam::{
|
||||
f32::{Mat4, Vec3, Vec4},
|
||||
Vec4Swizzles,
|
||||
};
|
||||
|
||||
use metal::*;
|
||||
|
||||
pub const GEOMETRY_MASK_TRIANGLE: u32 = 1;
|
||||
pub const GEOMETRY_MASK_SPHERE: u32 = 2;
|
||||
pub const GEOMETRY_MASK_LIGHT: u32 = 4;
|
||||
|
||||
pub const FACE_MASK_NONE: u16 = 0;
|
||||
pub const FACE_MASK_NEGATIVE_X: u16 = 1 << 0;
|
||||
pub const FACE_MASK_POSITIVE_X: u16 = 1 << 1;
|
||||
pub const FACE_MASK_NEGATIVE_Y: u16 = 1 << 2;
|
||||
pub const FACE_MASK_POSITIVE_Y: u16 = 1 << 3;
|
||||
pub const FACE_MASK_NEGATIVE_Z: u16 = 1 << 4;
|
||||
pub const FACE_MASK_POSITIVE_Z: u16 = 1 << 5;
|
||||
pub const FACE_MASK_ALL: u16 = (1 << 6) - 1;
|
||||
|
||||
pub trait Geometry {
|
||||
fn upload_to_buffers(&mut self) {
|
||||
todo!()
|
||||
}
|
||||
fn clear(&mut self) {
|
||||
todo!()
|
||||
}
|
||||
fn get_geometry_descriptor(&self) -> AccelerationStructureGeometryDescriptor {
|
||||
todo!()
|
||||
}
|
||||
fn get_resources(&self) -> Vec<Resource> {
|
||||
todo!()
|
||||
}
|
||||
fn get_intersection_function_name(&self) -> Option<&str> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_triangle_normal(v0: &Vec3, v1: &Vec3, v2: &Vec3) -> Vec3 {
|
||||
let e1 = Vec3::normalize(*v1 - *v0);
|
||||
let e2 = Vec3::normalize(*v2 - *v0);
|
||||
return Vec3::cross(e1, e2);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[repr(C)]
|
||||
pub struct Triangle {
|
||||
pub normals: [Vec4; 3],
|
||||
pub colours: [Vec4; 3],
|
||||
}
|
||||
|
||||
pub fn get_managed_buffer_storage_mode() -> MTLResourceOptions {
|
||||
return MTLResourceOptions::StorageModeManaged;
|
||||
}
|
||||
|
||||
pub struct TriangleGeometry {
|
||||
pub device: Device,
|
||||
pub name: String,
|
||||
pub index_buffer: Option<Buffer>,
|
||||
pub vertex_position_buffer: Option<Buffer>,
|
||||
pub vertex_normal_buffer: Option<Buffer>,
|
||||
pub vertex_colour_buffer: Option<Buffer>,
|
||||
pub per_primitive_data_buffer: Option<Buffer>,
|
||||
pub indices: Vec<u16>,
|
||||
pub vertices: Vec<Vec4>,
|
||||
pub normals: Vec<Vec4>,
|
||||
pub colours: Vec<Vec4>,
|
||||
pub triangles: Vec<Triangle>,
|
||||
}
|
||||
|
||||
impl TriangleGeometry {
|
||||
pub fn new(device: Device, name: String) -> Self {
|
||||
Self {
|
||||
device,
|
||||
name,
|
||||
index_buffer: None,
|
||||
vertex_position_buffer: None,
|
||||
vertex_normal_buffer: None,
|
||||
vertex_colour_buffer: None,
|
||||
per_primitive_data_buffer: None,
|
||||
indices: Vec::new(),
|
||||
vertices: Vec::new(),
|
||||
normals: Vec::new(),
|
||||
colours: Vec::new(),
|
||||
triangles: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_cube_face_with_cube_vertices(
|
||||
&mut self,
|
||||
cube_vertices: &[Vec3],
|
||||
colour: Vec3,
|
||||
i0: u16,
|
||||
i1: u16,
|
||||
i2: u16,
|
||||
i3: u16,
|
||||
inward_normals: bool,
|
||||
) {
|
||||
let v0 = cube_vertices[i0 as usize];
|
||||
let v1 = cube_vertices[i1 as usize];
|
||||
let v2 = cube_vertices[i2 as usize];
|
||||
let v3 = cube_vertices[i3 as usize];
|
||||
|
||||
let n0 = compute_triangle_normal(&v0, &v1, &v2) * if inward_normals { -1f32 } else { 1f32 };
|
||||
let n1 = compute_triangle_normal(&v0, &v2, &v3) * if inward_normals { -1f32 } else { 1f32 };
|
||||
|
||||
let first_index = self.indices.len();
|
||||
let base_index = self.vertices.len() as u16;
|
||||
|
||||
self.indices.push(base_index + 0);
|
||||
self.indices.push(base_index + 1);
|
||||
self.indices.push(base_index + 2);
|
||||
self.indices.push(base_index + 0);
|
||||
self.indices.push(base_index + 2);
|
||||
self.indices.push(base_index + 3);
|
||||
|
||||
self.vertices.push(From::from((v0, 0.0)));
|
||||
self.vertices.push(From::from((v1, 0.0)));
|
||||
self.vertices.push(From::from((v2, 0.0)));
|
||||
self.vertices.push(From::from((v3, 0.0)));
|
||||
|
||||
self.normals
|
||||
.push(From::from((Vec3::normalize(n0 + n1), 0.0)));
|
||||
self.normals.push(From::from((n0, 0.0)));
|
||||
self.normals
|
||||
.push(From::from((Vec3::normalize(n0 + n1), 0.0)));
|
||||
self.normals.push(From::from((n1, 0.0)));
|
||||
|
||||
for _ in 0..4 {
|
||||
self.colours.push(From::from((colour, 0.0)));
|
||||
}
|
||||
|
||||
for triangle_index in 0..2 {
|
||||
let mut triangle = Triangle::default();
|
||||
for i in 0..3 {
|
||||
let index = self.indices[first_index + triangle_index * 3 + i];
|
||||
triangle.normals[i] = self.normals[index as usize];
|
||||
triangle.colours[i] = self.colours[index as usize];
|
||||
}
|
||||
self.triangles.push(triangle);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_cube_with_faces(
|
||||
&mut self,
|
||||
face_mask: u16,
|
||||
colour: Vec3,
|
||||
transform: Mat4,
|
||||
inward_normals: bool,
|
||||
) {
|
||||
let mut cube_vertices = [
|
||||
Vec3::new(-0.5, -0.5, -0.5),
|
||||
Vec3::new(0.5, -0.5, -0.5),
|
||||
Vec3::new(-0.5, 0.5, -0.5),
|
||||
Vec3::new(0.5, 0.5, -0.5),
|
||||
Vec3::new(-0.5, -0.5, 0.5),
|
||||
Vec3::new(0.5, -0.5, 0.5),
|
||||
Vec3::new(-0.5, 0.5, 0.5),
|
||||
Vec3::new(0.5, 0.5, 0.5),
|
||||
];
|
||||
|
||||
for i in 0..8 {
|
||||
let transformed_vertex = Vec4::from((cube_vertices[i], 1.0));
|
||||
let transformed_vertex = transform * transformed_vertex;
|
||||
cube_vertices[i] = transformed_vertex.xyz();
|
||||
}
|
||||
|
||||
const CUBE_INDICES: [[u16; 4]; 6] = [
|
||||
[0, 4, 6, 2],
|
||||
[1, 3, 7, 5],
|
||||
[0, 1, 5, 4],
|
||||
[2, 6, 7, 3],
|
||||
[0, 2, 3, 1],
|
||||
[4, 5, 7, 6],
|
||||
];
|
||||
|
||||
for face in 0..6 {
|
||||
if face_mask & (1 << face) != 0 {
|
||||
self.add_cube_face_with_cube_vertices(
|
||||
&cube_vertices,
|
||||
colour,
|
||||
CUBE_INDICES[face][0],
|
||||
CUBE_INDICES[face][1],
|
||||
CUBE_INDICES[face][2],
|
||||
CUBE_INDICES[face][3],
|
||||
inward_normals,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Geometry for TriangleGeometry {
|
||||
fn upload_to_buffers(&mut self) {
|
||||
self.index_buffer = Some(unsafe {
|
||||
self.device.new_buffer_with_data(
|
||||
transmute(self.indices.as_ptr()),
|
||||
(self.indices.len() * size_of::<u16>()) as NSUInteger,
|
||||
get_managed_buffer_storage_mode(),
|
||||
)
|
||||
});
|
||||
self.vertex_position_buffer = Some(unsafe {
|
||||
self.device.new_buffer_with_data(
|
||||
transmute(self.vertices.as_ptr()),
|
||||
(self.vertices.len() * size_of::<Vec4>()) as NSUInteger,
|
||||
get_managed_buffer_storage_mode(),
|
||||
)
|
||||
});
|
||||
self.vertex_normal_buffer = Some(unsafe {
|
||||
self.device.new_buffer_with_data(
|
||||
transmute(self.normals.as_ptr()),
|
||||
(self.normals.len() * size_of::<Vec4>()) as NSUInteger,
|
||||
get_managed_buffer_storage_mode(),
|
||||
)
|
||||
});
|
||||
self.vertex_colour_buffer = Some(unsafe {
|
||||
self.device.new_buffer_with_data(
|
||||
transmute(self.colours.as_ptr()),
|
||||
(self.colours.len() * size_of::<Vec4>()) as NSUInteger,
|
||||
get_managed_buffer_storage_mode(),
|
||||
)
|
||||
});
|
||||
self.per_primitive_data_buffer = Some(unsafe {
|
||||
self.device.new_buffer_with_data(
|
||||
transmute(self.triangles.as_ptr()),
|
||||
(self.triangles.len() * size_of::<Triangle>()) as NSUInteger,
|
||||
get_managed_buffer_storage_mode(),
|
||||
)
|
||||
});
|
||||
self.index_buffer
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.did_modify_range(NSRange::new(
|
||||
0,
|
||||
self.index_buffer.as_ref().unwrap().length(),
|
||||
));
|
||||
self.vertex_position_buffer
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.did_modify_range(NSRange::new(
|
||||
0,
|
||||
self.vertex_position_buffer.as_ref().unwrap().length(),
|
||||
));
|
||||
self.vertex_normal_buffer
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.did_modify_range(NSRange::new(
|
||||
0,
|
||||
self.vertex_normal_buffer.as_ref().unwrap().length(),
|
||||
));
|
||||
self.vertex_colour_buffer
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.did_modify_range(NSRange::new(
|
||||
0,
|
||||
self.vertex_colour_buffer.as_ref().unwrap().length(),
|
||||
));
|
||||
self.per_primitive_data_buffer
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.did_modify_range(NSRange::new(
|
||||
0,
|
||||
self.per_primitive_data_buffer.as_ref().unwrap().length(),
|
||||
));
|
||||
|
||||
self.index_buffer
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_label(&format!("index buffer of {}", self.name));
|
||||
self.vertex_position_buffer
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_label(&format!("vertex position buffer of {}", self.name));
|
||||
self.vertex_normal_buffer
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_label(&format!("vertex normal buffer of {}", self.name));
|
||||
self.vertex_colour_buffer
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_label(&format!("vertex colour buffer of {}", self.name));
|
||||
self.per_primitive_data_buffer
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_label(&format!("per primitive data buffer of {}", self.name));
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.indices.clear();
|
||||
self.vertices.clear();
|
||||
self.normals.clear();
|
||||
self.colours.clear();
|
||||
self.triangles.clear();
|
||||
}
|
||||
|
||||
fn get_geometry_descriptor(&self) -> AccelerationStructureGeometryDescriptor {
|
||||
let descriptor = AccelerationStructureTriangleGeometryDescriptor::descriptor();
|
||||
|
||||
descriptor.set_index_buffer(Some(self.index_buffer.as_ref().unwrap()));
|
||||
descriptor.set_index_type(MTLIndexType::UInt16);
|
||||
descriptor.set_vertex_buffer(Some(self.vertex_position_buffer.as_ref().unwrap()));
|
||||
descriptor.set_vertex_stride(size_of::<Vec4>() as NSUInteger);
|
||||
descriptor.set_triangle_count((self.indices.len() / 3) as NSUInteger);
|
||||
descriptor
|
||||
.set_primitive_data_buffer(Some(self.per_primitive_data_buffer.as_ref().unwrap()));
|
||||
descriptor.set_primitive_data_stride(size_of::<Triangle>() as NSUInteger);
|
||||
descriptor.set_primitive_data_element_size(size_of::<Triangle>() as NSUInteger);
|
||||
From::from(descriptor)
|
||||
}
|
||||
|
||||
fn get_resources(&self) -> Vec<Resource> {
|
||||
vec![
|
||||
From::from(self.index_buffer.as_ref().unwrap().clone()),
|
||||
From::from(self.vertex_normal_buffer.as_ref().unwrap().clone()),
|
||||
From::from(self.vertex_colour_buffer.as_ref().unwrap().clone()),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct BoundingBox {
|
||||
pub min: Vec3,
|
||||
pub max: Vec3,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Sphere {
|
||||
pub origin_radius_squared: Vec4,
|
||||
pub colour_radius: Vec4,
|
||||
}
|
||||
|
||||
pub struct SphereGeometry {
|
||||
pub device: Device,
|
||||
pub sphere_buffer: Option<Buffer>,
|
||||
pub bounding_box_buffer: Option<Buffer>,
|
||||
pub per_primitive_data_buffer: Option<Buffer>,
|
||||
pub spheres: Vec<Sphere>,
|
||||
}
|
||||
|
||||
impl SphereGeometry {
|
||||
pub fn new(device: Device) -> Self {
|
||||
Self {
|
||||
device,
|
||||
sphere_buffer: None,
|
||||
bounding_box_buffer: None,
|
||||
per_primitive_data_buffer: None,
|
||||
spheres: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_sphere_with_origin(&mut self, origin: Vec3, radius: f32, colour: Vec3) {
|
||||
self.spheres.push(Sphere {
|
||||
origin_radius_squared: Vec4::from((origin, radius * radius)),
|
||||
colour_radius: Vec4::from((colour, radius)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Geometry for SphereGeometry {
|
||||
fn upload_to_buffers(&mut self) {
|
||||
self.sphere_buffer = Some(unsafe {
|
||||
self.device.new_buffer_with_data(
|
||||
transmute(self.spheres.as_ptr()),
|
||||
(self.spheres.len() * size_of::<Sphere>()) as NSUInteger,
|
||||
get_managed_buffer_storage_mode(),
|
||||
)
|
||||
});
|
||||
self.sphere_buffer
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_label("sphere buffer");
|
||||
let mut bounding_boxes = Vec::new();
|
||||
for sphere in &self.spheres {
|
||||
bounding_boxes.push(BoundingBox {
|
||||
min: sphere.origin_radius_squared.xyz() - sphere.colour_radius.w,
|
||||
max: sphere.origin_radius_squared.xyz() + sphere.colour_radius.w,
|
||||
});
|
||||
}
|
||||
self.bounding_box_buffer = Some(unsafe {
|
||||
self.device.new_buffer_with_data(
|
||||
transmute(bounding_boxes.as_ptr()),
|
||||
(bounding_boxes.len() * size_of::<BoundingBox>()) as NSUInteger,
|
||||
get_managed_buffer_storage_mode(),
|
||||
)
|
||||
});
|
||||
self.bounding_box_buffer
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_label("bounding box buffer");
|
||||
self.sphere_buffer
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.did_modify_range(NSRange::new(
|
||||
0,
|
||||
self.sphere_buffer.as_ref().unwrap().length(),
|
||||
));
|
||||
self.bounding_box_buffer
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.did_modify_range(NSRange::new(
|
||||
0,
|
||||
self.bounding_box_buffer.as_ref().unwrap().length(),
|
||||
));
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.spheres.clear();
|
||||
}
|
||||
|
||||
fn get_geometry_descriptor(&self) -> AccelerationStructureGeometryDescriptor {
|
||||
let descriptor = AccelerationStructureBoundingBoxGeometryDescriptor::descriptor();
|
||||
descriptor.set_bounding_box_buffer(Some(self.bounding_box_buffer.as_ref().unwrap()));
|
||||
descriptor.set_bounding_box_count(self.spheres.len() as NSUInteger);
|
||||
descriptor.set_primitive_data_buffer(Some(&self.sphere_buffer.as_ref().unwrap()));
|
||||
descriptor.set_primitive_data_stride(size_of::<Sphere>() as NSUInteger);
|
||||
descriptor.set_primitive_data_element_size(size_of::<Sphere>() as NSUInteger);
|
||||
From::from(descriptor)
|
||||
}
|
||||
|
||||
fn get_resources(&self) -> Vec<Resource> {
|
||||
return vec![From::from(self.sphere_buffer.as_ref().unwrap().clone())];
|
||||
}
|
||||
|
||||
fn get_intersection_function_name(&self) -> Option<&str> {
|
||||
Some("sphereIntersectionFunction")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GeometryInstance {
|
||||
pub geometry: Arc<dyn Geometry>,
|
||||
pub transform: Mat4,
|
||||
pub mask: u32,
|
||||
pub index_in_scene: NSUInteger,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct AreaLight {
|
||||
pub position: Vec4,
|
||||
pub forward: Vec4,
|
||||
pub right: Vec4,
|
||||
pub up: Vec4,
|
||||
pub colour: Vec4,
|
||||
}
|
||||
87
third-party/vendor/metal/examples/raytracing/main.rs
vendored
Normal file
87
third-party/vendor/metal/examples/raytracing/main.rs
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
extern crate objc;
|
||||
|
||||
use cocoa::{appkit::NSView, base::id as cocoa_id};
|
||||
use core_graphics_types::geometry::CGSize;
|
||||
use metal::*;
|
||||
use objc::{rc::autoreleasepool, runtime::YES};
|
||||
use std::mem;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::ControlFlow,
|
||||
platform::macos::WindowExtMacOS,
|
||||
};
|
||||
|
||||
pub mod camera;
|
||||
pub mod geometry;
|
||||
pub mod renderer;
|
||||
pub mod scene;
|
||||
|
||||
fn find_raytracing_supporting_device() -> Device {
|
||||
for device in Device::all() {
|
||||
if !device.supports_raytracing() {
|
||||
continue;
|
||||
}
|
||||
if device.is_low_power() {
|
||||
continue;
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
panic!("No device in this machine supports raytracing!")
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let events_loop = winit::event_loop::EventLoop::new();
|
||||
let size = winit::dpi::LogicalSize::new(800, 600);
|
||||
|
||||
let window = winit::window::WindowBuilder::new()
|
||||
.with_inner_size(size)
|
||||
.with_title("Metal Raytracing Example".to_string())
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
|
||||
let device = find_raytracing_supporting_device();
|
||||
|
||||
let layer = MetalLayer::new();
|
||||
layer.set_device(&device);
|
||||
layer.set_pixel_format(MTLPixelFormat::RGBA16Float);
|
||||
layer.set_presents_with_transaction(false);
|
||||
|
||||
unsafe {
|
||||
let view = window.ns_view() as cocoa_id;
|
||||
view.setWantsLayer(YES);
|
||||
view.setLayer(mem::transmute(layer.as_ref()));
|
||||
}
|
||||
|
||||
let draw_size = window.inner_size();
|
||||
let cg_size = CGSize::new(draw_size.width as f64, draw_size.height as f64);
|
||||
layer.set_drawable_size(cg_size);
|
||||
|
||||
let mut renderer = renderer::Renderer::new(device);
|
||||
renderer.window_resized(cg_size);
|
||||
|
||||
events_loop.run(move |event, _, control_flow| {
|
||||
autoreleasepool(|| {
|
||||
*control_flow = ControlFlow::Poll;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::Resized(size) => {
|
||||
let size = CGSize::new(size.width as f64, size.height as f64);
|
||||
layer.set_drawable_size(size);
|
||||
renderer.window_resized(size);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::MainEventsCleared => {
|
||||
window.request_redraw();
|
||||
}
|
||||
Event::RedrawRequested(_) => {
|
||||
renderer.draw(&layer);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
512
third-party/vendor/metal/examples/raytracing/renderer.rs
vendored
Normal file
512
third-party/vendor/metal/examples/raytracing/renderer.rs
vendored
Normal file
|
|
@ -0,0 +1,512 @@
|
|||
use core_graphics_types::{base::CGFloat, geometry::CGSize};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
ffi::c_void,
|
||||
mem::{size_of, transmute},
|
||||
ops::Index,
|
||||
sync::{Arc, Condvar, Mutex},
|
||||
};
|
||||
|
||||
use glam::{Vec3, Vec4, Vec4Swizzles};
|
||||
use rand::{thread_rng, RngCore};
|
||||
|
||||
use metal::{foreign_types::ForeignType, *};
|
||||
|
||||
use crate::{camera::Camera, geometry::get_managed_buffer_storage_mode, scene::Scene};
|
||||
|
||||
#[repr(C)]
|
||||
struct Uniforms {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub frame_index: u32,
|
||||
pub light_count: u32,
|
||||
pub camera: Camera,
|
||||
}
|
||||
|
||||
pub const MAX_FRAMES_IN_FLIGHT: NSUInteger = 3;
|
||||
pub const ALIGNED_UNIFORMS_SIZE: NSUInteger = (size_of::<Uniforms>() as NSUInteger + 255) & !255;
|
||||
pub const UNIFORM_BUFFER_SIZE: NSUInteger = MAX_FRAMES_IN_FLIGHT * ALIGNED_UNIFORMS_SIZE;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Semaphore {
|
||||
data: Arc<(Mutex<usize>, Condvar)>,
|
||||
}
|
||||
|
||||
impl Semaphore {
|
||||
fn new(capacity: usize) -> Self {
|
||||
Self {
|
||||
data: Arc::new((Mutex::new(capacity), Condvar::new())),
|
||||
}
|
||||
}
|
||||
|
||||
fn acquire(&self) {
|
||||
let mut value = self.data.0.lock().unwrap();
|
||||
while *value == 0 {
|
||||
value = self.data.1.wait(value).unwrap();
|
||||
}
|
||||
*value -= 1;
|
||||
}
|
||||
|
||||
fn release(&self) {
|
||||
let mut value = self.data.0.lock().unwrap();
|
||||
*value += 1;
|
||||
self.data.1.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Renderer {
|
||||
pub device: Device,
|
||||
pub scene: Scene,
|
||||
pub uniform_buffer: Buffer,
|
||||
pub resource_buffer: Buffer,
|
||||
pub instance_acceleration_structure: AccelerationStructure,
|
||||
pub accumulation_targets: [Texture; 2],
|
||||
pub random_texture: Texture,
|
||||
pub frame_index: NSUInteger,
|
||||
pub uniform_buffer_index: NSUInteger,
|
||||
pub uniform_buffer_offset: NSUInteger,
|
||||
pub size: CGSize,
|
||||
semaphore: Semaphore,
|
||||
pub queue: CommandQueue,
|
||||
instance_buffer: Buffer,
|
||||
intersection_function_table: IntersectionFunctionTable,
|
||||
primitive_acceleration_structures: Vec<AccelerationStructure>,
|
||||
raytracing_pipeline: ComputePipelineState,
|
||||
copy_pipeline: RenderPipelineState,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
pub fn new(device: Device) -> Self {
|
||||
let scene = Scene::new(device.clone());
|
||||
|
||||
let library_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("examples/raytracing/shaders.metallib");
|
||||
let library = device.new_library_with_file(library_path).unwrap();
|
||||
let queue = device.new_command_queue();
|
||||
|
||||
let buffer_data = [0u8; UNIFORM_BUFFER_SIZE as usize];
|
||||
let uniform_buffer = device.new_buffer_with_data(
|
||||
buffer_data.as_ptr() as *const c_void,
|
||||
UNIFORM_BUFFER_SIZE,
|
||||
get_managed_buffer_storage_mode(),
|
||||
);
|
||||
uniform_buffer.set_label("uniform buffer");
|
||||
let resources_stride = {
|
||||
let mut max = 0;
|
||||
for geometry in &scene.geometries {
|
||||
let s = geometry.get_resources().len();
|
||||
if s > max {
|
||||
max = s;
|
||||
}
|
||||
}
|
||||
max
|
||||
};
|
||||
let mut resource_buffer_data = vec![0u64; resources_stride * scene.geometries.len()];
|
||||
for geometry_index in 0..scene.geometries.len() {
|
||||
let geometry = scene.geometries[geometry_index].as_ref();
|
||||
let resource_buffer_begin_index = resources_stride * geometry_index;
|
||||
let resources = geometry.get_resources();
|
||||
|
||||
for argument_index in 0..resources.len() {
|
||||
let resource_buffer_index = resource_buffer_begin_index + argument_index;
|
||||
let resource = resources[argument_index].clone();
|
||||
resource_buffer_data[resource_buffer_index] =
|
||||
if resource.conforms_to_protocol::<MTLBuffer>().unwrap() {
|
||||
let buffer = unsafe { Buffer::from_ptr(transmute(resource.into_ptr())) };
|
||||
buffer.gpu_address()
|
||||
} else if resource.conforms_to_protocol::<MTLTexture>().unwrap() {
|
||||
let texture = unsafe { Texture::from_ptr(transmute(resource.into_ptr())) };
|
||||
texture.gpu_resource_id()._impl
|
||||
} else {
|
||||
panic!("Unexpected resource!")
|
||||
}
|
||||
}
|
||||
}
|
||||
let resource_buffer = device.new_buffer_with_data(
|
||||
resource_buffer_data.as_ptr() as *const c_void,
|
||||
(resource_buffer_data.len() * size_of::<u64>()) as NSUInteger,
|
||||
get_managed_buffer_storage_mode(),
|
||||
);
|
||||
resource_buffer.set_label("resource buffer");
|
||||
resource_buffer.did_modify_range(NSRange::new(0, resource_buffer.length()));
|
||||
|
||||
let mut primitive_acceleration_structures = Vec::new();
|
||||
for i in 0..scene.geometries.len() {
|
||||
let mesh = scene.geometries[i].as_ref();
|
||||
let geometry_descriptor = mesh.get_geometry_descriptor();
|
||||
geometry_descriptor.set_intersection_function_table_offset(i as NSUInteger);
|
||||
let geometry_descriptors = Array::from_owned_slice(&[geometry_descriptor]);
|
||||
let accel_descriptor = PrimitiveAccelerationStructureDescriptor::descriptor();
|
||||
accel_descriptor.set_geometry_descriptors(&geometry_descriptors);
|
||||
let accel_descriptor: AccelerationStructureDescriptor = From::from(accel_descriptor);
|
||||
primitive_acceleration_structures.push(
|
||||
Self::new_acceleration_structure_with_descriptor(
|
||||
&device,
|
||||
&queue,
|
||||
&accel_descriptor,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
let mut instance_descriptors = vec![
|
||||
MTLAccelerationStructureInstanceDescriptor::default();
|
||||
scene.geometry_instances.len()
|
||||
];
|
||||
for instance_index in 0..scene.geometry_instances.len() {
|
||||
let instance = scene.geometry_instances[instance_index].as_ref();
|
||||
let geometry_index = instance.index_in_scene;
|
||||
instance_descriptors[instance_index].acceleration_structure_index =
|
||||
geometry_index as u32;
|
||||
instance_descriptors[instance_index].options =
|
||||
if instance.geometry.get_intersection_function_name().is_none() {
|
||||
MTLAccelerationStructureInstanceOptions::Opaque
|
||||
} else {
|
||||
MTLAccelerationStructureInstanceOptions::None
|
||||
};
|
||||
instance_descriptors[instance_index].intersection_function_table_offset = 0;
|
||||
instance_descriptors[instance_index].mask = instance.mask as u32;
|
||||
for column in 0..4 {
|
||||
for row in 0..3 {
|
||||
instance_descriptors[instance_index].transformation_matrix[column][row] =
|
||||
*instance.transform.col(column).index(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
let instance_buffer = device.new_buffer_with_data(
|
||||
instance_descriptors.as_ptr() as *const c_void,
|
||||
(size_of::<MTLAccelerationStructureInstanceDescriptor>()
|
||||
* scene.geometry_instances.len()) as NSUInteger,
|
||||
get_managed_buffer_storage_mode(),
|
||||
);
|
||||
instance_buffer.set_label("instance buffer");
|
||||
instance_buffer.did_modify_range(NSRange::new(0, instance_buffer.length()));
|
||||
|
||||
let accel_descriptor = InstanceAccelerationStructureDescriptor::descriptor();
|
||||
accel_descriptor.set_instanced_acceleration_structures(&Array::from_owned_slice(
|
||||
&primitive_acceleration_structures,
|
||||
));
|
||||
accel_descriptor.set_instance_count(scene.geometry_instances.len() as NSUInteger);
|
||||
accel_descriptor.set_instance_descriptor_buffer(&instance_buffer);
|
||||
let accel_descriptor: AccelerationStructureDescriptor = From::from(accel_descriptor);
|
||||
let instance_acceleration_structure =
|
||||
Self::new_acceleration_structure_with_descriptor(&device, &queue, &accel_descriptor);
|
||||
|
||||
let mut intersection_functions = BTreeMap::<String, Function>::new();
|
||||
for geometry in &scene.geometries {
|
||||
if let Some(name) = geometry.get_intersection_function_name() {
|
||||
if !intersection_functions.contains_key(name) {
|
||||
let intersection_function = Self::new_specialised_function_with_name(
|
||||
&library,
|
||||
resources_stride as u32,
|
||||
name,
|
||||
);
|
||||
intersection_functions.insert(name.to_string(), intersection_function);
|
||||
}
|
||||
}
|
||||
}
|
||||
let raytracing_function = Self::new_specialised_function_with_name(
|
||||
&library,
|
||||
resources_stride as u32,
|
||||
"raytracingKernel",
|
||||
);
|
||||
let intersection_function_array: Vec<&FunctionRef> = intersection_functions
|
||||
.values()
|
||||
.map(|f| -> &FunctionRef { f })
|
||||
.collect();
|
||||
let raytracing_pipeline = Self::new_compute_pipeline_state_with_function(
|
||||
&device,
|
||||
&raytracing_function,
|
||||
&intersection_function_array,
|
||||
);
|
||||
let intersection_function_table_descriptor = IntersectionFunctionTableDescriptor::new();
|
||||
intersection_function_table_descriptor
|
||||
.set_function_count(scene.geometries.len() as NSUInteger);
|
||||
let intersection_function_table = raytracing_pipeline
|
||||
.new_intersection_function_table_with_descriptor(
|
||||
&intersection_function_table_descriptor,
|
||||
);
|
||||
for geometry_index in 0..scene.geometries.len() {
|
||||
let geometry = scene.geometries[geometry_index].as_ref();
|
||||
if let Some(intersection_function_name) = geometry.get_intersection_function_name() {
|
||||
let intersection_function = &intersection_functions[intersection_function_name];
|
||||
let handle = raytracing_pipeline
|
||||
.function_handle_with_function(intersection_function)
|
||||
.unwrap();
|
||||
intersection_function_table.set_function(handle, geometry_index as NSUInteger);
|
||||
}
|
||||
}
|
||||
let render_descriptor = RenderPipelineDescriptor::new();
|
||||
render_descriptor
|
||||
.set_vertex_function(Some(&library.get_function("copyVertex", None).unwrap()));
|
||||
render_descriptor
|
||||
.set_fragment_function(Some(&library.get_function("copyFragment", None).unwrap()));
|
||||
render_descriptor
|
||||
.color_attachments()
|
||||
.object_at(0)
|
||||
.unwrap()
|
||||
.set_pixel_format(MTLPixelFormat::RGBA16Float);
|
||||
let copy_pipeline = device
|
||||
.new_render_pipeline_state(&render_descriptor)
|
||||
.unwrap();
|
||||
|
||||
let texture_descriptor = Self::create_target_descriptor(1024, 1024);
|
||||
let accumulation_targets = [
|
||||
device.new_texture(&texture_descriptor),
|
||||
device.new_texture(&texture_descriptor),
|
||||
];
|
||||
let random_texture = device.new_texture(&texture_descriptor);
|
||||
|
||||
Self {
|
||||
device,
|
||||
scene,
|
||||
uniform_buffer,
|
||||
resource_buffer,
|
||||
instance_acceleration_structure,
|
||||
accumulation_targets,
|
||||
random_texture,
|
||||
frame_index: 0,
|
||||
uniform_buffer_index: 0,
|
||||
uniform_buffer_offset: 0,
|
||||
size: CGSize::new(1024 as CGFloat, 1024 as CGFloat),
|
||||
semaphore: Semaphore::new((MAX_FRAMES_IN_FLIGHT - 2) as usize),
|
||||
instance_buffer,
|
||||
queue,
|
||||
intersection_function_table,
|
||||
primitive_acceleration_structures,
|
||||
raytracing_pipeline,
|
||||
copy_pipeline,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_target_descriptor(width: NSUInteger, height: NSUInteger) -> TextureDescriptor {
|
||||
let texture_descriptor = TextureDescriptor::new();
|
||||
texture_descriptor.set_pixel_format(MTLPixelFormat::RGBA32Float);
|
||||
texture_descriptor.set_texture_type(MTLTextureType::D2);
|
||||
texture_descriptor.set_width(width);
|
||||
texture_descriptor.set_height(height);
|
||||
texture_descriptor.set_storage_mode(MTLStorageMode::Private);
|
||||
texture_descriptor.set_usage(MTLTextureUsage::ShaderRead | MTLTextureUsage::ShaderWrite);
|
||||
texture_descriptor
|
||||
}
|
||||
|
||||
pub fn window_resized(&mut self, size: CGSize) {
|
||||
self.size = size;
|
||||
let texture_descriptor =
|
||||
Self::create_target_descriptor(size.width as NSUInteger, size.height as NSUInteger);
|
||||
self.accumulation_targets[0] = self.device.new_texture(&texture_descriptor);
|
||||
self.accumulation_targets[1] = self.device.new_texture(&texture_descriptor);
|
||||
texture_descriptor.set_pixel_format(MTLPixelFormat::R32Uint);
|
||||
texture_descriptor.set_usage(MTLTextureUsage::ShaderRead);
|
||||
texture_descriptor.set_storage_mode(MTLStorageMode::Managed);
|
||||
self.random_texture = self.device.new_texture(&texture_descriptor);
|
||||
let mut rng = thread_rng();
|
||||
let mut random_values = vec![0u32; (size.width * size.height) as usize];
|
||||
for v in &mut random_values {
|
||||
*v = rng.next_u32();
|
||||
}
|
||||
self.random_texture.replace_region(
|
||||
MTLRegion::new_2d(0, 0, size.width as NSUInteger, size.height as NSUInteger),
|
||||
0,
|
||||
random_values.as_ptr() as *const c_void,
|
||||
size_of::<u32>() as NSUInteger * size.width as NSUInteger,
|
||||
);
|
||||
self.frame_index = 0;
|
||||
}
|
||||
|
||||
fn update_uniforms(&mut self) {
|
||||
self.uniform_buffer_offset = ALIGNED_UNIFORMS_SIZE * self.uniform_buffer_index;
|
||||
|
||||
let uniforms = unsafe {
|
||||
&mut *((self.uniform_buffer.contents() as *mut u8)
|
||||
.add(self.uniform_buffer_offset as usize) as *mut Uniforms)
|
||||
};
|
||||
|
||||
let position = self.scene.camera.position;
|
||||
let target = self.scene.camera.forward;
|
||||
let up = self.scene.camera.up;
|
||||
|
||||
let forward = Vec3::normalize(target.xyz() - position.xyz());
|
||||
let right = Vec3::normalize(Vec3::cross(forward, up.xyz()));
|
||||
let up = Vec3::normalize(Vec3::cross(right, forward));
|
||||
|
||||
uniforms.camera.position = position;
|
||||
uniforms.camera.forward = Vec4::from((forward, 0.0));
|
||||
uniforms.camera.right = Vec4::from((right, 0.0));
|
||||
uniforms.camera.up = Vec4::from((up, 0.0));
|
||||
|
||||
let field_of_view = 45.0 * (std::f32::consts::PI / 180.0);
|
||||
let aspect_ratio = self.size.width as f32 / self.size.height as f32;
|
||||
let image_plane_height = f32::tan(field_of_view / 2.0);
|
||||
let image_plane_width = aspect_ratio * image_plane_height;
|
||||
|
||||
uniforms.camera.right *= image_plane_width;
|
||||
uniforms.camera.up *= image_plane_height;
|
||||
|
||||
uniforms.width = self.size.width as u32;
|
||||
uniforms.height = self.size.height as u32;
|
||||
|
||||
uniforms.frame_index = self.frame_index as u32;
|
||||
self.frame_index += 1;
|
||||
|
||||
uniforms.light_count = self.scene.lights.len() as u32;
|
||||
|
||||
self.uniform_buffer.did_modify_range(NSRange {
|
||||
location: self.uniform_buffer_offset,
|
||||
length: ALIGNED_UNIFORMS_SIZE,
|
||||
});
|
||||
|
||||
self.uniform_buffer_index = (self.uniform_buffer_index + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||
}
|
||||
|
||||
pub fn draw(&mut self, layer: &MetalLayer) {
|
||||
self.semaphore.acquire();
|
||||
self.update_uniforms();
|
||||
let command_buffer = self.queue.new_command_buffer();
|
||||
let sem = self.semaphore.clone();
|
||||
let block = block::ConcreteBlock::new(move |_| {
|
||||
sem.release();
|
||||
})
|
||||
.copy();
|
||||
command_buffer.add_completed_handler(&block);
|
||||
let width = self.size.width as NSUInteger;
|
||||
let height = self.size.height as NSUInteger;
|
||||
let threads_per_thread_group = MTLSize::new(8, 8, 1);
|
||||
let thread_groups = MTLSize::new(
|
||||
(width + threads_per_thread_group.width - 1) / threads_per_thread_group.width,
|
||||
(height + threads_per_thread_group.height - 1) / threads_per_thread_group.height,
|
||||
1,
|
||||
);
|
||||
let compute_encoder = command_buffer.new_compute_command_encoder();
|
||||
compute_encoder.set_buffer(0, Some(&self.uniform_buffer), self.uniform_buffer_offset);
|
||||
compute_encoder.set_buffer(2, Some(&self.instance_buffer), 0);
|
||||
compute_encoder.set_buffer(3, Some(&self.scene.lights_buffer), 0);
|
||||
compute_encoder.set_acceleration_structure(4, Some(&self.instance_acceleration_structure));
|
||||
compute_encoder.set_intersection_function_table(5, Some(&self.intersection_function_table));
|
||||
compute_encoder.set_texture(0, Some(&self.random_texture));
|
||||
compute_encoder.set_texture(1, Some(&self.accumulation_targets[0]));
|
||||
compute_encoder.set_texture(2, Some(&self.accumulation_targets[1]));
|
||||
for geometry in &self.scene.geometries {
|
||||
for resource in geometry.get_resources() {
|
||||
compute_encoder.use_resource(&resource, MTLResourceUsage::Read);
|
||||
}
|
||||
}
|
||||
for primitive_acceleration_structure in &self.primitive_acceleration_structures {
|
||||
let resource: Resource = From::from(primitive_acceleration_structure.clone());
|
||||
compute_encoder.use_resource(&resource, MTLResourceUsage::Read);
|
||||
}
|
||||
compute_encoder.set_compute_pipeline_state(&self.raytracing_pipeline);
|
||||
compute_encoder.dispatch_thread_groups(thread_groups, threads_per_thread_group);
|
||||
compute_encoder.end_encoding();
|
||||
(self.accumulation_targets[0], self.accumulation_targets[1]) = (
|
||||
self.accumulation_targets[1].clone(),
|
||||
self.accumulation_targets[0].clone(),
|
||||
);
|
||||
if let Some(drawable) = layer.next_drawable() {
|
||||
let render_pass_descriptor = RenderPassDescriptor::new();
|
||||
let colour_attachment = render_pass_descriptor
|
||||
.color_attachments()
|
||||
.object_at(0)
|
||||
.unwrap();
|
||||
colour_attachment.set_texture(Some(drawable.texture()));
|
||||
colour_attachment.set_load_action(MTLLoadAction::Clear);
|
||||
colour_attachment.set_clear_color(MTLClearColor::new(0.0, 0.0, 0.0, 1.0));
|
||||
let render_encoder = command_buffer.new_render_command_encoder(render_pass_descriptor);
|
||||
render_encoder.set_render_pipeline_state(&self.copy_pipeline);
|
||||
render_encoder.set_fragment_texture(0, Some(&self.accumulation_targets[0]));
|
||||
render_encoder.draw_primitives(MTLPrimitiveType::Triangle, 0, 6);
|
||||
render_encoder.end_encoding();
|
||||
command_buffer.present_drawable(&drawable);
|
||||
}
|
||||
command_buffer.commit();
|
||||
}
|
||||
|
||||
fn new_acceleration_structure_with_descriptor(
|
||||
device: &Device,
|
||||
queue: &CommandQueue,
|
||||
descriptor: &AccelerationStructureDescriptorRef,
|
||||
) -> AccelerationStructure {
|
||||
let accel_sizes = device.acceleration_structure_sizes_with_descriptor(descriptor);
|
||||
let acceleration_structure =
|
||||
device.new_acceleration_structure_with_size(accel_sizes.acceleration_structure_size);
|
||||
let scratch_buffer = device.new_buffer(
|
||||
accel_sizes.build_scratch_buffer_size,
|
||||
MTLResourceOptions::StorageModePrivate,
|
||||
);
|
||||
let command_buffer = queue.new_command_buffer();
|
||||
let command_encoder = command_buffer.new_acceleration_structure_command_encoder();
|
||||
let compacted_size_buffer = device.new_buffer(
|
||||
size_of::<u32>() as NSUInteger,
|
||||
MTLResourceOptions::StorageModeShared,
|
||||
);
|
||||
command_encoder.build_acceleration_structure(
|
||||
&acceleration_structure,
|
||||
&descriptor,
|
||||
&scratch_buffer,
|
||||
0,
|
||||
);
|
||||
command_encoder.write_compacted_acceleration_structure_size(
|
||||
&acceleration_structure,
|
||||
&compacted_size_buffer,
|
||||
0,
|
||||
);
|
||||
command_encoder.end_encoding();
|
||||
command_buffer.commit();
|
||||
command_buffer.wait_until_completed();
|
||||
let compacted_size: *const u32 = unsafe { transmute(compacted_size_buffer.contents()) };
|
||||
let compacted_size = unsafe { *compacted_size } as NSUInteger;
|
||||
let compacted_acceleration_structure =
|
||||
device.new_acceleration_structure_with_size(compacted_size);
|
||||
let command_buffer = queue.new_command_buffer();
|
||||
let command_encoder = command_buffer.new_acceleration_structure_command_encoder();
|
||||
command_encoder.copy_and_compact_acceleration_structure(
|
||||
&acceleration_structure,
|
||||
&compacted_acceleration_structure,
|
||||
);
|
||||
command_encoder.end_encoding();
|
||||
command_buffer.commit();
|
||||
compacted_acceleration_structure
|
||||
}
|
||||
|
||||
fn new_specialised_function_with_name(
|
||||
library: &Library,
|
||||
resources_stride: u32,
|
||||
name: &str,
|
||||
) -> Function {
|
||||
let constants = FunctionConstantValues::new();
|
||||
let resources_stride = resources_stride * size_of::<u64>() as u32;
|
||||
constants.set_constant_value_at_index(
|
||||
&resources_stride as *const u32 as *const c_void,
|
||||
MTLDataType::UInt,
|
||||
0,
|
||||
);
|
||||
let v = true;
|
||||
constants.set_constant_value_at_index(
|
||||
&v as *const bool as *const c_void,
|
||||
MTLDataType::Bool,
|
||||
1,
|
||||
);
|
||||
constants.set_constant_value_at_index(
|
||||
&v as *const bool as *const c_void,
|
||||
MTLDataType::Bool,
|
||||
2,
|
||||
);
|
||||
library.get_function(name, Some(constants)).unwrap()
|
||||
}
|
||||
|
||||
fn new_compute_pipeline_state_with_function(
|
||||
device: &Device,
|
||||
function: &Function,
|
||||
linked_functions: &[&FunctionRef],
|
||||
) -> ComputePipelineState {
|
||||
let linked_functions = {
|
||||
let lf = LinkedFunctions::new();
|
||||
lf.set_functions(linked_functions);
|
||||
lf
|
||||
};
|
||||
let descriptor = ComputePipelineDescriptor::new();
|
||||
descriptor.set_compute_function(Some(function));
|
||||
descriptor.set_linked_functions(linked_functions.as_ref());
|
||||
descriptor.set_thread_group_size_is_multiple_of_thread_execution_width(true);
|
||||
device.new_compute_pipeline_state(&descriptor).unwrap()
|
||||
}
|
||||
}
|
||||
135
third-party/vendor/metal/examples/raytracing/scene.rs
vendored
Normal file
135
third-party/vendor/metal/examples/raytracing/scene.rs
vendored
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
use std::{ffi::c_void, mem::size_of, sync::Arc};
|
||||
|
||||
use glam::{Mat4, Vec3, Vec4};
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
use metal::{Buffer, Device, NSRange, NSUInteger};
|
||||
|
||||
use super::{camera::Camera, geometry::*};
|
||||
|
||||
pub struct Scene {
|
||||
pub device: Device,
|
||||
pub camera: Camera,
|
||||
pub geometries: Vec<Arc<dyn Geometry>>,
|
||||
pub geometry_instances: Vec<Arc<GeometryInstance>>,
|
||||
pub lights: Vec<AreaLight>,
|
||||
pub lights_buffer: Buffer,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
pub fn new(device: Device) -> Self {
|
||||
let mut geometries = Vec::<Arc<dyn Geometry>>::new();
|
||||
let mut light_mesh = TriangleGeometry::new(device.clone(), "light".to_string());
|
||||
let transform = Mat4::from_translation(Vec3::new(0.0, 1.0, 0.0))
|
||||
* Mat4::from_scale(Vec3::new(0.5, 1.98, 0.5));
|
||||
light_mesh.add_cube_with_faces(
|
||||
FACE_MASK_POSITIVE_Y,
|
||||
Vec3::new(1.0, 1.0, 1.0),
|
||||
transform,
|
||||
true,
|
||||
);
|
||||
light_mesh.upload_to_buffers();
|
||||
let light_mesh = Arc::new(light_mesh);
|
||||
geometries.push(light_mesh.clone());
|
||||
|
||||
let mut geometry_mesh = TriangleGeometry::new(device.clone(), "geometry".to_string());
|
||||
let transform = Mat4::from_translation(Vec3::new(0.0, 1.0, 0.0))
|
||||
* Mat4::from_scale(Vec3::new(2.0, 2.0, 2.0));
|
||||
geometry_mesh.add_cube_with_faces(
|
||||
FACE_MASK_NEGATIVE_Y | FACE_MASK_POSITIVE_Y | FACE_MASK_NEGATIVE_Z,
|
||||
Vec3::new(0.725, 0.71, 0.68),
|
||||
transform,
|
||||
true,
|
||||
);
|
||||
geometry_mesh.add_cube_with_faces(
|
||||
FACE_MASK_NEGATIVE_X,
|
||||
Vec3::new(0.63, 0.065, 0.05),
|
||||
transform,
|
||||
true,
|
||||
);
|
||||
geometry_mesh.add_cube_with_faces(
|
||||
FACE_MASK_POSITIVE_X,
|
||||
Vec3::new(0.14, 0.45, 0.091),
|
||||
transform,
|
||||
true,
|
||||
);
|
||||
let transform = Mat4::from_translation(Vec3::new(-0.335, 0.6, -0.29))
|
||||
* Mat4::from_rotation_y(0.3)
|
||||
* Mat4::from_scale(Vec3::new(0.6, 1.2, 0.6));
|
||||
geometry_mesh.add_cube_with_faces(
|
||||
FACE_MASK_ALL,
|
||||
Vec3::new(0.725, 0.71, 0.68),
|
||||
transform,
|
||||
false,
|
||||
);
|
||||
geometry_mesh.upload_to_buffers();
|
||||
let geometry_mesh = Arc::new(geometry_mesh);
|
||||
geometries.push(geometry_mesh.clone());
|
||||
|
||||
let mut sphere_geometry = SphereGeometry::new(device.clone());
|
||||
sphere_geometry.add_sphere_with_origin(
|
||||
Vec3::new(0.3275, 0.3, 0.3725),
|
||||
0.3,
|
||||
Vec3::new(0.725, 0.71, 0.68),
|
||||
);
|
||||
sphere_geometry.upload_to_buffers();
|
||||
let sphere_geometry = Arc::new(sphere_geometry);
|
||||
geometries.push(sphere_geometry.clone());
|
||||
|
||||
let mut rng = thread_rng();
|
||||
let mut geometry_instances = Vec::new();
|
||||
let mut lights = Vec::new();
|
||||
for y in -1..2 {
|
||||
for x in -1..2 {
|
||||
let transform =
|
||||
Mat4::from_translation(Vec3::new(x as f32 * 2.5, y as f32 * 2.5, 0.0));
|
||||
geometry_instances.push(Arc::new(GeometryInstance {
|
||||
geometry: light_mesh.clone(),
|
||||
transform,
|
||||
mask: GEOMETRY_MASK_LIGHT,
|
||||
index_in_scene: 0,
|
||||
}));
|
||||
geometry_instances.push(Arc::new(GeometryInstance {
|
||||
geometry: geometry_mesh.clone(),
|
||||
transform,
|
||||
mask: GEOMETRY_MASK_TRIANGLE,
|
||||
index_in_scene: 1,
|
||||
}));
|
||||
geometry_instances.push(Arc::new(GeometryInstance {
|
||||
geometry: sphere_geometry.clone(),
|
||||
transform,
|
||||
mask: GEOMETRY_MASK_SPHERE,
|
||||
index_in_scene: 2,
|
||||
}));
|
||||
lights.push(AreaLight {
|
||||
position: Vec4::new(x as f32 * 2.5, y as f32 * 2.5 + 1.98, 0.0, 0.0),
|
||||
forward: Vec4::new(0.0, -1.0, 0.0, 0.0),
|
||||
right: Vec4::new(0.25, 0.0, 0.0, 0.0),
|
||||
up: Vec4::new(0.0, 0.0, 0.25, 0.0),
|
||||
colour: Vec4::new(
|
||||
rng.gen_range(0f32..=1.0),
|
||||
rng.gen_range(0f32..=1.0),
|
||||
rng.gen_range(0f32..=1.0),
|
||||
0.0,
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
let lights_buffer = device.new_buffer_with_data(
|
||||
lights.as_ptr() as *const c_void,
|
||||
(lights.len() * size_of::<AreaLight>()) as NSUInteger,
|
||||
get_managed_buffer_storage_mode(),
|
||||
);
|
||||
lights_buffer.did_modify_range(NSRange::new(0, lights_buffer.length()));
|
||||
lights_buffer.set_label("lights buffer");
|
||||
|
||||
Self {
|
||||
device,
|
||||
camera: Camera::new(),
|
||||
geometries,
|
||||
geometry_instances,
|
||||
lights,
|
||||
lights_buffer,
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
third-party/vendor/metal/examples/raytracing/screenshot.png
vendored
Normal file
BIN
third-party/vendor/metal/examples/raytracing/screenshot.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 MiB |
598
third-party/vendor/metal/examples/raytracing/shaders.metal
vendored
Normal file
598
third-party/vendor/metal/examples/raytracing/shaders.metal
vendored
Normal file
|
|
@ -0,0 +1,598 @@
|
|||
/*
|
||||
See LICENSE folder for this sample’s licensing information.
|
||||
|
||||
Abstract:
|
||||
The Metal shaders used for this sample.
|
||||
*/
|
||||
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
using namespace raytracing;
|
||||
|
||||
|
||||
#define GEOMETRY_MASK_TRIANGLE 1
|
||||
#define GEOMETRY_MASK_SPHERE 2
|
||||
#define GEOMETRY_MASK_LIGHT 4
|
||||
|
||||
#define GEOMETRY_MASK_GEOMETRY (GEOMETRY_MASK_TRIANGLE | GEOMETRY_MASK_SPHERE)
|
||||
|
||||
#define RAY_MASK_PRIMARY (GEOMETRY_MASK_GEOMETRY | GEOMETRY_MASK_LIGHT)
|
||||
#define RAY_MASK_SHADOW GEOMETRY_MASK_GEOMETRY
|
||||
#define RAY_MASK_SECONDARY GEOMETRY_MASK_GEOMETRY
|
||||
|
||||
struct Camera {
|
||||
vector_float3 position;
|
||||
vector_float3 right;
|
||||
vector_float3 up;
|
||||
vector_float3 forward;
|
||||
};
|
||||
|
||||
struct AreaLight {
|
||||
vector_float3 position;
|
||||
vector_float3 forward;
|
||||
vector_float3 right;
|
||||
vector_float3 up;
|
||||
vector_float3 color;
|
||||
};
|
||||
|
||||
struct Uniforms {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int frameIndex;
|
||||
unsigned int lightCount;
|
||||
Camera camera;
|
||||
};
|
||||
|
||||
struct Sphere {
|
||||
packed_float3 origin;
|
||||
float radiusSquared;
|
||||
packed_float3 color;
|
||||
float radius;
|
||||
};
|
||||
|
||||
struct Triangle {
|
||||
vector_float3 normals[3];
|
||||
vector_float3 colors[3];
|
||||
};
|
||||
|
||||
constant unsigned int resourcesStride [[function_constant(0)]];
|
||||
constant bool useIntersectionFunctions [[function_constant(1)]];
|
||||
constant bool usePerPrimitiveData [[function_constant(2)]];
|
||||
constant bool useResourcesBuffer = !usePerPrimitiveData;
|
||||
|
||||
constant unsigned int primes[] = {
|
||||
2, 3, 5, 7,
|
||||
11, 13, 17, 19,
|
||||
23, 29, 31, 37,
|
||||
41, 43, 47, 53,
|
||||
59, 61, 67, 71,
|
||||
73, 79, 83, 89
|
||||
};
|
||||
|
||||
// Returns the i'th element of the Halton sequence using the d'th prime number as a
|
||||
// base. The Halton sequence is a low discrepency sequence: the values appear
|
||||
// random, but are more evenly distributed than a purely random sequence. Each random
|
||||
// value used to render the image uses a different independent dimension, `d`,
|
||||
// and each sample (frame) uses a different index `i`. To decorrelate each pixel,
|
||||
// you can apply a random offset to `i`.
|
||||
float halton(unsigned int i, unsigned int d) {
|
||||
unsigned int b = primes[d];
|
||||
|
||||
float f = 1.0f;
|
||||
float invB = 1.0f / b;
|
||||
|
||||
float r = 0;
|
||||
|
||||
while (i > 0) {
|
||||
f = f * invB;
|
||||
r = r + f * (i % b);
|
||||
i = i / b;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
// Interpolates the vertex attribute of an arbitrary type across the surface of a triangle
|
||||
// given the barycentric coordinates and triangle index in an intersection structure.
|
||||
template<typename T, typename IndexType>
|
||||
inline T interpolateVertexAttribute(device T *attributes,
|
||||
IndexType i0,
|
||||
IndexType i1,
|
||||
IndexType i2,
|
||||
float2 uv) {
|
||||
// Look up value for each vertex.
|
||||
const T T0 = attributes[i0];
|
||||
const T T1 = attributes[i1];
|
||||
const T T2 = attributes[i2];
|
||||
|
||||
// Compute the sum of the vertex attributes weighted by the barycentric coordinates.
|
||||
// The barycentric coordinates sum to one.
|
||||
return (1.0f - uv.x - uv.y) * T0 + uv.x * T1 + uv.y * T2;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T interpolateVertexAttribute(thread T *attributes, float2 uv) {
|
||||
// Look up the value for each vertex.
|
||||
const T T0 = attributes[0];
|
||||
const T T1 = attributes[1];
|
||||
const T T2 = attributes[2];
|
||||
|
||||
// Compute the sum of the vertex attributes weighted by the barycentric coordinates.
|
||||
// The barycentric coordinates sum to one.
|
||||
return (1.0f - uv.x - uv.y) * T0 + uv.x * T1 + uv.y * T2;
|
||||
}
|
||||
|
||||
// Uses the inversion method to map two uniformly random numbers to a 3D
|
||||
// unit hemisphere, where the probability of a given sample is proportional to the cosine
|
||||
// of the angle between the sample direction and the "up" direction (0, 1, 0).
|
||||
inline float3 sampleCosineWeightedHemisphere(float2 u) {
|
||||
float phi = 2.0f * M_PI_F * u.x;
|
||||
|
||||
float cos_phi;
|
||||
float sin_phi = sincos(phi, cos_phi);
|
||||
|
||||
float cos_theta = sqrt(u.y);
|
||||
float sin_theta = sqrt(1.0f - cos_theta * cos_theta);
|
||||
|
||||
return float3(sin_theta * cos_phi, cos_theta, sin_theta * sin_phi);
|
||||
}
|
||||
|
||||
// Maps two uniformly random numbers to the surface of a 2D area light
|
||||
// source and returns the direction to this point, the amount of light that travels
|
||||
// between the intersection point and the sample point on the light source, as well
|
||||
// as the distance between these two points.
|
||||
|
||||
inline void sampleAreaLight(constant AreaLight & light,
|
||||
float2 u,
|
||||
float3 position,
|
||||
thread float3 & lightDirection,
|
||||
thread float3 & lightColor,
|
||||
thread float & lightDistance)
|
||||
{
|
||||
// Map to -1..1
|
||||
u = u * 2.0f - 1.0f;
|
||||
|
||||
// Transform into the light's coordinate system.
|
||||
float3 samplePosition = light.position +
|
||||
light.right * u.x +
|
||||
light.up * u.y;
|
||||
|
||||
// Compute the vector from sample point on the light source to intersection point.
|
||||
lightDirection = samplePosition - position;
|
||||
|
||||
lightDistance = length(lightDirection);
|
||||
|
||||
float inverseLightDistance = 1.0f / max(lightDistance, 1e-3f);
|
||||
|
||||
// Normalize the light direction.
|
||||
lightDirection *= inverseLightDistance;
|
||||
|
||||
// Start with the light's color.
|
||||
lightColor = light.color;
|
||||
|
||||
// Light falls off with the inverse square of the distance to the intersection point.
|
||||
lightColor *= (inverseLightDistance * inverseLightDistance);
|
||||
|
||||
// Light also falls off with the cosine of the angle between the intersection point
|
||||
// and the light source.
|
||||
lightColor *= saturate(dot(-lightDirection, light.forward));
|
||||
}
|
||||
|
||||
// Aligns a direction on the unit hemisphere such that the hemisphere's "up" direction
|
||||
// (0, 1, 0) maps to the given surface normal direction.
|
||||
inline float3 alignHemisphereWithNormal(float3 sample, float3 normal) {
|
||||
// Set the "up" vector to the normal
|
||||
float3 up = normal;
|
||||
|
||||
// Find an arbitrary direction perpendicular to the normal, which becomes the
|
||||
// "right" vector.
|
||||
float3 right = normalize(cross(normal, float3(0.0072f, 1.0f, 0.0034f)));
|
||||
|
||||
// Find a third vector perpendicular to the previous two, which becomes the
|
||||
// "forward" vector.
|
||||
float3 forward = cross(right, up);
|
||||
|
||||
// Map the direction on the unit hemisphere to the coordinate system aligned
|
||||
// with the normal.
|
||||
return sample.x * right + sample.y * up + sample.z * forward;
|
||||
}
|
||||
|
||||
// Return the type for a bounding box intersection function.
|
||||
struct BoundingBoxIntersection {
|
||||
bool accept [[accept_intersection]]; // Whether to accept or reject the intersection.
|
||||
float distance [[distance]]; // Distance from the ray origin to the intersection point.
|
||||
};
|
||||
|
||||
// Resources for a piece of triangle geometry.
|
||||
struct TriangleResources {
|
||||
device uint16_t *indices;
|
||||
device float3 *vertexNormals;
|
||||
device float3 *vertexColors;
|
||||
};
|
||||
|
||||
// Resources for a piece of sphere geometry.
|
||||
struct SphereResources {
|
||||
device Sphere *spheres;
|
||||
};
|
||||
|
||||
/*
|
||||
Custom sphere intersection function. The [[intersection]] keyword marks this as an intersection
|
||||
function. The [[bounding_box]] keyword means that this intersection function handles intersecting rays
|
||||
with bounding box primitives. To create sphere primitives, the sample creates bounding boxes that
|
||||
enclose the sphere primitives.
|
||||
|
||||
The [[triangle_data]] and [[instancing]] keywords indicate that the intersector that calls this
|
||||
intersection function returns barycentric coordinates for triangle intersections and traverses
|
||||
an instance acceleration structure. These keywords must match between the intersection functions,
|
||||
intersection function table, intersector, and intersection result to ensure that Metal propagates
|
||||
data correctly between stages. Using fewer tags when possible may result in better performance,
|
||||
as Metal may need to store less data and pass less data between stages. For example, if you do not
|
||||
need barycentric coordinates, omitting [[triangle_data]] means Metal can avoid computing and storing
|
||||
them.
|
||||
|
||||
The arguments to the intersection function contain information about the ray, primitive to be
|
||||
tested, and so on. The ray intersector provides this datas when it calls the intersection function.
|
||||
Metal provides other built-in arguments, but this sample doesn't use them.
|
||||
*/
|
||||
[[intersection(bounding_box, triangle_data, instancing)]]
|
||||
BoundingBoxIntersection sphereIntersectionFunction(// Ray parameters passed to the ray intersector below
|
||||
float3 origin [[origin]],
|
||||
float3 direction [[direction]],
|
||||
float minDistance [[min_distance]],
|
||||
float maxDistance [[max_distance]],
|
||||
// Information about the primitive.
|
||||
unsigned int primitiveIndex [[primitive_id]],
|
||||
unsigned int geometryIndex [[geometry_intersection_function_table_offset]],
|
||||
// Custom resources bound to the intersection function table.
|
||||
device void *resources [[buffer(0), function_constant(useResourcesBuffer)]]
|
||||
,const device void* perPrimitiveData [[primitive_data]])
|
||||
{
|
||||
Sphere sphere;
|
||||
// Look up the resources for this piece of sphere geometry.
|
||||
if (usePerPrimitiveData) {
|
||||
// Per-primitive data points to data from the specified buffer as was configured in the MTLAccelerationStructureBoundingBoxGeometryDescriptor.
|
||||
sphere = *(const device Sphere*)perPrimitiveData;
|
||||
} else
|
||||
{
|
||||
device SphereResources& sphereResources = *(device SphereResources *)((device char *)resources + resourcesStride * geometryIndex);
|
||||
// Get the actual sphere enclosed in this bounding box.
|
||||
sphere = sphereResources.spheres[primitiveIndex];
|
||||
}
|
||||
|
||||
// Check for intersection between the ray and sphere mathematically.
|
||||
float3 oc = origin - sphere.origin;
|
||||
|
||||
float a = dot(direction, direction);
|
||||
float b = 2 * dot(oc, direction);
|
||||
float c = dot(oc, oc) - sphere.radiusSquared;
|
||||
|
||||
float disc = b * b - 4 * a * c;
|
||||
|
||||
BoundingBoxIntersection ret;
|
||||
|
||||
if (disc <= 0.0f) {
|
||||
// If the ray missed the sphere, return false.
|
||||
ret.accept = false;
|
||||
}
|
||||
else {
|
||||
// Otherwise, compute the intersection distance.
|
||||
ret.distance = (-b - sqrt(disc)) / (2 * a);
|
||||
|
||||
// The intersection function must also check whether the intersection distance is
|
||||
// within the acceptable range. Intersection functions do not run in any particular order,
|
||||
// so the maximum distance may be different from the one passed into the ray intersector.
|
||||
ret.accept = ret.distance >= minDistance && ret.distance <= maxDistance;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
float3 transformPoint(float3 p, float4x4 transform) {
|
||||
return (transform * float4(p.x, p.y, p.z, 1.0f)).xyz;
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
float3 transformDirection(float3 p, float4x4 transform) {
|
||||
return (transform * float4(p.x, p.y, p.z, 0.0f)).xyz;
|
||||
}
|
||||
|
||||
// Main ray tracing kernel.
|
||||
kernel void raytracingKernel(
|
||||
uint2 tid [[thread_position_in_grid]],
|
||||
constant Uniforms & uniforms [[buffer(0)]],
|
||||
texture2d<unsigned int> randomTex [[texture(0)]],
|
||||
texture2d<float> prevTex [[texture(1)]],
|
||||
texture2d<float, access::write> dstTex [[texture(2)]],
|
||||
device void *resources [[buffer(1), function_constant(useResourcesBuffer)]],
|
||||
constant MTLAccelerationStructureInstanceDescriptor *instances [[buffer(2)]],
|
||||
constant AreaLight *areaLights [[buffer(3)]],
|
||||
instance_acceleration_structure accelerationStructure [[buffer(4)]],
|
||||
intersection_function_table<triangle_data, instancing> intersectionFunctionTable [[buffer(5)]]
|
||||
)
|
||||
{
|
||||
// The sample aligns the thread count to the threadgroup size, which means the thread count
|
||||
// may be different than the bounds of the texture. Test to make sure this thread
|
||||
// is referencing a pixel within the bounds of the texture.
|
||||
if (tid.x >= uniforms.width || tid.y >= uniforms.height) return;
|
||||
|
||||
// The ray to cast.
|
||||
ray ray;
|
||||
|
||||
// Pixel coordinates for this thread.
|
||||
float2 pixel = (float2)tid;
|
||||
|
||||
// Apply a random offset to the random number index to decorrelate pixels.
|
||||
unsigned int offset = randomTex.read(tid).x;
|
||||
|
||||
// Add a random offset to the pixel coordinates for antialiasing.
|
||||
float2 r = float2(halton(offset + uniforms.frameIndex, 0),
|
||||
halton(offset + uniforms.frameIndex, 1));
|
||||
|
||||
pixel += r;
|
||||
|
||||
// Map pixel coordinates to -1..1.
|
||||
float2 uv = (float2)pixel / float2(uniforms.width, uniforms.height);
|
||||
uv = uv * 2.0f - 1.0f;
|
||||
|
||||
constant Camera & camera = uniforms.camera;
|
||||
|
||||
// Rays start at the camera position.
|
||||
ray.origin = camera.position;
|
||||
|
||||
// Map normalized pixel coordinates into camera's coordinate system.
|
||||
ray.direction = normalize(uv.x * camera.right +
|
||||
uv.y * camera.up +
|
||||
camera.forward);
|
||||
|
||||
// Don't limit intersection distance.
|
||||
ray.max_distance = INFINITY;
|
||||
|
||||
// Start with a fully white color. The kernel scales the light each time the
|
||||
// ray bounces off of a surface, based on how much of each light component
|
||||
// the surface absorbs.
|
||||
float3 color = float3(1.0f, 1.0f, 1.0f);
|
||||
|
||||
float3 accumulatedColor = float3(0.0f, 0.0f, 0.0f);
|
||||
|
||||
// Create an intersector to test for intersection between the ray and the geometry in the scene.
|
||||
intersector<triangle_data, instancing> i;
|
||||
|
||||
// If the sample isn't using intersection functions, provide some hints to Metal for
|
||||
// better performance.
|
||||
if (!useIntersectionFunctions) {
|
||||
i.assume_geometry_type(geometry_type::triangle);
|
||||
i.force_opacity(forced_opacity::opaque);
|
||||
}
|
||||
|
||||
typename intersector<triangle_data, instancing>::result_type intersection;
|
||||
|
||||
// Simulate up to three ray bounces. Each bounce propagates light backward along the
|
||||
// ray's path toward the camera.
|
||||
for (int bounce = 0; bounce < 3; bounce++) {
|
||||
// Get the closest intersection, not the first intersection. This is the default, but
|
||||
// the sample adjusts this property below when it casts shadow rays.
|
||||
i.accept_any_intersection(false);
|
||||
|
||||
// Check for intersection between the ray and the acceleration structure. If the sample
|
||||
// isn't using intersection functions, it doesn't need to include one.
|
||||
if (useIntersectionFunctions)
|
||||
intersection = i.intersect(ray, accelerationStructure, bounce == 0 ? RAY_MASK_PRIMARY : RAY_MASK_SECONDARY, intersectionFunctionTable);
|
||||
else
|
||||
intersection = i.intersect(ray, accelerationStructure, bounce == 0 ? RAY_MASK_PRIMARY : RAY_MASK_SECONDARY);
|
||||
|
||||
// Stop if the ray didn't hit anything and has bounced out of the scene.
|
||||
if (intersection.type == intersection_type::none)
|
||||
break;
|
||||
|
||||
unsigned int instanceIndex = intersection.instance_id;
|
||||
|
||||
// Look up the mask for this instance, which indicates what type of geometry the ray hit.
|
||||
unsigned int mask = instances[instanceIndex].mask;
|
||||
|
||||
// If the ray hit a light source, set the color to white, and stop immediately.
|
||||
if (mask == GEOMETRY_MASK_LIGHT) {
|
||||
accumulatedColor = float3(1.0f, 1.0f, 1.0f);
|
||||
break;
|
||||
}
|
||||
|
||||
// The ray hit something. Look up the transformation matrix for this instance.
|
||||
float4x4 objectToWorldSpaceTransform(1.0f);
|
||||
|
||||
for (int column = 0; column < 4; column++)
|
||||
for (int row = 0; row < 3; row++)
|
||||
objectToWorldSpaceTransform[column][row] = instances[instanceIndex].transformationMatrix[column][row];
|
||||
|
||||
// Compute the intersection point in world space.
|
||||
float3 worldSpaceIntersectionPoint = ray.origin + ray.direction * intersection.distance;
|
||||
|
||||
unsigned primitiveIndex = intersection.primitive_id;
|
||||
unsigned int geometryIndex = instances[instanceIndex].accelerationStructureIndex;
|
||||
float2 barycentric_coords = intersection.triangle_barycentric_coord;
|
||||
|
||||
float3 worldSpaceSurfaceNormal = 0.0f;
|
||||
float3 surfaceColor = 0.0f;
|
||||
|
||||
if (mask & GEOMETRY_MASK_TRIANGLE) {
|
||||
Triangle triangle;
|
||||
|
||||
float3 objectSpaceSurfaceNormal;
|
||||
if (usePerPrimitiveData) {
|
||||
// Per-primitive data points to data from the specified buffer as was configured in the MTLAccelerationStructureTriangleGeometryDescriptor.
|
||||
triangle = *(const device Triangle*)intersection.primitive_data;
|
||||
} else
|
||||
{
|
||||
// The ray hit a triangle. Look up the corresponding geometry's normal and UV buffers.
|
||||
device TriangleResources & triangleResources = *(device TriangleResources *)((device char *)resources + resourcesStride * geometryIndex);
|
||||
|
||||
triangle.normals[0] = triangleResources.vertexNormals[triangleResources.indices[primitiveIndex * 3 + 0]];
|
||||
triangle.normals[1] = triangleResources.vertexNormals[triangleResources.indices[primitiveIndex * 3 + 1]];
|
||||
triangle.normals[2] = triangleResources.vertexNormals[triangleResources.indices[primitiveIndex * 3 + 2]];
|
||||
|
||||
triangle.colors[0] = triangleResources.vertexColors[triangleResources.indices[primitiveIndex * 3 + 0]];
|
||||
triangle.colors[1] = triangleResources.vertexColors[triangleResources.indices[primitiveIndex * 3 + 1]];
|
||||
triangle.colors[2] = triangleResources.vertexColors[triangleResources.indices[primitiveIndex * 3 + 2]];
|
||||
}
|
||||
|
||||
// Interpolate the vertex normal at the intersection point.
|
||||
objectSpaceSurfaceNormal = interpolateVertexAttribute(triangle.normals, barycentric_coords);
|
||||
|
||||
// Interpolate the vertex color at the intersection point.
|
||||
surfaceColor = interpolateVertexAttribute(triangle.colors, barycentric_coords);
|
||||
|
||||
// Transform the normal from object to world space.
|
||||
worldSpaceSurfaceNormal = normalize(transformDirection(objectSpaceSurfaceNormal, objectToWorldSpaceTransform));
|
||||
}
|
||||
else if (mask & GEOMETRY_MASK_SPHERE) {
|
||||
Sphere sphere;
|
||||
if (usePerPrimitiveData) {
|
||||
// Per-primitive data points to data from the specified buffer as was configured in the MTLAccelerationStructureBoundingBoxGeometryDescriptor.
|
||||
sphere = *(const device Sphere*)intersection.primitive_data;
|
||||
} else
|
||||
{
|
||||
// The ray hit a sphere. Look up the corresponding sphere buffer.
|
||||
device SphereResources & sphereResources = *(device SphereResources *)((device char *)resources + resourcesStride * geometryIndex);
|
||||
sphere = sphereResources.spheres[primitiveIndex];
|
||||
}
|
||||
|
||||
// Transform the sphere's origin from object space to world space.
|
||||
float3 worldSpaceOrigin = transformPoint(sphere.origin, objectToWorldSpaceTransform);
|
||||
|
||||
// Compute the surface normal directly in world space.
|
||||
worldSpaceSurfaceNormal = normalize(worldSpaceIntersectionPoint - worldSpaceOrigin);
|
||||
|
||||
// The sphere is a uniform color, so you don't need to interpolate the color across the surface.
|
||||
surfaceColor = sphere.color;
|
||||
}
|
||||
|
||||
dstTex.write(float4(accumulatedColor, 1.0f), tid);
|
||||
|
||||
// Choose a random light source to sample.
|
||||
float lightSample = halton(offset + uniforms.frameIndex, 2 + bounce * 5 + 0);
|
||||
unsigned int lightIndex = min((unsigned int)(lightSample * uniforms.lightCount), uniforms.lightCount - 1);
|
||||
|
||||
// Choose a random point to sample on the light source.
|
||||
float2 r = float2(halton(offset + uniforms.frameIndex, 2 + bounce * 5 + 1),
|
||||
halton(offset + uniforms.frameIndex, 2 + bounce * 5 + 2));
|
||||
|
||||
float3 worldSpaceLightDirection;
|
||||
float3 lightColor;
|
||||
float lightDistance;
|
||||
|
||||
// Sample the lighting between the intersection point and the point on the area light.
|
||||
sampleAreaLight(areaLights[lightIndex], r, worldSpaceIntersectionPoint, worldSpaceLightDirection,
|
||||
lightColor, lightDistance);
|
||||
|
||||
// Scale the light color by the cosine of the angle between the light direction and
|
||||
// surface normal.
|
||||
lightColor *= saturate(dot(worldSpaceSurfaceNormal, worldSpaceLightDirection));
|
||||
|
||||
// Scale the light color by the number of lights to compensate for the fact that
|
||||
// the sample samples only one light source at random.
|
||||
lightColor *= uniforms.lightCount;
|
||||
|
||||
// Scale the ray color by the color of the surface to simulate the surface absorbing light.
|
||||
color *= surfaceColor;
|
||||
|
||||
// Compute the shadow ray. The shadow ray checks whether the sample position on the
|
||||
// light source is visible from the current intersection point.
|
||||
// If it is, the kernel adds lighting to the output image.
|
||||
struct ray shadowRay;
|
||||
|
||||
// Add a small offset to the intersection point to avoid intersecting the same
|
||||
// triangle again.
|
||||
shadowRay.origin = worldSpaceIntersectionPoint + worldSpaceSurfaceNormal * 1e-3f;
|
||||
|
||||
// Travel toward the light source.
|
||||
shadowRay.direction = worldSpaceLightDirection;
|
||||
|
||||
// Don't overshoot the light source.
|
||||
shadowRay.max_distance = lightDistance - 1e-3f;
|
||||
|
||||
// Shadow rays check only whether there is an object between the intersection point
|
||||
// and the light source. Tell Metal to return after finding any intersection.
|
||||
i.accept_any_intersection(true);
|
||||
|
||||
if (useIntersectionFunctions)
|
||||
intersection = i.intersect(shadowRay, accelerationStructure, RAY_MASK_SHADOW, intersectionFunctionTable);
|
||||
else
|
||||
intersection = i.intersect(shadowRay, accelerationStructure, RAY_MASK_SHADOW);
|
||||
|
||||
// If there was no intersection, then the light source is visible from the original
|
||||
// intersection point. Add the light's contribution to the image.
|
||||
if (intersection.type == intersection_type::none)
|
||||
accumulatedColor += lightColor * color;
|
||||
|
||||
// Choose a random direction to continue the path of the ray. This causes light to
|
||||
// bounce between surfaces. An app might evaluate a more complicated equation to
|
||||
// calculate the amount of light that reflects between intersection points. However,
|
||||
// all the math in this kernel cancels out because this app assumes a simple diffuse
|
||||
// BRDF and samples the rays with a cosine distribution over the hemisphere (importance
|
||||
// sampling). This requires that the kernel only multiply the colors together. This
|
||||
// sampling strategy also reduces the amount of noise in the output image.
|
||||
r = float2(halton(offset + uniforms.frameIndex, 2 + bounce * 5 + 3),
|
||||
halton(offset + uniforms.frameIndex, 2 + bounce * 5 + 4));
|
||||
|
||||
float3 worldSpaceSampleDirection = sampleCosineWeightedHemisphere(r);
|
||||
worldSpaceSampleDirection = alignHemisphereWithNormal(worldSpaceSampleDirection, worldSpaceSurfaceNormal);
|
||||
|
||||
ray.origin = worldSpaceIntersectionPoint + worldSpaceSurfaceNormal * 1e-3f;
|
||||
ray.direction = worldSpaceSampleDirection;
|
||||
}
|
||||
|
||||
// Average this frame's sample with all of the previous frames.
|
||||
if (uniforms.frameIndex > 0) {
|
||||
float3 prevColor = prevTex.read(tid).xyz;
|
||||
prevColor *= uniforms.frameIndex;
|
||||
|
||||
accumulatedColor += prevColor;
|
||||
accumulatedColor /= (uniforms.frameIndex + 1);
|
||||
}
|
||||
|
||||
dstTex.write(float4(accumulatedColor, 1.0f), tid);
|
||||
}
|
||||
|
||||
// Screen filling quad in normalized device coordinates.
|
||||
constant float2 quadVertices[] = {
|
||||
float2(-1, -1),
|
||||
float2(-1, 1),
|
||||
float2( 1, 1),
|
||||
float2(-1, -1),
|
||||
float2( 1, 1),
|
||||
float2( 1, -1)
|
||||
};
|
||||
|
||||
struct CopyVertexOut {
|
||||
float4 position [[position]];
|
||||
float2 uv;
|
||||
};
|
||||
|
||||
// Simple vertex shader that passes through NDC quad positions.
|
||||
vertex CopyVertexOut copyVertex(unsigned short vid [[vertex_id]]) {
|
||||
float2 position = quadVertices[vid];
|
||||
|
||||
CopyVertexOut out;
|
||||
|
||||
out.position = float4(position, 0, 1);
|
||||
out.uv = position * 0.5f + 0.5f;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Simple fragment shader that copies a texture and applies a simple tonemapping function.
|
||||
fragment float4 copyFragment(CopyVertexOut in [[stage_in]],
|
||||
texture2d<float> tex)
|
||||
{
|
||||
constexpr sampler sam(min_filter::nearest, mag_filter::nearest, mip_filter::none);
|
||||
|
||||
float3 color = tex.sample(sam, in.uv).xyz;
|
||||
|
||||
// Apply a simple tonemapping function to reduce the dynamic range of the
|
||||
// input image into a range which the screen can display.
|
||||
color = color / (1.0f + color);
|
||||
|
||||
return float4(color, 1.0f);
|
||||
}
|
||||
BIN
third-party/vendor/metal/examples/raytracing/shaders.metallib
vendored
Normal file
BIN
third-party/vendor/metal/examples/raytracing/shaders.metallib
vendored
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue