Hovered and selected faces light solidly again over GPU-drawn bodies (#1064)
Since the model's mesh went back to the reference mesher (#940), the GPU viewport drew opaque bodies from their display plans while hover and selection lit faces with the reference mesh's triangles. Two triangulations of the same face at equal depth fought, so a hovered face shimmered in wedges instead of lighting whole. Faces lit over a body drawn from its plan now use the plan's own triangles (ModelBody::plan_mesh, evaluated once on the CPU), and the highlight pass gets a small depth bias toward the eye so f32 rounding between the GPU and CPU evaluations of the same triangles cannot speckle it either. Test: every face of a box with a pocket and a round boss, hovered and rendered offscreen through the viewport's real pipelines on the GPU path, lights the same pixels as on the CPU path (skipped without a GPU adapter). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b5efde94c8
commit
75f1cdd4c9
3 files changed
+162
-22
No files matched your search
@@ -642,7 +642,7 @@ impl Resources {
|
||||
access_bgl,
|
||||
access_bind,
|
||||
access_version: 0,
|
||||
tri_hl: pipeline("sc_tris_hl", "vs_tri", "fs_tri", tri_layout(), depth(wgpu::CompareFunction::LessEqual, false), alpha),
|
||||
tri_hl: pipeline("sc_tris_hl", "vs_tri", "fs_tri", tri_layout(), highlight_depth(t.depth), alpha),
|
||||
trans: pipeline("sc_trans", "vs_tri", "fs_tri", tri_layout(), depth(wgpu::CompareFunction::LessEqual, false), alpha),
|
||||
glass_far: glass("sc_glass_far", wgpu::Face::Front),
|
||||
glass_near: glass("sc_glass_near", wgpu::Face::Back),
|
||||
@@ -662,6 +662,24 @@ impl Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// Depth state of faces lit over the model (hover, selection): tested, not written, and pulled a
|
||||
/// little toward the eye. They lie on the model's own faces, but the GPU evaluates a body's plan
|
||||
/// in f32 while the highlight's copy of the same triangles is evaluated on the CPU, so the two
|
||||
/// depths differ by rounding and an unbiased equal test lights the face in speckles and wedges.
|
||||
/// The bias is a few depth steps plus the face's own slope: far less than any real gap.
|
||||
fn highlight_depth(format: Option<wgpu::TextureFormat>) -> Option<wgpu::DepthStencilState> {
|
||||
format.map(|format| wgpu::DepthStencilState {
|
||||
format,
|
||||
depth_write_enabled: Some(false),
|
||||
depth_compare: Some(wgpu::CompareFunction::LessEqual),
|
||||
stencil: Default::default(),
|
||||
bias: HIGHLIGHT_BIAS,
|
||||
})
|
||||
}
|
||||
|
||||
/// See [`highlight_depth`]. Negative: toward the eye (depth grows away from it).
|
||||
const HIGHLIGHT_BIAS: wgpu::DepthBiasState = wgpu::DepthBiasState { constant: -8, slope_scale: -1.0, clamp: 0.0 };
|
||||
|
||||
fn upload(device: &wgpu::Device, label: &str, bytes: &[u8], stride: usize) -> Option<Batch> {
|
||||
upload_with(device, label, bytes, stride, wgpu::BufferUsages::VERTEX)
|
||||
}
|
||||
@@ -895,8 +913,17 @@ impl egui_wgpu::CallbackTrait for ViewportCallback {
|
||||
/// Render `scene` offscreen on a device of its own, through the viewport's real resources and
|
||||
/// frame callback (`prepare`, then `paint`), and read the pixels back: straight RGBA, `size` ×
|
||||
/// `size`. `None` without a GPU adapter. With `tess`, planned bodies are evaluated on the GPU.
|
||||
/// `highlight` is drawn as the viewport's highlight scene (hover and selection).
|
||||
#[cfg(all(test, not(target_arch = "wasm32")))]
|
||||
pub(crate) fn render_offscreen(scene: GpuScene, view_proj: [[f32; 4]; 4], back: [f32; 3], eye: [f32; 4], size: u32, tess: bool) -> Option<Vec<u8>> {
|
||||
pub(crate) fn render_offscreen(
|
||||
scene: GpuScene,
|
||||
highlight: Option<GpuScene>,
|
||||
view_proj: [[f32; 4]; 4],
|
||||
back: [f32; 3],
|
||||
eye: [f32; 4],
|
||||
size: u32,
|
||||
tess: bool,
|
||||
) -> Option<Vec<u8>> {
|
||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle());
|
||||
let adapter = pollster::block_on(instance.request_adapter(&Default::default())).ok()?;
|
||||
let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
|
||||
@@ -915,8 +942,8 @@ pub(crate) fn render_offscreen(scene: GpuScene, view_proj: [[f32; 4]; 4], back:
|
||||
slot: Arc::new(Mutex::new(Some(scene))),
|
||||
sketch_key: 0,
|
||||
sketch_slot: none(),
|
||||
hl_key: 0,
|
||||
hl_slot: none(),
|
||||
hl_key: u64::from(highlight.is_some()),
|
||||
hl_slot: Arc::new(Mutex::new(highlight)),
|
||||
pv_key: 0,
|
||||
pv_slot: none(),
|
||||
view_proj,
|
||||
|
||||
Reference in new issue
Block a user