feat(render): add scene environment pipeline
This commit is contained in:
parent
b667eeeb05
commit
c45daeffb6
17 changed files with 2236 additions and 213 deletions
|
|
@ -612,7 +612,7 @@ pub fn create_material_bind_group(
|
|||
[
|
||||
material.advanced_data_present as u32,
|
||||
material.is_anonymous as u32,
|
||||
0,
|
||||
material.handle.is_some() as u32,
|
||||
0,
|
||||
]
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ use device_capabilities::DeviceCapabilities;
|
|||
|
||||
/// MSAA sample count for the main drawing pipelines.
|
||||
const MSAA_SAMPLES: u32 = 4;
|
||||
const SHADOW_MAP_SIZE: u32 = 2048;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum MeshHighlightKind {
|
||||
|
|
@ -74,6 +75,8 @@ struct MeshCullUniform {
|
|||
}
|
||||
|
||||
pub struct Pipeline {
|
||||
background_pipeline: wgpu::RenderPipeline,
|
||||
shadow_pipeline: wgpu::RenderPipeline,
|
||||
wire_pipeline: wgpu::RenderPipeline,
|
||||
/// Stamps clip-boundary polygons into the stencil buffer (viewports + XCLIP).
|
||||
clip_mask_pipeline: wgpu::RenderPipeline,
|
||||
|
|
@ -130,7 +133,18 @@ pub struct Pipeline {
|
|||
/// writes depth). Paired with `mesh_depth_pipeline` for HiddenLine.
|
||||
face3d_depth_pipeline: wgpu::RenderPipeline,
|
||||
uniform_buffer: wgpu::Buffer,
|
||||
frame_bgl: wgpu::BindGroupLayout,
|
||||
uniform_bind_group: wgpu::BindGroup,
|
||||
shadow_uniform_bind_group: wgpu::BindGroup,
|
||||
background_texture: wgpu::Texture,
|
||||
environment_texture: wgpu::Texture,
|
||||
background_sampler: wgpu::Sampler,
|
||||
background_source_id: usize,
|
||||
environment_source_id: usize,
|
||||
_shadow_texture: wgpu::Texture,
|
||||
shadow_view: wgpu::TextureView,
|
||||
shadow_sampler: wgpu::Sampler,
|
||||
shadow_enabled: bool,
|
||||
wipeout_bgl1: wgpu::BindGroupLayout,
|
||||
image_bgl1: wgpu::BindGroupLayout,
|
||||
/// Group-1 layout for the text pipeline (atlas texture + sampler).
|
||||
|
|
@ -347,26 +361,198 @@ impl Pipeline {
|
|||
// Bind group layout 0 — shared by wire and hatch pipelines.
|
||||
let frame_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
label: Some("viewer.frame_bgl"),
|
||||
entries: &[wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
multisampled: false,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 2,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 3,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
multisampled: false,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 4,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 5,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
sample_type: wgpu::TextureSampleType::Depth,
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
multisampled: false,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 6,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
});
|
||||
let shadow_frame_bgl = device.create_bind_group_layout(
|
||||
&wgpu::BindGroupLayoutDescriptor {
|
||||
label: Some("shadow.frame_bgl"),
|
||||
entries: &[wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::VERTEX,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
},
|
||||
);
|
||||
|
||||
let placeholder_texture = |label: &'static str, rgba: [u8; 4]| {
|
||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some(label),
|
||||
size: wgpu::Extent3d {
|
||||
width: 1,
|
||||
height: 1,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||
view_formats: &[],
|
||||
});
|
||||
queue.write_texture(
|
||||
texture.as_image_copy(),
|
||||
&rgba,
|
||||
wgpu::TexelCopyBufferLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: Some(4),
|
||||
rows_per_image: Some(1),
|
||||
},
|
||||
wgpu::Extent3d {
|
||||
width: 1,
|
||||
height: 1,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
);
|
||||
texture
|
||||
};
|
||||
let background_texture = placeholder_texture("background.placeholder", [0, 0, 0, 255]);
|
||||
let environment_texture =
|
||||
placeholder_texture("environment.placeholder", [128, 128, 128, 255]);
|
||||
let background_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
label: Some("background.sampler"),
|
||||
address_mode_u: wgpu::AddressMode::Repeat,
|
||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||
mag_filter: wgpu::FilterMode::Linear,
|
||||
min_filter: wgpu::FilterMode::Linear,
|
||||
mipmap_filter: wgpu::MipmapFilterMode::Linear,
|
||||
..Default::default()
|
||||
});
|
||||
let shadow_texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("shadow.texture"),
|
||||
size: wgpu::Extent3d {
|
||||
width: SHADOW_MAP_SIZE,
|
||||
height: SHADOW_MAP_SIZE,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Depth32Float,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
|
||||
view_formats: &[],
|
||||
});
|
||||
let shadow_view = shadow_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let shadow_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
label: Some("shadow.sampler"),
|
||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||
compare: Some(wgpu::CompareFunction::LessEqual),
|
||||
mag_filter: wgpu::FilterMode::Linear,
|
||||
min_filter: wgpu::FilterMode::Linear,
|
||||
..Default::default()
|
||||
});
|
||||
let background_view = background_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let environment_view = environment_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("viewer.bind_group"),
|
||||
layout: &frame_bgl,
|
||||
entries: &[wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: uniform_buffer.as_entire_binding(),
|
||||
}],
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: uniform_buffer.as_entire_binding(),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::TextureView(&background_view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: wgpu::BindingResource::Sampler(&background_sampler),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 3,
|
||||
resource: wgpu::BindingResource::TextureView(&environment_view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 4,
|
||||
resource: wgpu::BindingResource::Sampler(&background_sampler),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 5,
|
||||
resource: wgpu::BindingResource::TextureView(&shadow_view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 6,
|
||||
resource: wgpu::BindingResource::Sampler(&shadow_sampler),
|
||||
},
|
||||
],
|
||||
});
|
||||
let shadow_uniform_bind_group = device.create_bind_group(
|
||||
&wgpu::BindGroupDescriptor {
|
||||
label: Some("shadow.frame_bind_group"),
|
||||
layout: &shadow_frame_bgl,
|
||||
entries: &[wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: uniform_buffer.as_entire_binding(),
|
||||
}],
|
||||
},
|
||||
);
|
||||
|
||||
// ── Wire pipeline ──────────────────────────────────────────────────
|
||||
// Select once from actual device limits. Any device whose compositor
|
||||
|
|
@ -424,6 +610,57 @@ impl Pipeline {
|
|||
write_mask: 0x00,
|
||||
};
|
||||
|
||||
let background_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("background.shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!(
|
||||
"../../shaders/background.wgsl"
|
||||
))),
|
||||
});
|
||||
let background_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("background.pipeline_layout"),
|
||||
bind_group_layouts: &[Some(&frame_bgl)],
|
||||
immediate_size: 0,
|
||||
});
|
||||
let background_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("background.pipeline"),
|
||||
layout: Some(&background_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &background_shader,
|
||||
entry_point: Some("vs_main"),
|
||||
buffers: &[],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
},
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
cull_mode: None,
|
||||
..Default::default()
|
||||
},
|
||||
depth_stencil: Some(wgpu::DepthStencilState {
|
||||
format: wgpu::TextureFormat::Depth24PlusStencil8,
|
||||
depth_write_enabled: Some(false),
|
||||
depth_compare: Some(wgpu::CompareFunction::Always),
|
||||
stencil: content_stencil.clone(),
|
||||
bias: wgpu::DepthBiasState::default(),
|
||||
}),
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: MSAA_SAMPLES,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &background_shader,
|
||||
entry_point: Some("fs_main"),
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format,
|
||||
blend: Some(wgpu::BlendState::REPLACE),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
}),
|
||||
multiview_mask: None,
|
||||
cache: None,
|
||||
});
|
||||
|
||||
let wire_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("wire.pipeline"),
|
||||
layout: Some(&wire_layout),
|
||||
|
|
@ -1033,6 +1270,55 @@ impl Pipeline {
|
|||
immediate_size: 0,
|
||||
});
|
||||
|
||||
let shadow_source = include_str!("../../shaders/shadow.wgsl");
|
||||
let shadow_source = if mesh_storage_instancing {
|
||||
std::borrow::Cow::Borrowed(shadow_source)
|
||||
} else {
|
||||
std::borrow::Cow::Owned(shadow_source.replace(
|
||||
"var<storage, read> mesh_instances: array<MeshInstance>;",
|
||||
"var<uniform> mesh_instances: array<MeshInstance, 1>;",
|
||||
))
|
||||
};
|
||||
let shadow_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("shadow.shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(shadow_source),
|
||||
});
|
||||
let shadow_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("shadow.pipeline_layout"),
|
||||
bind_group_layouts: &[&shadow_frame_bgl, &mesh_material_bgl].map(Some),
|
||||
immediate_size: 0,
|
||||
});
|
||||
let shadow_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("shadow.pipeline"),
|
||||
layout: Some(&shadow_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shadow_shader,
|
||||
entry_point: Some("vs_main"),
|
||||
buffers: &[mesh_gpu::MeshVertex::layout()],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
},
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
cull_mode: None,
|
||||
..Default::default()
|
||||
},
|
||||
depth_stencil: Some(wgpu::DepthStencilState {
|
||||
format: wgpu::TextureFormat::Depth32Float,
|
||||
depth_write_enabled: Some(true),
|
||||
depth_compare: Some(wgpu::CompareFunction::LessEqual),
|
||||
stencil: wgpu::StencilState::default(),
|
||||
bias: wgpu::DepthBiasState {
|
||||
constant: 2,
|
||||
slope_scale: 2.0,
|
||||
clamp: 0.0,
|
||||
},
|
||||
}),
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
fragment: None,
|
||||
multiview_mask: None,
|
||||
cache: None,
|
||||
});
|
||||
|
||||
let mesh_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("mesh.pipeline"),
|
||||
layout: Some(&mesh_layout),
|
||||
|
|
@ -1612,6 +1898,8 @@ impl Pipeline {
|
|||
});
|
||||
|
||||
Self {
|
||||
background_pipeline,
|
||||
shadow_pipeline,
|
||||
wire_pipeline,
|
||||
clip_mask_pipeline,
|
||||
wire_black_pipeline,
|
||||
|
|
@ -1654,7 +1942,18 @@ impl Pipeline {
|
|||
face3d_pipeline,
|
||||
face3d_depth_pipeline,
|
||||
uniform_buffer,
|
||||
frame_bgl,
|
||||
uniform_bind_group,
|
||||
shadow_uniform_bind_group,
|
||||
background_texture,
|
||||
environment_texture,
|
||||
background_sampler,
|
||||
background_source_id: 0,
|
||||
environment_source_id: 0,
|
||||
_shadow_texture: shadow_texture,
|
||||
shadow_view,
|
||||
shadow_sampler,
|
||||
shadow_enabled: false,
|
||||
wipeout_bgl1,
|
||||
image_bgl1,
|
||||
depth_texture_size: Size::new(1, 1),
|
||||
|
|
@ -2819,10 +3118,118 @@ impl Pipeline {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn upload_uniforms(&self, queue: &wgpu::Queue, uniforms: &Uniforms) {
|
||||
pub fn upload_uniforms(&mut self, queue: &wgpu::Queue, uniforms: &Uniforms) {
|
||||
self.shadow_enabled = uniforms.shadow_params[0] > 0.5;
|
||||
queue.write_buffer(&self.uniform_buffer, 0, bytemuck::bytes_of(uniforms));
|
||||
}
|
||||
|
||||
pub fn upload_background_images(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
background: Option<&crate::scene::model::image_model::DecodedImage>,
|
||||
environment: Option<&crate::scene::model::image_model::DecodedImage>,
|
||||
) {
|
||||
let source_id = |image: Option<&crate::scene::model::image_model::DecodedImage>| {
|
||||
image
|
||||
.map(|image| std::sync::Arc::as_ptr(&image.pixels) as usize)
|
||||
.unwrap_or(0)
|
||||
};
|
||||
let background_id = source_id(background);
|
||||
let environment_id = source_id(environment);
|
||||
if background_id == self.background_source_id
|
||||
&& environment_id == self.environment_source_id
|
||||
{
|
||||
return;
|
||||
}
|
||||
let upload = |
|
||||
label: &'static str,
|
||||
image: Option<&crate::scene::model::image_model::DecodedImage>,
|
||||
fallback: [u8; 4],
|
||||
| {
|
||||
let (width, height, pixels) = image
|
||||
.map(|image| (image.width, image.height, image.pixels.as_slice()))
|
||||
.unwrap_or((1, 1, fallback.as_slice()));
|
||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some(label),
|
||||
size: wgpu::Extent3d {
|
||||
width: width.max(1),
|
||||
height: height.max(1),
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||
view_formats: &[],
|
||||
});
|
||||
queue.write_texture(
|
||||
texture.as_image_copy(),
|
||||
pixels,
|
||||
wgpu::TexelCopyBufferLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: Some(4 * width.max(1)),
|
||||
rows_per_image: Some(height.max(1)),
|
||||
},
|
||||
wgpu::Extent3d {
|
||||
width: width.max(1),
|
||||
height: height.max(1),
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
);
|
||||
texture
|
||||
};
|
||||
self.background_texture = upload("background.texture", background, [0, 0, 0, 255]);
|
||||
self.environment_texture = upload(
|
||||
"environment.texture",
|
||||
environment,
|
||||
[128, 128, 128, 255],
|
||||
);
|
||||
let background_view = self
|
||||
.background_texture
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let environment_view = self
|
||||
.environment_texture
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
self.uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("viewer.bind_group"),
|
||||
layout: &self.frame_bgl,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: self.uniform_buffer.as_entire_binding(),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::TextureView(&background_view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: wgpu::BindingResource::Sampler(&self.background_sampler),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 3,
|
||||
resource: wgpu::BindingResource::TextureView(&environment_view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 4,
|
||||
resource: wgpu::BindingResource::Sampler(&self.background_sampler),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 5,
|
||||
resource: wgpu::BindingResource::TextureView(&self.shadow_view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 6,
|
||||
resource: wgpu::BindingResource::Sampler(&self.shadow_sampler),
|
||||
},
|
||||
],
|
||||
});
|
||||
self.background_source_id = background_id;
|
||||
self.environment_source_id = environment_id;
|
||||
}
|
||||
|
||||
/// Write the blit shader's UV crop uniform. Call in `prepare` (the only
|
||||
/// place with a `&Queue`) — `render` then just submits the draw call.
|
||||
pub fn upload_blit_uv(&self, queue: &wgpu::Queue, uv_offset: [f32; 2], uv_scale: [f32; 2]) {
|
||||
|
|
@ -2869,11 +3276,15 @@ impl Pipeline {
|
|||
};
|
||||
let msaa = &self.msaa_view;
|
||||
let [r, g, b, a] = bg_color;
|
||||
let clear_color = wgpu::Color {
|
||||
r: r as f64,
|
||||
g: g as f64,
|
||||
b: b as f64,
|
||||
a: a as f64,
|
||||
let clear_color = if self.clip_boundary.is_some() {
|
||||
wgpu::Color::TRANSPARENT
|
||||
} else {
|
||||
wgpu::Color {
|
||||
r: r as f64,
|
||||
g: g as f64,
|
||||
b: b as f64,
|
||||
a: a as f64,
|
||||
}
|
||||
};
|
||||
// Non-rectangular viewport clip: the boundary is stamped into the
|
||||
// just-cleared (0x00) stencil with `Invert`, so an odd (interior)
|
||||
|
|
@ -2883,6 +3294,51 @@ impl Pipeline {
|
|||
// render rectangle does the clipping).
|
||||
let stencil_ref: u32 = if self.clip_boundary.is_some() { 0xFF } else { 0 };
|
||||
|
||||
if self.shadow_enabled && !self.skip_geometry && !mesh_wireframe {
|
||||
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("shadow.render_pass"),
|
||||
color_attachments: &[],
|
||||
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
||||
view: &self.shadow_view,
|
||||
depth_ops: Some(wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(1.0),
|
||||
store: wgpu::StoreOp::Store,
|
||||
}),
|
||||
stencil_ops: None,
|
||||
}),
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
multiview_mask: None,
|
||||
});
|
||||
pass.set_pipeline(&self.shadow_pipeline);
|
||||
pass.set_bind_group(0, &self.shadow_uniform_bind_group, &[]);
|
||||
for (_, chunk) in self.active_mesh_chunks_indexed() {
|
||||
if !chunk.visible {
|
||||
continue;
|
||||
}
|
||||
pass.set_bind_group(
|
||||
1,
|
||||
chunk
|
||||
.material_bind_group
|
||||
.as_ref()
|
||||
.unwrap_or(&self.mesh_default_material_bind_group),
|
||||
&[],
|
||||
);
|
||||
pass.set_vertex_buffer(0, chunk.vertex_buffer.slice(..));
|
||||
if chunk.index_count != 0 {
|
||||
pass.set_index_buffer(chunk.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
|
||||
pass.draw_indexed(0..chunk.index_count, 0, 0..chunk.instance_count);
|
||||
}
|
||||
if chunk.transp_index_count != 0 {
|
||||
pass.set_index_buffer(
|
||||
chunk.transp_index_buffer.slice(..),
|
||||
wgpu::IndexFormat::Uint32,
|
||||
);
|
||||
pass.draw_indexed(0..chunk.transp_index_count, 0, 0..chunk.instance_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scene-render cache: when `prepare` found this frame pixel-identical
|
||||
// to the last (unchanged view / geometry / selection / preview), the
|
||||
// resolve texture already holds the image. Skip every geometry pass +
|
||||
|
|
@ -2931,6 +3387,10 @@ impl Pipeline {
|
|||
pass.set_vertex_buffer(0, vbuf.slice(..));
|
||||
pass.draw(0..*vcount, 0..1);
|
||||
}
|
||||
pass.set_pipeline(&self.background_pipeline);
|
||||
pass.set_bind_group(0, &self.uniform_bind_group, &[]);
|
||||
pass.set_stencil_reference(stencil_ref);
|
||||
pass.draw(0..3, 0..1);
|
||||
// The capability-selected façade dispatches storage or texture
|
||||
// draws before wires so outlines remain on top in either backend.
|
||||
// Skipped while navigating because per-pixel hatch work dominates
|
||||
|
|
|
|||
|
|
@ -53,8 +53,51 @@ pub struct Uniforms {
|
|||
pub light_color_hotspot: [[f32; 4]; 4],
|
||||
/// x = attenuation mode, y/z = start/end limits, w = cosine of falloff.
|
||||
pub light_attenuation: [[f32; 4]; 4],
|
||||
/// x = active light count, y = fallback ambient strength.
|
||||
/// x = active light count, yzw = per-viewport ambient-light RGB.
|
||||
pub lighting: [f32; 4],
|
||||
/// x = viewport face-colour mode, y = viewport face opacity,
|
||||
/// z = highlight intensity for faces without an attached material,
|
||||
/// w = visual-style brightness.
|
||||
pub visual_style: [f32; 4],
|
||||
/// Viewport visual-style mono/tint colour.
|
||||
pub visual_style_color: [f32; 4],
|
||||
/// RGB background selected by the viewport; alpha is one when a drawing
|
||||
/// background object, rather than the application canvas, supplies it.
|
||||
pub viewport_background: [f32; 4],
|
||||
/// x/y = normalized linear brightness/contrast in the documented
|
||||
/// -10..10 viewport range; z/w are reserved for exposure controls.
|
||||
pub view_tone: [f32; 4],
|
||||
pub background_top: [f32; 4],
|
||||
pub background_middle: [f32; 4],
|
||||
pub background_bottom: [f32; 4],
|
||||
pub background_aux0: [f32; 4],
|
||||
pub background_aux1: [f32; 4],
|
||||
/// x = mode, y = horizon, z = height, w = rotation in radians.
|
||||
pub background_params: [f32; 4],
|
||||
/// x = fit, y = keep aspect, z = tile, w = decoded image aspect.
|
||||
pub background_image_params: [f32; 4],
|
||||
/// xy = image offset, zw = image scale.
|
||||
pub background_image_transform: [f32; 4],
|
||||
/// x = environment enabled, y = rotation, z = diffuse strength,
|
||||
/// w = reflection strength.
|
||||
pub environment_params: [f32; 4],
|
||||
/// View-to-world rotation used to derive environment lookup directions.
|
||||
pub environment_view: glam::Mat4,
|
||||
/// Eye-relative light projection for the first shadow-casting source.
|
||||
pub shadow_view_proj: glam::Mat4,
|
||||
/// x = enabled, y = depth bias, z = softness, w = light index.
|
||||
pub shadow_params: [f32; 4],
|
||||
/// RGB = document render-environment fog colour.
|
||||
pub fog_color: [f32; 4],
|
||||
/// x = enabled, y = affect background, z/w = near/far density.
|
||||
pub fog_params: [f32; 4],
|
||||
/// x/y = near/far eye distance used to interpolate the density.
|
||||
pub fog_distances: [f32; 4],
|
||||
/// Eight normalized vertical-candela samples for each photometric light.
|
||||
pub light_web_profile_a: [[f32; 4]; 4],
|
||||
pub light_web_profile_b: [[f32; 4]; 4],
|
||||
/// xyz = web rotation, w = profile enabled.
|
||||
pub light_web_rotation: [[f32; 4]; 4],
|
||||
}
|
||||
|
||||
impl Uniforms {
|
||||
|
|
@ -83,7 +126,29 @@ impl Uniforms {
|
|||
light_direction_intensity: [[0.0; 4]; 4],
|
||||
light_color_hotspot: [[0.0; 4]; 4],
|
||||
light_attenuation: [[0.0; 4]; 4],
|
||||
lighting: [0.0, 0.18, 0.0, 0.0],
|
||||
lighting: [0.0, 0.18, 0.18, 0.18],
|
||||
visual_style: [1.0, 1.0, -1.0, 0.0],
|
||||
visual_style_color: [1.0, 1.0, 1.0, 1.0],
|
||||
viewport_background: [0.0, 0.0, 0.0, 0.0],
|
||||
view_tone: [0.0; 4],
|
||||
background_top: [0.0; 4],
|
||||
background_middle: [0.0; 4],
|
||||
background_bottom: [0.0; 4],
|
||||
background_aux0: [0.0; 4],
|
||||
background_aux1: [0.0; 4],
|
||||
background_params: [0.0; 4],
|
||||
background_image_params: [0.0; 4],
|
||||
background_image_transform: [0.0, 0.0, 1.0, 1.0],
|
||||
environment_params: [0.0, 0.0, 0.25, 0.35],
|
||||
environment_view: glam::Mat4::IDENTITY,
|
||||
shadow_view_proj: glam::Mat4::IDENTITY,
|
||||
shadow_params: [0.0, 0.0015, 1.0, 0.0],
|
||||
fog_color: [0.0, 0.0, 0.0, 1.0],
|
||||
fog_params: [0.0; 4],
|
||||
fog_distances: [0.0, 1.0, 0.0, 0.0],
|
||||
light_web_profile_a: [[1.0; 4]; 4],
|
||||
light_web_profile_b: [[1.0; 4]; 4],
|
||||
light_web_rotation: [[0.0; 4]; 4],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue