Compare commits

..

5 Commits

Author SHA1 Message Date
53b44f27ed Different sort of glare? 2025-03-29 11:31:59 +03:00
3e4de46b50 Merge branch 'glare1' into daylight 2025-03-29 11:31:49 +03:00
60f648cfb9 This works as kinda glare but... not really 2025-01-06 02:13:06 +03:00
973c505f0c Some more glare stuff 2025-01-05 02:36:27 +03:00
c82a1c3803 A simple postprocessing glare 2025-01-05 01:46:49 +03:00
6 changed files with 301 additions and 7 deletions

View File

@ -1,6 +1,7 @@
use std::error::Error; use std::error::Error;
use glam::uvec2; use glam::{uvec2, vec2};
use raytracing3::glare::Glarer;
use raytracing3::present::{self, Presenter}; use raytracing3::present::{self, Presenter};
use raytracing3::scene::{Renderer, SceneParams}; use raytracing3::scene::{Renderer, SceneParams};
use winit::{ use winit::{
@ -27,6 +28,7 @@ fn main() {
let hdr_format = wgpu::TextureFormat::Rgba16Float; let hdr_format = wgpu::TextureFormat::Rgba16Float;
let scene = SceneParams::new(N_SPHERES); let scene = SceneParams::new(N_SPHERES);
let renderer = Renderer::new(&device); let renderer = Renderer::new(&device);
let glarer = Glarer::new(&device, hdr_format);
let presenter = Presenter::new(&device, output_format); let presenter = Presenter::new(&device, output_format);
let mut frame = 0; let mut frame = 0;
@ -72,13 +74,13 @@ fn main() {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[], view_formats: &[],
}); });
let hdr = hdr.create_view(&wgpu::TextureViewDescriptor::default()); let hdr1v = hdr.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
{ {
let mut render_pass = renderer.prepare(&mut encoder, &hdr); let mut render_pass = renderer.prepare(&mut encoder, &hdr1v);
let time = frame as f32 / (60. * RAYS_PER_PIXEL as f32);
for _ in 0..RAYS_PER_PIXEL { for _ in 0..RAYS_PER_PIXEL {
frame += 1; frame += 1;
let time = frame as f32 / (60. * RAYS_PER_PIXEL as f32);
renderer.render_frame( renderer.render_frame(
&device, &device,
&mut render_pass, &mut render_pass,
@ -89,10 +91,12 @@ fn main() {
); );
} }
} }
let sigma = vec2(size.width as f32, size.height as f32).length();
glarer.render(&device, &mut encoder, &hdr, raytracing3::glare::Params { sigma });
presenter.render( presenter.render(
&device, &device,
&mut encoder, &mut encoder,
&hdr, &hdr1v,
&view, &view,
present::Params { present::Params {
divisor: RAYS_PER_PIXEL as f32, divisor: RAYS_PER_PIXEL as f32,

222
src/glare.rs Normal file
View File

@ -0,0 +1,222 @@
use std::mem::offset_of;
use bytemuck::{bytes_of, Pod, Zeroable};
use glam::{vec2, UVec2, Vec2};
use wgpu::util::DeviceExt;
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
#[repr(C)]
struct Vertex {
pub screen: Vec2,
}
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct Params {
pub sigma: f32, // in pixels
}
pub struct Glarer {
view_buf: wgpu::Buffer,
format: wgpu::TextureFormat,
pipeline_h: wgpu::RenderPipeline,
pipeline_v: wgpu::RenderPipeline,
}
static SHADER: &str = include_str!("glare.wgsl");
impl Glarer {
pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self {
let view_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None,
contents: bytes_of(&[(0., 0.), (1., 0.), (0., 1.), (1., 1.)].map(|(x, y)| Vertex { screen: vec2(x, y) })),
usage: wgpu::BufferUsages::VERTEX,
});
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Wgsl(SHADER.into()),
});
let vertex_state = 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,
}],
}],
};
let pipeline1 = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
layout: None,
vertex: vertex_state.clone(),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleStrip,
..Default::default()
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: Some("pass_h"),
compilation_options: wgpu::PipelineCompilationOptions::default(),
targets: &[Some(wgpu::ColorTargetState {
format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
cache: None,
});
let pipeline2 = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
layout: None,
vertex: vertex_state.clone(),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleStrip,
..Default::default()
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: Some("pass_v"),
compilation_options: wgpu::PipelineCompilationOptions::default(),
targets: &[Some(wgpu::ColorTargetState {
format,
blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::One,
dst_factor: wgpu::BlendFactor::One,
operation: wgpu::BlendOperation::Add,
},
alpha: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::One,
dst_factor: wgpu::BlendFactor::One,
operation: wgpu::BlendOperation::Add,
},
}),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
cache: None,
});
Self {
view_buf,
format,
pipeline_h: pipeline1,
pipeline_v: pipeline2,
}
}
pub fn render(
&self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
source: &wgpu::Texture,
// target: &wgpu::Texture,
params: Params,
) {
let target = source;
let size = target.size();
// assert_eq!(source.size(), size);
let intermediate = device.create_texture(&wgpu::TextureDescriptor {
label: None,
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: self.format,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
let intermediate = intermediate.create_view(&wgpu::TextureViewDescriptor::default());
let params_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None,
contents: bytes_of(&params),
usage: wgpu::BufferUsages::UNIFORM,
});
let source = &source.create_view(&wgpu::TextureViewDescriptor::default());
let target = &target.create_view(&wgpu::TextureViewDescriptor::default());
{
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &intermediate,
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,
});
let bindings = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.pipeline_h.get_bind_group_layout(0),
entries: &[
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::TextureView(source),
},
wgpu::BindGroupEntry {
binding: 2,
resource: params_buf.as_entire_binding(),
},
],
});
pass.set_pipeline(&self.pipeline_h);
pass.set_vertex_buffer(0, self.view_buf.slice(..));
pass.set_bind_group(0, &bindings, &[]);
pass.draw(0..4, 0..1);
}
{
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: target,
resolve_target: None,
ops: wgpu::Operations {
// load: wgpu::LoadOp::Load,
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
timestamp_writes: None,
});
let params_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None,
contents: bytes_of(&params),
usage: wgpu::BufferUsages::UNIFORM,
});
let bindings = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.pipeline_v.get_bind_group_layout(0),
entries: &[
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::TextureView(&intermediate),
},
wgpu::BindGroupEntry {
binding: 2,
resource: params_buf.as_entire_binding(),
},
],
});
pass.set_pipeline(&self.pipeline_v);
pass.set_vertex_buffer(0, self.view_buf.slice(..));
pass.set_bind_group(0, &bindings, &[]);
pass.draw(0..4, 0..1);
}
}
}

67
src/glare.wgsl Normal file
View File

@ -0,0 +1,67 @@
struct Params {
sigma: f32,
}
struct Vertex {
@location(0) screen: vec2f,
}
struct Varying {
@location(0) tex: vec2f,
@builtin(position) v: vec4f,
}
@group(0) @binding(1) var tex: texture_2d<f32>;
@group(0) @binding(2) var<uniform> params: Params;
@vertex
fn on_vertex(in: Vertex) -> Varying {
let uv = in.screen;
let xy = vec2(2. * uv.x - 1., 1. - 2. * uv.y);
return Varying(uv, vec4(xy, 0., 1.));
}
const CUTOFF: i32 = 50;
@fragment
fn pass_h(in: Varying) -> @location(0) vec4f {
let size = vec2i(textureDimensions(tex));
let pix = vec2i(floor(in.tex * vec2f(size)));
let a = max(0, pix.x - CUTOFF);
let b = min(size.x, pix.x + CUTOFF + 1);
var accum = vec4(0.);
for (var x = a; x < b; x++) {
let w = weight(x - pix.x);
let pixel = textureLoad(tex, vec2i(x, pix.y), 0);
if (all(pixel >= vec4(0.))) { // excludes NaNs
accum += w * pixel;
}
}
// accum.x = textureLoad(tex, pix, 0).x;
// accum.y = textureLoad(tex, pix, 0).y;
return vec4(accum.xyz, params.sigma);
}
@fragment
fn pass_v(in: Varying) -> @location(0) vec4f {
let size = vec2i(textureDimensions(tex));
let pix = vec2i(floor(in.tex * vec2f(size)));
let a = max(0, pix.y - CUTOFF);
let b = min(size.y, pix.y + CUTOFF + 1);
var accum = vec4(0.);
for (var y = a; y < b; y++) {
let w = weight(y - pix.y);
let pixel = textureLoad(tex, vec2i(pix.x, y), 0);
if (all(pixel >= vec4(0.))) { // excludes NaNs
accum += w * pixel;
}
}
// accum.x = textureLoad(tex, pix, 0).x;
return vec4(accum.xyz, params.sigma);
}
fn weight(dist: i32) -> f32 {
let σ = params.sigma / 1e4;
let d = f32(dist) / σ;
return 1. / (1. + d*d);
}

View File

@ -1,4 +1,5 @@
pub mod anim; pub mod anim;
pub mod glare;
pub mod perlin; pub mod perlin;
pub mod present; pub mod present;
pub mod scene; pub mod scene;

View File

@ -24,7 +24,7 @@ fn on_vertex(in: Vertex) -> Varying {
@fragment @fragment
fn on_fragment(in: Varying) -> @location(0) vec4f { fn on_fragment(in: Varying) -> @location(0) vec4f {
let pixel = textureSample(tex, smp, in.tex) / params.divisor; let pixel = .1 * textureSample(tex, smp, in.tex) / params.divisor;
return vec4(rational_tone_map(pixel.xyz), 1.0); return vec4(rational_tone_map(pixel.xyz), 1.0);
} }

View File

@ -105,7 +105,7 @@ impl Renderer {
trace::Params { trace::Params {
max_reflections: 3, max_reflections: 3,
min_strength: 0.1, min_strength: 0.1,
sphere_count: N_SPHERES, sphere_count: scene.spheres.len() as u32,
seed, seed,
}, },
viewport, viewport,