Compare commits

...

4 Commits

Author SHA1 Message Date
c1b505356c Keep opacity independent of direction 2024-11-17 20:14:04 +03:00
30f02e4ff2 Custom alpha-to-coverage 2024-11-17 20:03:34 +03:00
f00f5f2f77 Depth + basic sample-to-coverage 2024-11-17 19:05:43 +03:00
03e91fac28 draw tube as translucent solid 2024-11-17 18:53:58 +03:00
5 changed files with 372 additions and 64 deletions

View File

@ -10,6 +10,7 @@ use winit::{
mod camera; mod camera;
mod lines; mod lines;
mod meshes;
mod scene; mod scene;
mod viewport; mod viewport;
@ -18,11 +19,17 @@ mod viewport;
// * Y: left // * Y: left
// * Z: up // * Z: up
fn prepare_scene(device: &wgpu::Device) -> Vec<lines::Line> { fn prepare_scene(device: &wgpu::Device) -> (Vec<meshes::Mesh>, Vec<lines::Line>) {
scene::build() let (meshes, lines) = scene::build();
let meshes = meshes
.into_iter()
.map(|mesh| meshes::Mesh::new_list(device, meshes::Attrs { color: mesh.color }, mesh.tris))
.collect();
let lines = lines
.into_iter() .into_iter()
.map(|line| lines::Line::new_strip(device, lines::Attrs { color: line.color }, line.pts)) .map(|line| lines::Line::new_strip(device, lines::Attrs { color: line.color }, line.pts))
.collect() .collect();
(meshes, lines)
} }
#[cfg(any())] #[cfg(any())]
@ -160,8 +167,10 @@ struct State<'a> {
cam_loc: camctl::CameraLocation, cam_loc: camctl::CameraLocation,
cam_obj: camera::Camera, cam_obj: camera::Camera,
line_rend: lines::LineRenderer, line_rend: lines::LineRenderer,
mesh_rend: meshes::Renderer,
scene: Vec<lines::Line>, lines: Vec<lines::Line>,
meshes: Vec<meshes::Mesh>,
window: &'a Window, window: &'a Window,
} }
@ -206,17 +215,43 @@ impl<'a> State<'a> {
let cam_loc = camctl::CameraLocation::new(); let cam_loc = camctl::CameraLocation::new();
let t1 = Instant::now(); let t1 = Instant::now();
let depth = None; let cam_obj = camera::Camera::new(&device);
let msaa = wgpu::MultisampleState { let line_rend = lines::LineRenderer::new(
&device,
cam_obj.bind_group_layout(),
viewport.format(),
Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth24Plus,
depth_write_enabled: false,
depth_compare: wgpu::CompareFunction::LessEqual,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),
wgpu::MultisampleState {
count: viewport.sample_count(), count: viewport.sample_count(),
mask: !0, mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}; },
);
let mesh_rend = meshes::Renderer::new(
&device,
cam_obj.bind_group_layout(),
viewport.format(),
Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth24Plus,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::LessEqual,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),
wgpu::MultisampleState {
count: viewport.sample_count(),
mask: !0,
alpha_to_coverage_enabled: false,
},
);
let cam_obj = camera::Camera::new(&device); let (meshes, lines) = prepare_scene(&device);
let line_rend = lines::LineRenderer::new(&device, cam_obj.bind_group_layout(), viewport.format(), depth, msaa);
let scene = prepare_scene(&device);
let fps = fps::Counter::new(); let fps = fps::Counter::new();
@ -225,12 +260,14 @@ impl<'a> State<'a> {
queue, queue,
viewport, viewport,
line_rend, line_rend,
mesh_rend,
kbd, kbd,
fps, fps,
cam_loc, cam_loc,
cam_obj, cam_obj,
t1, t1,
scene, lines,
meshes,
window, window,
} }
} }
@ -266,8 +303,10 @@ impl<'a> State<'a> {
.set_title(&format!("Space Refraction ({:.1} FPS)", self.fps.get())); .set_title(&format!("Space Refraction ({:.1} FPS)", self.fps.get()));
self.viewport self.viewport
.render_single_pass(&self.device, &self.queue, |mut render_pass| { .render_single_pass(&self.device, &self.queue, |mut render_pass| {
self.mesh_rend
.render(&mut render_pass, self.cam_obj.bind_group(), self.meshes.iter());
self.line_rend self.line_rend
.render(&mut render_pass, self.cam_obj.bind_group(), self.scene.iter()); .render(&mut render_pass, self.cam_obj.bind_group(), self.lines.iter());
}) })
} }
} }

View File

@ -0,0 +1,63 @@
struct CameraUniform {
mvp: mat4x4<f32>,
scale: vec2<f32>,
}
@group(0) @binding(0)
var<uniform> camera: CameraUniform;
struct MeshUniform {
color: vec4<f32>,
}
var<push_constant> mesh: MeshUniform;
struct VertexInput {
@location(0) position: vec3<f32>,
@location(1) normal: vec3<f32>,
}
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) vertex_color: vec4<f32>,
}
struct FragmentOutput {
@location(0) color: vec4<f32>,
@builtin(sample_mask) mask: u32,
}
fn hash(key : u32) -> u32 {
var v = key;
v *= 0xb384af1bu;
v ^= v >> 15u;
return v;
}
@vertex
fn vs_main(ver: VertexInput) -> VertexOutput {
var out: VertexOutput;
var light = dot(ver.normal, normalize(vec3(1., 1., 1.)));
light = .7 + .3 * light;
out.vertex_color = vec4(light * mesh.color.xyz, mesh.color.w);
out.clip_position = camera.mvp * vec4(ver.position, 1.);
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> FragmentOutput {
var out: FragmentOutput;
out.color = vec4(in.vertex_color.xyz, 1.);
var x = bitcast<u32>(in.clip_position.x);
var y = bitcast<u32>(in.clip_position.y);
var z = bitcast<u32>(in.clip_position.z);
var alpha = in.vertex_color.w;
var seed = hash(hash(hash(x) ^ y) ^ z);
var mask = 0u;
for (var sample = 0u; sample < 8u; sample++) {
var threshold = f32(hash(seed ^ sample)) / 0x1p32;
if (alpha > threshold) {
mask |= 1u << sample;
}
}
out.mask = mask;
return out;
}

168
src/bin/wireframe/meshes.rs Normal file
View File

@ -0,0 +1,168 @@
use std::mem;
use bytemuck::{bytes_of, cast_slice, Pod, Zeroable};
use glam::{Vec3, Vec4};
use wgpu::util::DeviceExt as _;
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
struct Vertex {
pub position: [f32; 3],
pub normal: [f32; 3],
}
#[repr(C)]
#[derive(Copy, Clone, Pod, Zeroable)]
struct PushConsts {
pub color: [f32; 4],
}
#[derive(Copy, Clone)]
pub struct Attrs {
pub color: Vec4,
}
impl Attrs {
fn consts(&self) -> PushConsts {
PushConsts {
color: self.color.to_array(),
}
}
}
pub struct Mesh {
consts: PushConsts,
npoints: u32,
buf: wgpu::Buffer,
}
impl Mesh {
pub fn new_list(device: &wgpu::Device, attrs: Attrs, tris: Vec<(Vec3, Vec3, Vec3)>) -> Self {
let data: Vec<Vertex> = tris
.into_iter()
.flat_map(|(a, b, c)| {
let n = (b - a).cross(c - a).normalize();
[
Vertex {
position: a.into(),
normal: n.into(),
},
Vertex {
position: b.into(),
normal: n.into(),
},
Vertex {
position: c.into(),
normal: n.into(),
},
]
})
.collect();
let buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Mesh Vertex Buffer"),
contents: cast_slice(&data),
usage: wgpu::BufferUsages::VERTEX,
});
Mesh {
consts: attrs.consts(),
npoints: data.len() as u32,
buf,
}
}
}
pub struct Renderer {
pipeline: wgpu::RenderPipeline,
}
static SHADER: &'static str = include_str!("mesh.wgsl");
impl Renderer {
pub fn new(
device: &wgpu::Device,
cam_layout: &wgpu::BindGroupLayout,
target_format: wgpu::TextureFormat,
depth_stencil: Option<wgpu::DepthStencilState>,
multisample: wgpu::MultisampleState,
) -> Renderer {
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Mesh Shader"),
source: wgpu::ShaderSource::Wgsl(SHADER.into()),
});
let consts_range = wgpu::PushConstantRange {
stages: wgpu::ShaderStages::VERTEX,
range: 0..mem::size_of::<PushConsts>() as u32,
};
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Mesh RenderPipelineLayout"),
bind_group_layouts: &[cam_layout],
push_constant_ranges: &[consts_range],
});
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Mesh RenderPipeline"),
layout: Some(&layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &[wgpu::VertexBufferLayout {
array_stride: mem::size_of::<Vertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[
wgpu::VertexAttribute {
offset: mem::offset_of!(Vertex, position) as u64,
shader_location: 0,
format: wgpu::VertexFormat::Float32x3,
},
wgpu::VertexAttribute {
offset: mem::offset_of!(Vertex, normal) as u64,
shader_location: 1,
format: wgpu::VertexFormat::Float32x3,
},
],
}],
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &[Some(wgpu::ColorTargetState {
format: target_format,
blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent::OVER,
alpha: wgpu::BlendComponent::OVER,
}),
write_mask: wgpu::ColorWrites::ALL,
})],
compilation_options: Default::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
..Default::default()
},
depth_stencil,
multisample,
multiview: None,
cache: None,
});
Renderer { pipeline }
}
pub fn render<'a>(
&self,
pass: &mut wgpu::RenderPass,
cam_bind: &wgpu::BindGroup,
meshes: impl Iterator<Item = &'a Mesh>,
) {
pass.set_pipeline(&self.pipeline);
pass.set_bind_group(0, cam_bind, &[]);
for mesh in meshes {
pass.set_push_constants(wgpu::ShaderStages::VERTEX, 0, bytes_of(&mesh.consts));
pass.set_vertex_buffer(0, mesh.buf.slice(..));
pass.draw(0..mesh.npoints, 0..1);
}
}
}

