Basic noise generator
This commit is contained in:
parent
cec72b0661
commit
b4b1805ce2
|
|
@ -11,6 +11,11 @@
|
|||
"build_cmd": "cargo build",
|
||||
"name": "build",
|
||||
"run_cmd": "cargo run"
|
||||
},
|
||||
{
|
||||
"build_cmd": "cargo build --bin envmap",
|
||||
"name": "envmap",
|
||||
"run_cmd": "cargo run --bin envmap"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
name = "raytracing3"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
default-run = "raytracing3"
|
||||
|
||||
[dependencies]
|
||||
bytemuck = { version = "1.21.0", features = ["derive"] }
|
||||
|
|
|
|||
129
src/bin/envmap/main.rs
Normal file
129
src/bin/envmap/main.rs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
use std::error::Error;
|
||||
|
||||
use glam::{vec2, vec3};
|
||||
use perlin::{Pipeline, Vertex};
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::{Window, WindowAttributes},
|
||||
};
|
||||
|
||||
mod perlin;
|
||||
|
||||
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, 0.), vec3(w, -h, 0.), vec3(-w, h, 0.), vec3(w, h, 0.)];
|
||||
[0, 1, 2, 3].map(|k| Vertex {
|
||||
world: 10. * 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 mut noiser = Pipeline::new(&device);
|
||||
noiser.set_params(
|
||||
&queue,
|
||||
perlin::Params {
|
||||
seed: 42,
|
||||
layers: 4,
|
||||
persistence: 0.5,
|
||||
},
|
||||
);
|
||||
|
||||
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::Bgra8Unorm,
|
||||
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))
|
||||
}
|
||||
125
src/bin/envmap/perlin.rs
Normal file
125
src/bin/envmap/perlin.rs
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
use std::mem::{offset_of, size_of};
|
||||
|
||||
use bytemuck::{bytes_of, Pod, Zeroable};
|
||||
use glam::{Vec2, Vec3};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct Params {
|
||||
pub seed: u32,
|
||||
pub layers: u32,
|
||||
pub persistence: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct Vertex {
|
||||
pub world: Vec3,
|
||||
pub screen: Vec2,
|
||||
}
|
||||
|
||||
pub struct Pipeline {
|
||||
view_buf: wgpu::Buffer,
|
||||
params_buf: wgpu::Buffer,
|
||||
bindings: wgpu::BindGroup,
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
}
|
||||
|
||||
static SHADER: &str = include_str!("perlin.wgsl");
|
||||
|
||||
impl Pipeline {
|
||||
pub fn new(device: &wgpu::Device) -> Self {
|
||||
let view_buf = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: None,
|
||||
size: (4 * size_of::<Vertex>()) as u64,
|
||||
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
let params_buf = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: None,
|
||||
size: size_of::<Params>() as u64,
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: None,
|
||||
source: wgpu::ShaderSource::Wgsl(SHADER.into()),
|
||||
});
|
||||
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: None,
|
||||
layout: None,
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: None,
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
buffers: &[wgpu::VertexBufferLayout {
|
||||
array_stride: size_of::<Vertex>() as u64,
|
||||
step_mode: wgpu::VertexStepMode::Vertex,
|
||||
attributes: &[
|
||||
wgpu::VertexAttribute {
|
||||
shader_location: 0,
|
||||
offset: offset_of!(Vertex, screen) as u64,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
shader_location: 1,
|
||||
offset: offset_of!(Vertex, world) as u64,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
],
|
||||
}],
|
||||
},
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleStrip,
|
||||
..Default::default()
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: 1,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: None,
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: wgpu::TextureFormat::Bgra8Unorm,
|
||||
blend: Some(wgpu::BlendState::REPLACE),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
}),
|
||||
multiview: None,
|
||||
cache: None,
|
||||
});
|
||||
let bindings = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: None,
|
||||
layout: &pipeline.get_bind_group_layout(0),
|
||||
entries: &[wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: params_buf.as_entire_binding(),
|
||||
}],
|
||||
});
|
||||
Self {
|
||||
view_buf,
|
||||
params_buf,
|
||||
bindings,
|
||||
pipeline,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_params(&mut self, queue: &wgpu::Queue, params: Params) {
|
||||
queue.write_buffer(&self.params_buf, 0, bytes_of(¶ms));
|
||||
}
|
||||
|
||||
pub fn set_view(&mut self, queue: &wgpu::Queue, vertices: &[Vertex; 4]) {
|
||||
queue.write_buffer(&self.view_buf, 0, bytes_of(vertices));
|
||||
}
|
||||
|
||||
pub fn render(&self, pass: &mut wgpu::RenderPass) {
|
||||
pass.set_pipeline(&self.pipeline);
|
||||
pass.set_vertex_buffer(0, self.view_buf.slice(..));
|
||||
pass.set_bind_group(0, &self.bindings, &[]);
|
||||
pass.draw(0..4, 0..1);
|
||||
}
|
||||
}
|
||||
90
src/bin/envmap/perlin.wgsl
Normal file
90
src/bin/envmap/perlin.wgsl
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
struct Params {
|
||||
seed: u32,
|
||||
layers: u32,
|
||||
persistence: f32,
|
||||
}
|
||||
|
||||
struct Vertex {
|
||||
@location(0) screen: vec2f,
|
||||
@location(1) world: vec3f,
|
||||
}
|
||||
|
||||
struct Varying {
|
||||
@location(0) world: vec3f,
|
||||
@builtin(position) screen: vec4f,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<uniform> params: Params;
|
||||
|
||||
@vertex
|
||||
fn on_vertex(in: Vertex) -> Varying {
|
||||
return Varying(in.world, vec4(in.screen, 0.0, 1.0));
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn on_fragment(in: Varying) -> @location(0) vec4f {
|
||||
return vec4(0.5 + 0.5 * noise(in.world));
|
||||
}
|
||||
|
||||
fn noise(coords: vec3f) -> f32 {
|
||||
let s = split(coords);
|
||||
var ret = 0.0;
|
||||
for (var i = 0u; i < 2; i++) {
|
||||
for (var j = 0u; j < 2; j++) {
|
||||
for (var k = 0u; k < 2; k++) {
|
||||
ret += part(params.seed, s, vec3u(i, j, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn part(seed: u32, pos: Split, off: vec3u) -> f32 {
|
||||
let base_vec = base(seed, pos.int + off);
|
||||
let to_node = vec3f(off) - pos.frac;
|
||||
let base_val = dot(base_vec, to_node);
|
||||
let scale = smoothstep(vec3(0.0), vec3(1.0), 1.0 - abs(to_node));
|
||||
return scale.x * scale.y * scale.z * base_val;
|
||||
}
|
||||
|
||||
fn base(base_seed: u32, key: vec3u) -> vec3f {
|
||||
var seed = hash(hash(hash(hash(base_seed) ^ key.x) ^ key.y) ^ key.z);
|
||||
return rand_sphere(&seed);
|
||||
}
|
||||
|
||||
struct Split {
|
||||
int: vec3u,
|
||||
frac: vec3f,
|
||||
}
|
||||
|
||||
fn split(val: vec3f) -> Split {
|
||||
let int = floor(val);
|
||||
return Split(vec3u(vec3i(int)), val - int);
|
||||
}
|
||||
|
||||
fn hash(key : u32) -> u32 {
|
||||
var v = key;
|
||||
v *= 0xb384af1bu;
|
||||
v ^= v >> 15u;
|
||||
return v;
|
||||
}
|
||||
|
||||
fn rand(state: ptr<function, u32>) -> u32 {
|
||||
*state = hash(*state);
|
||||
return *state;
|
||||
}
|
||||
|
||||
fn rand_float(state: ptr<function, u32>) -> f32 {
|
||||
return f32(rand(state)) / 0x1p32;
|
||||
}
|
||||
|
||||
fn rand_sphere(state: ptr<function, u32>) -> vec3f {
|
||||
loop {
|
||||
let v = vec3f(rand_float(state), rand_float(state), rand_float(state)) - 0.5;
|
||||
let l = length(v);
|
||||
if (length(v) <= 0.5) {
|
||||
return v / l;
|
||||
}
|
||||
}
|
||||
return vec3f(0.0); // unreachable
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user