Render the cubemap

This commit is contained in:
numzero 2024-12-24 19:35:36 +03:00
parent 7ecbd50c05
commit 97cdbcff75
5 changed files with 336 additions and 81 deletions

99
Cargo.lock generated
View File

@ -18,6 +18,12 @@ version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046"
[[package]]
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]] [[package]]
name = "ahash" name = "ahash"
version = "0.8.11" version = "0.8.11"
@ -180,6 +186,12 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "byteorder-lite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.9.0" version = "1.9.0"
@ -316,6 +328,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.21" version = "0.8.21"
@ -380,6 +401,25 @@ dependencies = [
"windows-sys 0.59.0", "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]] [[package]]
name = "foldhash" name = "foldhash"
version = "0.1.4" version = "0.1.4"
@ -547,6 +587,20 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" 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]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.7.0" version = "2.7.0"
@ -715,6 +769,16 @@ dependencies = [
"paste", "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]] [[package]]
name = "naga" name = "naga"
version = "23.1.0" version = "23.1.0"
@ -1109,6 +1173,19 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" 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]] [[package]]
name = "polling" name = "polling"
version = "3.7.4" version = "3.7.4"
@ -1254,6 +1331,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"glam", "glam",
"image",
"pollster", "pollster",
"rand", "rand",
"rand_distr", "rand_distr",
@ -1365,6 +1443,12 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "simd-adler32"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.9" version = "0.4.9"
@ -2318,3 +2402,18 @@ dependencies = [
"quote", "quote",
"syn", "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",
]

View File

@ -7,6 +7,7 @@ default-run = "minitracer"
[dependencies] [dependencies]
bytemuck = { version = "1.21.0", features = ["derive"] } bytemuck = { version = "1.21.0", features = ["derive"] }
glam = { version = "0.29.2", features = ["bytemuck"] } glam = { version = "0.29.2", features = ["bytemuck"] }
image = { version = "0.25.5", default-features = false, features = ["png", "jpeg"] }
pollster = "0.4.0" pollster = "0.4.0"
rand = "0.8.5" rand = "0.8.5"
rand_distr = { version = "0.4.3", features = ["std_math"] } rand_distr = { version = "0.4.3", features = ["std_math"] }

127
src/bin/enview/main.rs Normal file
View File

@ -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<dyn Error>> {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::PRIMARY,
..Default::default()
});
let surface = instance.create_surface(wnd)?;
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
compatible_surface: 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))
}

View File

@ -1,19 +1,14 @@
use std::error::Error; 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 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] { const EXTENT: u32 = 8192;
let w = w as f32;
let h = h as f32; fn make_viewport(m: Mat3) -> [Vertex; 4] {
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 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 { [0, 1, 2, 3].map(|k| Vertex {
dir: world_coord[k], dir: world_coord[k],
screen: screen_coord[k], screen: screen_coord[k],
@ -21,16 +16,10 @@ fn make_viewport(w: u32, h: u32) -> [Vertex; 4] {
} }
fn main() { fn main() {
let event_loop = EventLoop::new().unwrap(); let (device, queue) = pollster::block_on(init_gpu()).unwrap();
#[allow(deprecated)] let format = wgpu::TextureFormat::Rgba8UnormSrgb;
let window = &event_loop let mut noiser = Pipeline::new(&device, format);
.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);
noiser.set_params( noiser.set_params(
&queue, &queue,
perlin::Params { perlin::Params {
@ -38,74 +27,113 @@ fn main() {
radius: 12., radius: 12.,
}, },
); );
let faces = [
let mut surface_configured = false; mat3(-Vec3::Z, -Vec3::Y, Vec3::X),
#[allow(deprecated)] mat3(Vec3::Z, -Vec3::Y, -Vec3::X),
event_loop mat3(Vec3::X, Vec3::Z, Vec3::Y),
.run(move |event, control_flow| match event { mat3(Vec3::X, -Vec3::Z, -Vec3::Y),
Event::WindowEvent { ref event, window_id } if window_id == window.id() => match event { mat3(Vec3::X, -Vec3::Y, Vec3::Z),
WindowEvent::CloseRequested => control_flow.exit(), mat3(-Vec3::X, -Vec3::Y, -Vec3::Z),
WindowEvent::Resized(physical_size) => { ];
surface.configure( let output = device.create_texture(&wgpu::TextureDescriptor {
&device, label: None,
&wgpu::SurfaceConfiguration { size: wgpu::Extent3d {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST, width: EXTENT,
format: wgpu::TextureFormat::Bgra8UnormSrgb, height: EXTENT,
width: physical_size.width, depth_or_array_layers: 6,
height: physical_size.height, },
present_mode: wgpu::PresentMode::Fifo, mip_level_count: 1,
alpha_mode: wgpu::CompositeAlphaMode::Auto, sample_count: 1,
view_formats: vec![], dimension: wgpu::TextureDimension::D2,
desired_maximum_frame_latency: 2, format,
}, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
); view_formats: &[],
noiser.set_view(&queue, &make_viewport(physical_size.width, physical_size.height)); });
surface_configured = true; let buffers = faces.map(|_| {
} device.create_buffer(&wgpu::BufferDescriptor {
WindowEvent::RedrawRequested => { label: None,
if !surface_configured { size: (4 * EXTENT * EXTENT) as u64,
return; usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
} mapped_at_creation: false,
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(); });
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<dyn Error>> { async fn init_gpu() -> Result<(wgpu::Device, wgpu::Queue), Box<dyn Error>> {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::PRIMARY, backends: wgpu::Backends::PRIMARY,
..Default::default() ..Default::default()
}); });
let surface = instance.create_surface(wnd)?;
let adapter = instance let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(), power_preference: wgpu::PowerPreference::default(),
compatible_surface: Some(&surface), compatible_surface: None,
force_fallback_adapter: false, force_fallback_adapter: false,
}) })
.await .await
@ -122,5 +150,5 @@ async fn init_gpu(wnd: &Window) -> Result<(wgpu::Device, wgpu::Queue, wgpu::Surf
) )
.await .await
.unwrap(); .unwrap();
Ok((device, queue, surface)) Ok((device, queue))
} }

View File

@ -28,7 +28,7 @@ pub struct Pipeline {
} }
impl 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 { let view_buf = device.create_buffer(&wgpu::BufferDescriptor {
label: None, label: None,
size: (4 * size_of::<Vertex>()) as u64, size: (4 * size_of::<Vertex>()) as u64,
@ -86,7 +86,7 @@ impl Pipeline {
entry_point: None, entry_point: None,
compilation_options: wgpu::PipelineCompilationOptions::default(), compilation_options: wgpu::PipelineCompilationOptions::default(),
targets: &[Some(wgpu::ColorTargetState { targets: &[Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::Bgra8UnormSrgb, format,
blend: Some(wgpu::BlendState::REPLACE), blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL, write_mask: wgpu::ColorWrites::ALL,
})], })],