From 97cdbcff75e33528543ae801d01498152afccba3 Mon Sep 17 00:00:00 2001 From: numzero Date: Tue, 24 Dec 2024 19:35:36 +0300 Subject: [PATCH] Render the cubemap --- Cargo.lock | 99 ++++++++++++++++++++++ Cargo.toml | 1 + src/bin/enview/main.rs | 127 ++++++++++++++++++++++++++++ src/bin/envmap/main.rs | 186 ++++++++++++++++++++++++----------------- src/perlin.rs | 4 +- 5 files changed, 336 insertions(+), 81 deletions(-) create mode 100644 src/bin/enview/main.rs diff --git a/Cargo.lock b/Cargo.lock index 27e123d..9893217 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,12 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "ahash" version = "0.8.11" @@ -180,6 +186,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.9.0" @@ -316,6 +328,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -380,6 +401,25 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "foldhash" version = "0.1.4" @@ -547,6 +587,20 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "image" +version = "0.25.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" +dependencies = [ + "bytemuck", + "byteorder-lite", + "num-traits", + "png", + "zune-core", + "zune-jpeg", +] + [[package]] name = "indexmap" version = "2.7.0" @@ -715,6 +769,16 @@ dependencies = [ "paste", ] +[[package]] +name = "miniz_oxide" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "naga" version = "23.1.0" @@ -1109,6 +1173,19 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "polling" version = "3.7.4" @@ -1254,6 +1331,7 @@ version = "0.1.0" dependencies = [ "bytemuck", "glam", + "image", "pollster", "rand", "rand_distr", @@ -1365,6 +1443,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "slab" version = "0.4.9" @@ -2318,3 +2402,18 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-jpeg" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml index 3864caf..a2c1ce0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ default-run = "minitracer" [dependencies] bytemuck = { version = "1.21.0", features = ["derive"] } glam = { version = "0.29.2", features = ["bytemuck"] } +image = { version = "0.25.5", default-features = false, features = ["png", "jpeg"] } pollster = "0.4.0" rand = "0.8.5" rand_distr = { version = "0.4.3", features = ["std_math"] } diff --git a/src/bin/enview/main.rs b/src/bin/enview/main.rs new file mode 100644 index 0000000..7b9e0a7 --- /dev/null +++ b/src/bin/enview/main.rs @@ -0,0 +1,127 @@ +use std::error::Error; + +use glam::{vec2, vec3}; +use raytracing3::perlin::{self, Pipeline, Vertex}; +use winit::{ + event::{Event, WindowEvent}, + event_loop::EventLoop, + window::{Window, WindowAttributes}, +}; + +fn make_viewport(w: u32, h: u32) -> [Vertex; 4] { + let w = w as f32; + let h = h as f32; + let (w, h) = (1.0f32.max(w / h), 1.0f32.max(h / w)); + let screen_coord = [vec2(-1., -1.), vec2(1., -1.), vec2(-1., 1.), vec2(1., 1.)]; + let world_coord = [vec3(-w, -h, 2.), vec3(w, -h, 2.), vec3(-w, h, 2.), vec3(w, h, 2.)]; + [0, 1, 2, 3].map(|k| Vertex { + dir: world_coord[k], + screen: screen_coord[k], + }) +} + +fn main() { + let event_loop = EventLoop::new().unwrap(); + + #[allow(deprecated)] + let window = &event_loop + .create_window(WindowAttributes::new().with_title("Noise generation test")) + .unwrap(); + + let (device, queue, surface) = pollster::block_on(init_gpu(window)).unwrap(); + + let format = wgpu::TextureFormat::Bgra8UnormSrgb; + let mut noiser = Pipeline::new(&device, format); + noiser.set_params( + &queue, + perlin::Params { + origin: vec3(0., 0., -12.), + radius: 12., + }, + ); + + let mut surface_configured = false; + #[allow(deprecated)] + event_loop + .run(move |event, control_flow| match event { + Event::WindowEvent { ref event, window_id } if window_id == window.id() => match event { + WindowEvent::CloseRequested => control_flow.exit(), + WindowEvent::Resized(physical_size) => { + surface.configure( + &device, + &wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST, + format, + width: physical_size.width, + height: physical_size.height, + present_mode: wgpu::PresentMode::Fifo, + alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: vec![], + desired_maximum_frame_latency: 2, + }, + ); + noiser.set_view(&queue, &make_viewport(physical_size.width, physical_size.height)); + surface_configured = true; + } + WindowEvent::RedrawRequested => { + if !surface_configured { + return; + } + let output = surface.get_current_texture().unwrap(); + let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default()); + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + occlusion_query_set: None, + timestamp_writes: None, + }); + noiser.render(&mut render_pass); + + drop(render_pass); + queue.submit(std::iter::once(encoder.finish())); + output.present(); + } + _ => {} + }, + _ => {} + }) + .unwrap(); +} + +async fn init_gpu(wnd: &Window) -> Result<(wgpu::Device, wgpu::Queue, wgpu::Surface), Box> { + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: wgpu::Backends::PRIMARY, + ..Default::default() + }); + let surface = instance.create_surface(wnd)?; + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::default(), + compatible_surface: Some(&surface), + force_fallback_adapter: false, + }) + .await + .unwrap(); + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + required_features: wgpu::Features::empty(), + required_limits: wgpu::Limits::default(), + memory_hints: Default::default(), + }, + None, + ) + .await + .unwrap(); + Ok((device, queue, surface)) +} diff --git a/src/bin/envmap/main.rs b/src/bin/envmap/main.rs index 0a976b2..664302a 100644 --- a/src/bin/envmap/main.rs +++ b/src/bin/envmap/main.rs @@ -1,19 +1,14 @@ use std::error::Error; -use glam::{vec2, vec3}; +use glam::{mat3, vec2, vec3, Mat3, Vec3}; +use image::buffer::ConvertBuffer; use raytracing3::perlin::{self, Pipeline, Vertex}; -use winit::{ - event::{Event, WindowEvent}, - event_loop::EventLoop, - window::{Window, WindowAttributes}, -}; -fn make_viewport(w: u32, h: u32) -> [Vertex; 4] { - let w = w as f32; - let h = h as f32; - let (w, h) = (1.0f32.max(w / h), 1.0f32.max(h / w)); +const EXTENT: u32 = 8192; + +fn make_viewport(m: Mat3) -> [Vertex; 4] { let screen_coord = [vec2(-1., -1.), vec2(1., -1.), vec2(-1., 1.), vec2(1., 1.)]; - let world_coord = [vec3(-w, -h, 2.), vec3(w, -h, 2.), vec3(-w, h, 2.), vec3(w, h, 2.)]; + let world_coord = screen_coord.map(|s| m * vec3(s.x, s.y, 1.)); [0, 1, 2, 3].map(|k| Vertex { dir: world_coord[k], screen: screen_coord[k], @@ -21,16 +16,10 @@ fn make_viewport(w: u32, h: u32) -> [Vertex; 4] { } fn main() { - let event_loop = EventLoop::new().unwrap(); + let (device, queue) = pollster::block_on(init_gpu()).unwrap(); - #[allow(deprecated)] - let window = &event_loop - .create_window(WindowAttributes::new().with_title("Noise generation test")) - .unwrap(); - - let (device, queue, surface) = pollster::block_on(init_gpu(window)).unwrap(); - - let mut noiser = Pipeline::new(&device); + let format = wgpu::TextureFormat::Rgba8UnormSrgb; + let mut noiser = Pipeline::new(&device, format); noiser.set_params( &queue, perlin::Params { @@ -38,74 +27,113 @@ fn main() { radius: 12., }, ); - - let mut surface_configured = false; - #[allow(deprecated)] - event_loop - .run(move |event, control_flow| match event { - Event::WindowEvent { ref event, window_id } if window_id == window.id() => match event { - WindowEvent::CloseRequested => control_flow.exit(), - WindowEvent::Resized(physical_size) => { - surface.configure( - &device, - &wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST, - format: wgpu::TextureFormat::Bgra8UnormSrgb, - width: physical_size.width, - height: physical_size.height, - present_mode: wgpu::PresentMode::Fifo, - alpha_mode: wgpu::CompositeAlphaMode::Auto, - view_formats: vec![], - desired_maximum_frame_latency: 2, - }, - ); - noiser.set_view(&queue, &make_viewport(physical_size.width, physical_size.height)); - surface_configured = true; - } - WindowEvent::RedrawRequested => { - if !surface_configured { - return; - } - let output = surface.get_current_texture().unwrap(); - let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default()); - let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: None, - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - occlusion_query_set: None, - timestamp_writes: None, - }); - noiser.render(&mut render_pass); - - drop(render_pass); - queue.submit(std::iter::once(encoder.finish())); - output.present(); - } - _ => {} - }, - _ => {} + let faces = [ + mat3(-Vec3::Z, -Vec3::Y, Vec3::X), + mat3(Vec3::Z, -Vec3::Y, -Vec3::X), + mat3(Vec3::X, Vec3::Z, Vec3::Y), + mat3(Vec3::X, -Vec3::Z, -Vec3::Y), + mat3(Vec3::X, -Vec3::Y, Vec3::Z), + mat3(-Vec3::X, -Vec3::Y, -Vec3::Z), + ]; + let output = device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: EXTENT, + height: EXTENT, + depth_or_array_layers: 6, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, + view_formats: &[], + }); + let buffers = faces.map(|_| { + device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: (4 * EXTENT * EXTENT) as u64, + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, }) - .unwrap(); + }); + for (face, m) in faces.iter().enumerate() { + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + let buffer = &buffers[face]; + let view = output.create_view(&wgpu::TextureViewDescriptor { + dimension: Some(wgpu::TextureViewDimension::D2), + base_array_layer: face as u32, + ..Default::default() + }); + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + occlusion_query_set: None, + timestamp_writes: None, + }); + noiser.set_view(&queue, &make_viewport(*m)); + noiser.render(&mut render_pass); + drop(render_pass); + encoder.copy_texture_to_buffer( + wgpu::ImageCopyTexture { + texture: &output, + mip_level: 0, + origin: wgpu::Origin3d { + x: 0, + y: 0, + z: face as u32, + }, + aspect: wgpu::TextureAspect::All, + }, + wgpu::ImageCopyBuffer { + buffer: &buffer, + layout: wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: Some(4 * EXTENT), + rows_per_image: Some(EXTENT), + }, + }, + wgpu::Extent3d { + width: EXTENT, + height: EXTENT, + depth_or_array_layers: 1, + }, + ); + queue.submit([encoder.finish()]); + } + for buf in &buffers { + buf.slice(..).map_async(wgpu::MapMode::Read, |res| res.unwrap()); + } + device.poll(wgpu::Maintain::Wait); + std::thread::scope(|s| { + for (face, buf) in buffers.iter().enumerate() { + s.spawn(move || { + let img = + image::RgbaImage::from_raw(EXTENT, EXTENT, buf.slice(..).get_mapped_range().to_vec()).unwrap(); + let img: image::RgbImage = img.convert(); + img.save(format!("textures/env{face}.jpeg")).unwrap(); + }); + } + }) } -async fn init_gpu(wnd: &Window) -> Result<(wgpu::Device, wgpu::Queue, wgpu::Surface), Box> { +async fn init_gpu() -> Result<(wgpu::Device, wgpu::Queue), Box> { let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { backends: wgpu::Backends::PRIMARY, ..Default::default() }); - let surface = instance.create_surface(wnd)?; let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::default(), - compatible_surface: Some(&surface), + compatible_surface: None, force_fallback_adapter: false, }) .await @@ -122,5 +150,5 @@ async fn init_gpu(wnd: &Window) -> Result<(wgpu::Device, wgpu::Queue, wgpu::Surf ) .await .unwrap(); - Ok((device, queue, surface)) + Ok((device, queue)) } diff --git a/src/perlin.rs b/src/perlin.rs index 2fb3a48..d6b09df 100644 --- a/src/perlin.rs +++ b/src/perlin.rs @@ -28,7 +28,7 @@ pub struct Pipeline { } impl Pipeline { - pub fn new(device: &wgpu::Device) -> Self { + pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self { let view_buf = device.create_buffer(&wgpu::BufferDescriptor { label: None, size: (4 * size_of::()) as u64, @@ -86,7 +86,7 @@ impl Pipeline { entry_point: None, compilation_options: wgpu::PipelineCompilationOptions::default(), targets: &[Some(wgpu::ColorTargetState { - format: wgpu::TextureFormat::Bgra8UnormSrgb, + format, blend: Some(wgpu::BlendState::REPLACE), write_mask: wgpu::ColorWrites::ALL, })],