Skip to main content

polyscope_render/engine/pipelines/
volume.rs

1//! Pipeline creation for volume visualization.
2//!
3//! Contains pipelines for:
4//! - Gridcube (volume grid scalar cube visualization)
5
6use std::num::NonZeroU64;
7
8use super::super::RenderEngine;
9
10impl RenderEngine {
11    /// Gets the gridcube bind group layout (for volume grid cube rendering).
12    pub fn gridcube_bind_group_layout(&self) -> &wgpu::BindGroupLayout {
13        self.gridcube_bind_group_layout
14            .as_ref()
15            .expect("gridcube pipeline not initialized")
16    }
17
18    /// Creates the gridcube pipeline (used for volume grid scalar cube visualization).
19    pub(crate) fn create_gridcube_pipeline(&mut self) {
20        let shader_source = include_str!("../../shaders/gridcube.wgsl");
21        let shader = self
22            .device
23            .create_shader_module(wgpu::ShaderModuleDescriptor {
24                label: Some("gridcube shader"),
25                source: wgpu::ShaderSource::Wgsl(shader_source.into()),
26            });
27
28        let bind_group_layout =
29            self.device
30                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
31                    label: Some("gridcube bind group layout"),
32                    entries: &[
33                        // Camera uniforms
34                        wgpu::BindGroupLayoutEntry {
35                            binding: 0,
36                            visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
37                            ty: wgpu::BindingType::Buffer {
38                                ty: wgpu::BufferBindingType::Uniform,
39                                has_dynamic_offset: false,
40                                min_binding_size: NonZeroU64::new(272),
41                            },
42                            count: None,
43                        },
44                        // GridcubeUniforms (96 bytes)
45                        wgpu::BindGroupLayoutEntry {
46                            binding: 1,
47                            visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
48                            ty: wgpu::BindingType::Buffer {
49                                ty: wgpu::BufferBindingType::Uniform,
50                                has_dynamic_offset: false,
51                                min_binding_size: NonZeroU64::new(96),
52                            },
53                            count: None,
54                        },
55                        // Cube positions storage buffer (template + per-instance centers)
56                        wgpu::BindGroupLayoutEntry {
57                            binding: 2,
58                            visibility: wgpu::ShaderStages::VERTEX,
59                            ty: wgpu::BindingType::Buffer {
60                                ty: wgpu::BufferBindingType::Storage { read_only: true },
61                                has_dynamic_offset: false,
62                                min_binding_size: None,
63                            },
64                            count: None,
65                        },
66                        // Cube normals storage buffer (36 face normals for unit cube)
67                        wgpu::BindGroupLayoutEntry {
68                            binding: 3,
69                            visibility: wgpu::ShaderStages::VERTEX,
70                            ty: wgpu::BindingType::Buffer {
71                                ty: wgpu::BufferBindingType::Storage { read_only: true },
72                                has_dynamic_offset: false,
73                                min_binding_size: None,
74                            },
75                            count: None,
76                        },
77                        // Scalar values storage buffer (one f32 per instance)
78                        wgpu::BindGroupLayoutEntry {
79                            binding: 4,
80                            visibility: wgpu::ShaderStages::VERTEX,
81                            ty: wgpu::BindingType::Buffer {
82                                ty: wgpu::BufferBindingType::Storage { read_only: true },
83                                has_dynamic_offset: false,
84                                min_binding_size: None,
85                            },
86                            count: None,
87                        },
88                        // Colormap 1D texture
89                        wgpu::BindGroupLayoutEntry {
90                            binding: 5,
91                            visibility: wgpu::ShaderStages::FRAGMENT,
92                            ty: wgpu::BindingType::Texture {
93                                sample_type: wgpu::TextureSampleType::Float { filterable: true },
94                                view_dimension: wgpu::TextureViewDimension::D1,
95                                multisampled: false,
96                            },
97                            count: None,
98                        },
99                        // Colormap sampler
100                        wgpu::BindGroupLayoutEntry {
101                            binding: 6,
102                            visibility: wgpu::ShaderStages::FRAGMENT,
103                            ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
104                            count: None,
105                        },
106                    ],
107                });
108
109        let pipeline_layout = self
110            .device
111            .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
112                label: Some("gridcube pipeline layout"),
113                bind_group_layouts: &[
114                    &bind_group_layout,
115                    &self.slice_plane_bind_group_layout,
116                    &self.matcap_bind_group_layout,
117                ],
118                push_constant_ranges: &[],
119            });
120
121        let pipeline = self
122            .device
123            .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
124                label: Some("gridcube pipeline"),
125                layout: Some(&pipeline_layout),
126                vertex: wgpu::VertexState {
127                    module: &shader,
128                    entry_point: Some("vs_main"),
129                    buffers: &[],
130                    compilation_options: wgpu::PipelineCompilationOptions::default(),
131                },
132                fragment: Some(wgpu::FragmentState {
133                    module: &shader,
134                    entry_point: Some("fs_main"),
135                    targets: &[
136                        // Color output (HDR)
137                        Some(wgpu::ColorTargetState {
138                            format: wgpu::TextureFormat::Rgba16Float,
139                            blend: Some(wgpu::BlendState::ALPHA_BLENDING),
140                            write_mask: wgpu::ColorWrites::ALL,
141                        }),
142                        // Normal output (G-buffer for SSAO)
143                        Some(wgpu::ColorTargetState {
144                            format: wgpu::TextureFormat::Rgba16Float,
145                            blend: None,
146                            write_mask: wgpu::ColorWrites::ALL,
147                        }),
148                    ],
149                    compilation_options: wgpu::PipelineCompilationOptions::default(),
150                }),
151                primitive: wgpu::PrimitiveState {
152                    topology: wgpu::PrimitiveTopology::TriangleList,
153                    strip_index_format: None,
154                    front_face: wgpu::FrontFace::Ccw,
155                    cull_mode: None,
156                    polygon_mode: wgpu::PolygonMode::Fill,
157                    unclipped_depth: false,
158                    conservative: false,
159                },
160                depth_stencil: Some(wgpu::DepthStencilState {
161                    format: wgpu::TextureFormat::Depth24PlusStencil8,
162                    depth_write_enabled: true,
163                    depth_compare: wgpu::CompareFunction::Less,
164                    stencil: wgpu::StencilState::default(),
165                    bias: wgpu::DepthBiasState::default(),
166                }),
167                multisample: wgpu::MultisampleState::default(),
168                multiview: None,
169                cache: None,
170            });
171
172        self.gridcube_pipeline = Some(pipeline);
173        self.gridcube_bind_group_layout = Some(bind_group_layout);
174    }
175}