View File

@ -1,5 +1,5 @@
use glam::*; use glam::*;
use itertools::{chain, iproduct}; use itertools::chain;
use refraction::ifaces::{DebugTraceable, Traceable}; use refraction::ifaces::{DebugTraceable, Traceable};
use refraction::tube::metric::Tube; use refraction::tube::metric::Tube;
@ -17,6 +17,17 @@ pub struct FancyLine {
pub pts: Vec<Ray>, pub pts: Vec<Ray>,
} }
pub type Face = (Vec3, Vec3, Vec3);
pub enum Mesh {
List(Vec<Face>),
}
pub struct FancyMesh {
pub color: Vec4,
pub tris: Vec<Face>,
}
fn paint(onto: &mut Vec<FancyLine>, color: Vec3, lines: Vec<Line>) { fn paint(onto: &mut Vec<FancyLine>, color: Vec3, lines: Vec<Line>) {
onto.extend(lines.into_iter().map(move |line| FancyLine { onto.extend(lines.into_iter().map(move |line| FancyLine {
color, color,
@ -35,29 +46,6 @@ fn draw_line(a: Vec3, b: Vec3) -> Line {
Line::Strip(vec![Ray { pos: a, dir }, Ray { pos: b, dir }]) Line::Strip(vec![Ray { pos: a, dir }, Ray { pos: b, dir }])
} }
fn draw_rect(center: Vec3, u: Vec3, v: Vec3) -> Vec<Line> {
let a = center - u - v;
let b = center + u - v;
let c = center + u + v;
let d = center - u + v;
vec![draw_line(a, b), draw_line(b, c), draw_line(c, d), draw_line(d, a)]
}
fn draw_ellipse(center: Vec3, u: Vec3, v: Vec3) -> Line {
let segments = 47;
let step = 2. * std::f32::consts::PI / segments as f32;
Line::Loop(
(0..segments)
.map(|k| k as f32 * step)
.map(Vec2::from_angle)
.map(|d| Ray {
pos: center + d.x * u + d.y * v,
dir: -d.y * u + d.x * v,
})
.collect(),
)
}
fn draw_mark(pos: Vec3) -> Vec<Line> { fn draw_mark(pos: Vec3) -> Vec<Line> {
[ [
vec3(1., 1., 1.), vec3(1., 1., 1.),
@ -70,7 +58,7 @@ fn draw_mark(pos: Vec3) -> Vec<Line> {
.collect() .collect()
} }
pub fn build() -> Vec<FancyLine> { pub fn build() -> (Vec<FancyMesh>, Vec<FancyLine>) {
let tube = Tube { let tube = Tube {
inner_radius: 30.0, inner_radius: 30.0,
outer_radius: 50.0, outer_radius: 50.0,
@ -110,7 +98,13 @@ pub fn build() -> Vec<FancyLine> {
); );
let mut gc = vec![]; let mut gc = vec![];
paint(&mut gc, vec3(0.6, 0.6, 0.6), tube.render()); gc.push(FancyMesh {
color: vec4(0.10, 0.12, 0.15, 0.8),
tris: tube.render(),
});
let meshes = gc;
let mut gc = vec![];
paint(&mut gc, vec3(0.0, 0.6, 1.0), draw_fan_2(&space, cam3, vec3(0., 1., 0.))); paint(&mut gc, vec3(0.0, 0.6, 1.0), draw_fan_2(&space, cam3, vec3(0., 1., 0.)));
paint(&mut gc, vec3(0.2, 1.0, 0.0), draw_fan_2(&space, cam2, vec3(0., 1., 0.))); paint(&mut gc, vec3(0.2, 1.0, 0.0), draw_fan_2(&space, cam2, vec3(0., 1., 0.)));
paint( paint(
@ -119,7 +113,9 @@ pub fn build() -> Vec<FancyLine> {
draw_fan_2(&space, cam2l, vec3(0., 0., 1.)), draw_fan_2(&space, cam2l, vec3(0., 0., 1.)),
); );
paint(&mut gc, vec3(1.0, 0.2, 0.0), draw_fan_2(&space, cam1, vec3(0., 1., 0.))); paint(&mut gc, vec3(1.0, 0.2, 0.0), draw_fan_2(&space, cam1, vec3(0., 1., 0.)));
gc let lines = gc;
(meshes, lines)
} }
fn draw_ray_2(gc: &mut Vec<Line>, space: &Space, camera: Location, dir: Vec3) { fn draw_ray_2(gc: &mut Vec<Line>, space: &Space, camera: Location, dir: Vec3) {
@ -153,23 +149,42 @@ fn draw_fan_2(space: &Space, camera: Location, spread: Vec3) -> Vec<Line> {
} }
trait Renderable { trait Renderable {
fn render(&self) -> Vec<Line>; fn render(&self) -> Vec<Face>;
} }
impl Renderable for Tube { impl Renderable for Tube {
fn render(&self) -> Vec<Line> { fn render(&self) -> Vec<Face> {
let lines = 4; let sides = 42;
let step = 2. * std::f32::consts::PI / lines as f32; let step = 2. * std::f32::consts::PI / sides as f32;
let r = 0.5 * (self.outer_radius + self.inner_radius); let dir = |k| {
let w = 0.5 * (self.outer_radius - self.inner_radius); let d = Vec2::from_angle(k as f32 * step);
let l = vec3(0., self.external_halflength, 0.); vec3(d.x, 0., d.y)
let along = (0..lines) };
.map(|k| k as f32 * step) let side = vec3(0., self.external_halflength, 0.);
.map(Vec2::from_angle) let r1 = self.inner_radius;
.map(|d| vec3(d.x, 0., d.y)) let r2 = self.outer_radius;
.flat_map(|d| draw_rect(r * d, w * d, l)); let inner = (0..sides).flat_map(|k| {
let caps = iproduct!([self.inner_radius, self.outer_radius], [-l, l]) let a = r1 * dir(k);
.map(|(r, l)| draw_ellipse(l, vec3(r, 0., 0.), vec3(0., 0., r))); let b = r1 * dir(k + 1);
chain!(along, caps).collect() [(a - side, b - side, a + side), (b - side, b + side, a + side)]
});
let outer = (0..sides).flat_map(|k| {
let a = r2 * dir(k);
let b = r2 * dir(k + 1);
[(a - side, b - side, a + side), (b - side, b + side, a + side)]
});
let cap = (0..sides).flat_map(|k| {
let a = r1 * dir(k);
let b = r1 * dir(k + 1);
let c = r2 * dir(k + 1);
let d = r2 * dir(k);
[
(a - side, b - side, c - side),
(a - side, c - side, d - side),
(a + side, b + side, c + side),
(a + side, c + side, d + side),
]
});
chain!(inner, outer, cap).collect()
} }
} }

View File

@ -75,7 +75,7 @@ impl<'a> Viewport<'a> {
let render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("RenderPass"), label: Some("RenderPass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment { color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &self.multisample.view, view: &self.multisample.color,
resolve_target: Some(&view), resolve_target: Some(&view),
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color { load: wgpu::LoadOp::Clear(wgpu::Color {
@ -87,7 +87,14 @@ impl<'a> Viewport<'a> {
store: wgpu::StoreOp::Store, store: wgpu::StoreOp::Store,
}, },
})], })],
depth_stencil_attachment: None, depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &self.multisample.depth,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.),
store: wgpu::StoreOp::Discard,
}),
stencil_ops: None,
}),
occlusion_query_set: None, occlusion_query_set: None,
timestamp_writes: None, timestamp_writes: None,
}); });
@ -99,13 +106,14 @@ impl<'a> Viewport<'a> {
} }
struct Multisample { struct Multisample {
view: wgpu::TextureView, color: wgpu::TextureView,
depth: wgpu::TextureView,
} }
impl Multisample { impl Multisample {
fn new(device: &wgpu::Device, format: wgpu::TextureFormat, size: UVec2, sample_count: u32) -> Multisample { fn new(device: &wgpu::Device, format: wgpu::TextureFormat, size: UVec2, sample_count: u32) -> Multisample {
let tex = device.create_texture(&wgpu::TextureDescriptor { let color = device.create_texture(&wgpu::TextureDescriptor {
label: Some("Multisample texture"), label: Some("Multisample color texture"),
size: wgpu::Extent3d { size: wgpu::Extent3d {
width: size.x, width: size.x,
height: size.y, height: size.y,
@ -118,7 +126,22 @@ impl Multisample {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT, usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[], view_formats: &[],
}); });
let view = tex.create_view(&wgpu::TextureViewDescriptor::default()); let color = color.create_view(&wgpu::TextureViewDescriptor::default());
Multisample { view } let depth = device.create_texture(&wgpu::TextureDescriptor {
label: Some("Multisample depth texture"),
size: wgpu::Extent3d {
width: size.x,
height: size.y,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Depth24Plus,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
let depth = depth.create_view(&wgpu::TextureViewDescriptor::default());
Multisample { color, depth }
} }
} }