veloren_voxygen/render/mod.rs
1pub mod bound;
2mod buffer;
3pub mod consts;
4mod error;
5pub mod instances;
6pub mod mesh;
7pub mod model;
8pub mod pipelines;
9pub mod renderer;
10pub mod texture;
11
12// Reexports
13pub use self::{
14 bound::Bound,
15 buffer::Buffer,
16 consts::Consts,
17 error::RenderError,
18 instances::Instances,
19 mesh::{Mesh, Quad, Tri},
20 model::{DynamicModel, Model, SubModel},
21 pipelines::{
22 FigureSpriteAtlasData, GlobalModel, Globals, GlobalsBindGroup, GlobalsLayouts, Light,
23 Shadow, TerrainAtlasData,
24 clouds::Locals as CloudsLocals,
25 debug::{DebugLayout, DebugPipeline, Locals as DebugLocals, Vertex as DebugVertex},
26 figure::{
27 BoneData as FigureBoneData, BoneMeshes, FigureLayout, FigureModel,
28 Locals as FigureLocals,
29 },
30 fluid::Vertex as FluidVertex,
31 lod_object::{Instance as LodObjectInstance, Vertex as LodObjectVertex},
32 lod_terrain::{LodData, Vertex as LodTerrainVertex},
33 particle::{Instance as ParticleInstance, Vertex as ParticleVertex},
34 postprocess::Locals as PostProcessLocals,
35 rain_occlusion::Locals as RainOcclusionLocals,
36 shadow::{Locals as ShadowLocals, PointLightMatrix},
37 skybox::{Vertex as SkyboxVertex, create_mesh as create_skybox_mesh},
38 sprite::{
39 Instance as SpriteInstance, SpriteGlobalsBindGroup, SpriteVerts,
40 VERT_PAGE_SIZE as SPRITE_VERT_PAGE_SIZE, Vertex as SpriteVertex,
41 },
42 terrain::{Locals as TerrainLocals, TerrainLayout, Vertex as TerrainVertex},
43 trail::Vertex as TrailVertex,
44 ui::{
45 BoundLocals as UiBoundLocals, Locals as UiLocals, Mode as UiMode,
46 TextureBindGroup as UiTextureBindGroup, UploadBatchId as UiUploadBatchId,
47 Vertex as UiVertex, create_quad as create_ui_quad,
48 create_quad_vert_gradient as create_ui_quad_vert_gradient, create_tri as create_ui_tri,
49 },
50 },
51 renderer::{
52 AltIndices, CullingMode, Renderer,
53 drawer::{
54 DebugDrawer, DebugShadowDrawer, Drawer, FigureDrawer, FigureShadowDrawer,
55 FirstPassDrawer, ParticleDrawer, PreparedUiDrawer, ShadowPassDrawer, SpriteDrawer,
56 TerrainDrawer, TerrainShadowDrawer, ThirdPassDrawer, TrailDrawer,
57 TransparentPassDrawer, UI_PREMULTIPLY_PASS, UiDrawer, VolumetricPassDrawer,
58 },
59 },
60 texture::Texture,
61};
62use hashbrown::HashSet;
63use tracing::warn;
64pub use wgpu::{AddressMode, FilterMode};
65
66pub trait Vertex: Clone + bytemuck::Pod {
67 const STRIDE: wgpu::BufferAddress;
68 // Whether these types of verts use the quad index buffer for drawing them
69 const QUADS_INDEX: Option<wgpu::IndexFormat>;
70}
71
72use serde::{Deserialize, Serialize};
73/// Anti-aliasing modes
74#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
75pub enum AaMode {
76 /// Fast approximate antialiasing.
77 ///
78 /// This is a screen-space technique, and therefore works fine with greedy
79 /// meshing.
80 #[default]
81 Fxaa,
82 /// Multisampling AA, up to 4 samples per pixel.
83 ///
84 /// NOTE: MSAA modes don't (currently) work with greedy meshing, and will
85 /// also struggle in the future with deferred shading, so they may be
86 /// removed in the future.
87 MsaaX4,
88 /// Multisampling AA, up to 8 samples per pixel.
89 ///
90 /// NOTE: MSAA modes don't (currently) work with greedy meshing, and will
91 /// also struggle in the future with deferred shading, so they may be
92 /// removed in the future.
93 MsaaX8,
94 /// Multisampling AA, up to 16 samples per pixel.
95 ///
96 /// NOTE: MSAA modes don't (currently) work with greedy meshing, and will
97 /// also struggle in the future with deferred shading, so they may be
98 /// removed in the future.
99 MsaaX16,
100 /// Fast edge-detecting upscaling.
101 ///
102 /// Screen-space technique that attempts to reconstruct lines and edges
103 /// in the original image. Useless at internal resolutions higher than 1.0x,
104 /// but potentially very effective at much lower internal resolutions.
105 Hqx,
106 /// Fast upscaling informed by FXAA.
107 ///
108 /// Screen-space technique that uses a combination of FXAA and
109 /// nearest-neighbour sample retargeting to produce crisp, clean upscaling.
110 FxUpscale,
111 /// Bilinear filtering.
112 ///
113 /// Linear interpolation of the color buffer in each axis to determine the
114 /// pixel.
115 Bilinear,
116 /// Nearest-neighbour filtering.
117 ///
118 /// The colour of each pixel is determined by the colour of the spatially
119 /// closest texel in the color buffer.
120 #[serde(other)]
121 None,
122}
123
124impl AaMode {
125 pub fn samples(&self) -> u32 {
126 match self {
127 AaMode::None | AaMode::Bilinear | AaMode::Fxaa | AaMode::Hqx | AaMode::FxUpscale => 1,
128 AaMode::MsaaX4 => 4,
129 AaMode::MsaaX8 => 8,
130 AaMode::MsaaX16 => 16,
131 }
132 }
133}
134
135/// Cloud modes
136#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
137pub enum CloudMode {
138 /// No volumetrics, flat cloud texture.
139 #[serde(alias = "None")]
140 Flat,
141 /// Clouds, but barely. Ideally, any machine should be able to handle this
142 /// just fine.
143 Minimal,
144 /// Enough visual detail to be pleasing, but generally using poor-but-cheap
145 /// approximations to derive parameters
146 Low,
147 /// More detail. Enough to look good in most cases. For those that value
148 /// looks but also high framerates.
149 Medium,
150 /// High, but with extra compute power thrown at it to smooth out subtle
151 /// imperfections
152 Ultra,
153 /// Lots of detail with good-but-costly derivation of parameters.
154 #[serde(other)]
155 #[default]
156 High,
157}
158
159/// Fluid modes
160#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
161pub enum FluidMode {
162 /// "Low" water. This water implements no waves, no reflections, no
163 /// diffraction, and no light attenuation through water. As a result,
164 /// it can be much cheaper than shiny reflection.
165 Low,
166 High,
167 /// This water implements waves on the surfaces, some attempt at
168 /// reflections, and tries to compute accurate light attenuation through
169 /// water (this is what results in the colors changing as you descend
170 /// into deep water).
171 ///
172 /// Unfortunately, the way the engine is currently set up, calculating
173 /// accurate attenuation is a bit difficult; we use estimates from
174 /// horizon maps for the current water altitude, which can both be off
175 /// by up to (max_altitude / 255) meters, only has per-chunk horizontal
176 /// resolution, and cannot handle edge cases like horizontal water (e.g.
177 /// waterfalls) well. We are okay with the latter, and will try to fix
178 /// the former soon.
179 ///
180 /// Another issue is that we don't always know whether light is *blocked*,
181 /// which causes attenuation to be computed incorrectly; this can be
182 /// addressed by using shadow maps (at least for terrain).
183 #[serde(other)]
184 #[default]
185 Medium,
186}
187
188/// Reflection modes
189#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
190pub enum ReflectionMode {
191 /// No or minimal reflections.
192 Low,
193 /// High quality reflections with screen-space raycasting and
194 /// all the bells & whistles.
195 #[default]
196 High,
197 // Medium quality screen-space reflections.
198 #[serde(other)]
199 Medium,
200}
201
202/// Lighting modes
203#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
204pub enum LightingMode {
205 /// Ashikhmin-Shirley BRDF lighting model. Attempts to generate a
206 /// physically plausible (to some extent) lighting distribution.
207 ///
208 /// This model may not work as well with purely directional lighting, and is
209 /// more expensive than the other models.
210 Ashikhmin,
211 /// Standard Lambertian lighting model, with only diffuse reflections. The
212 /// cheapest lighting model by a decent margin, but the performance
213 /// difference between it and Blinn-Phong will probably only be
214 /// significant on low-end machines that are bottlenecked on fragment
215 /// shading.
216 Lambertian,
217 /// Standard Blinn-Phong shading, combing Lambertian diffuse reflections and
218 /// specular highlights.
219 #[serde(other)]
220 #[default]
221 BlinnPhong,
222}
223
224/// Shadow map settings.
225#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
226pub struct ShadowMapMode {
227 /// Multiple of default resolution (default, which is 1.0, is currently
228 /// the closest higher power of two above the length of the longest
229 /// diagonal of the screen resolution, but this may change).
230 pub resolution: f32,
231}
232
233impl Default for ShadowMapMode {
234 fn default() -> Self { Self { resolution: 1.0 } }
235}
236
237/// Shadow modes
238#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
239pub enum ShadowMode {
240 /// No shadows at all. By far the cheapest option.
241 None,
242 /// Shadow map (render the scene from each light source, and also renders
243 /// LOD shadows using horizon maps).
244 Map(ShadowMapMode),
245 /// Point shadows (draw circles under figures, up to a configured maximum;
246 /// also render LOD shadows using horizon maps). Can be expensive on
247 /// some machines, probably mostly due to horizon mapping; the point
248 /// shadows are not rendered too efficiently, but that can probably
249 /// be addressed later.
250 #[serde(other)] // Would normally be on `Map`, but only allowed on unit variants
251 Cheap,
252}
253
254impl Default for ShadowMode {
255 fn default() -> Self { ShadowMode::Map(Default::default()) }
256}
257
258impl TryFrom<ShadowMode> for ShadowMapMode {
259 type Error = ();
260
261 /// Get the shadow map details if they exist.
262 fn try_from(value: ShadowMode) -> Result<Self, Self::Error> {
263 if let ShadowMode::Map(map) = value {
264 Ok(map)
265 } else {
266 Err(())
267 }
268 }
269}
270
271impl ShadowMode {
272 pub fn is_map(&self) -> bool { matches!(self, Self::Map(_)) }
273}
274
275/// Upscale mode settings.
276#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
277pub struct UpscaleMode {
278 // Determines non-UI graphics upscaling. 0.25 to 2.0.
279 pub factor: f32,
280}
281
282impl Default for UpscaleMode {
283 fn default() -> Self { Self { factor: 1.0 } }
284}
285
286/// Present modes
287/// See <https://docs.rs/wgpu/0.7.0/wgpu/enum.PresentMode.html>
288#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
289pub enum PresentMode {
290 Mailbox,
291 Immediate,
292 FifoRelaxed,
293 #[default]
294 #[serde(other)]
295 Fifo, // has to be last for `#[serde(other)]`
296}
297
298impl From<PresentMode> for wgpu::PresentMode {
299 fn from(mode: PresentMode) -> Self {
300 match mode {
301 PresentMode::Fifo => wgpu::PresentMode::Fifo,
302 PresentMode::FifoRelaxed => wgpu::PresentMode::FifoRelaxed,
303 PresentMode::Mailbox => wgpu::PresentMode::Mailbox,
304 PresentMode::Immediate => wgpu::PresentMode::Immediate,
305 }
306 }
307}
308
309impl TryFrom<wgpu::PresentMode> for PresentMode {
310 type Error = ();
311
312 fn try_from(mode: wgpu::PresentMode) -> Result<Self, ()> {
313 match mode {
314 wgpu::PresentMode::Fifo => Ok(PresentMode::Fifo),
315 wgpu::PresentMode::FifoRelaxed => Ok(PresentMode::FifoRelaxed),
316 wgpu::PresentMode::Mailbox => Ok(PresentMode::Mailbox),
317 wgpu::PresentMode::Immediate => Ok(PresentMode::Immediate),
318 _ => Err(()),
319 }
320 }
321}
322
323/// Bloom factor
324/// Controls fraction of output image luminosity that is blurred bloom
325#[derive(Default, PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
326pub enum BloomFactor {
327 Low,
328 High,
329 /// Max valid value is 1.0
330 Custom(f32),
331 // other variant has to be placed last
332 #[serde(other)]
333 #[default]
334 Medium,
335}
336
337impl BloomFactor {
338 /// Fraction of output image luminosity that is blurred bloom
339 pub fn fraction(self) -> f32 {
340 match self {
341 Self::Low => 0.1,
342 Self::Medium => 0.2,
343 Self::High => 0.3,
344 Self::Custom(val) => val.clamp(0.0, 1.0),
345 }
346 }
347}
348
349/// Bloom settings
350#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
351pub struct BloomConfig {
352 /// Controls fraction of output image luminosity that is blurred bloom
353 ///
354 /// Defaults to `Medium`
355 pub factor: BloomFactor,
356 /// Turning this on make the bloom blur less sharply concentrated around the
357 /// high intensity phenomena (removes adding in less blurred layers to the
358 /// final blur)
359 ///
360 /// Defaults to `false`
361 pub uniform_blur: bool,
362 // TODO: allow configuring the blur radius and/or the number of passes
363}
364
365#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
366pub enum BloomMode {
367 On(BloomConfig),
368 #[serde(other)]
369 Off,
370}
371
372impl Default for BloomMode {
373 fn default() -> Self {
374 Self::On(BloomConfig {
375 factor: BloomFactor::default(),
376 uniform_blur: false,
377 })
378 }
379}
380
381impl BloomMode {
382 fn is_on(&self) -> bool { matches!(self, BloomMode::On(_)) }
383}
384
385/// Render modes
386#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
387#[serde(default)]
388pub struct RenderMode {
389 pub aa: AaMode,
390 pub cloud: CloudMode,
391 pub reflection: ReflectionMode,
392 pub fluid: FluidMode,
393 pub lighting: LightingMode,
394 pub shadow: ShadowMode,
395 pub rain_enabled: bool,
396 pub rain_occlusion: ShadowMapMode,
397 pub bloom: BloomMode,
398 /// 0.0..1.0
399 pub point_glow: f32,
400
401 pub flashing_lights_enabled: bool,
402
403 pub experimental_shaders: HashSet<ExperimentalShader>,
404
405 pub upscale_mode: UpscaleMode,
406 pub present_mode: PresentMode,
407 pub profiler_enabled: bool,
408 #[serde(skip)]
409 pub enable_naga: bool,
410}
411
412impl Default for RenderMode {
413 fn default() -> Self {
414 Self {
415 aa: AaMode::default(),
416 cloud: CloudMode::default(),
417 fluid: FluidMode::default(),
418 reflection: ReflectionMode::default(),
419 lighting: LightingMode::default(),
420 shadow: ShadowMode::default(),
421 rain_enabled: true,
422 rain_occlusion: ShadowMapMode::default(),
423 bloom: BloomMode::default(),
424 point_glow: 0.35,
425 flashing_lights_enabled: true,
426 experimental_shaders: HashSet::default(),
427 upscale_mode: UpscaleMode::default(),
428 present_mode: PresentMode::default(),
429 profiler_enabled: false,
430 enable_naga: std::env::var("VELOREN_DISABLE_NAGA_SHADERS").is_err(),
431 }
432 }
433}
434
435impl RenderMode {
436 fn split(self) -> (PipelineModes, OtherModes) {
437 (
438 PipelineModes {
439 aa: self.aa,
440 cloud: self.cloud,
441 fluid: self.fluid,
442 reflection: self.reflection,
443 lighting: self.lighting,
444 shadow: self.shadow,
445 rain_enabled: self.rain_enabled,
446 rain_occlusion: self.rain_occlusion,
447 bloom: self.bloom,
448 point_glow: self.point_glow,
449 flashing_lights_enabled: self.flashing_lights_enabled,
450 experimental_shaders: self.experimental_shaders,
451 enable_naga: self.enable_naga,
452 },
453 OtherModes {
454 upscale_mode: self.upscale_mode,
455 present_mode: self.present_mode,
456 profiler_enabled: self.profiler_enabled,
457 },
458 )
459 }
460}
461
462/// Render modes that require pipeline recreation (e.g. shader recompilation)
463/// when changed
464#[derive(PartialEq, Clone, Debug)]
465pub struct PipelineModes {
466 aa: AaMode,
467 pub cloud: CloudMode,
468 fluid: FluidMode,
469 reflection: ReflectionMode,
470 lighting: LightingMode,
471 pub shadow: ShadowMode,
472 pub rain_enabled: bool,
473 pub rain_occlusion: ShadowMapMode,
474 bloom: BloomMode,
475 point_glow: f32,
476 flashing_lights_enabled: bool,
477 experimental_shaders: HashSet<ExperimentalShader>,
478 enable_naga: bool,
479}
480
481impl PipelineModes {
482 pub fn remove_unsupported(&mut self) {
483 // Only enable experimental shaders that are supported by the game's current
484 // state
485 self.experimental_shaders.retain(|s| {
486 if s.is_supported() {
487 true
488 } else {
489 warn!(
490 "Experimental shader {s:?} is not currently supported and will not be enabled."
491 );
492 false
493 }
494 });
495 }
496}
497
498/// Other render modes that don't effect pipelines
499#[derive(PartialEq, Clone, Debug)]
500struct OtherModes {
501 upscale_mode: UpscaleMode,
502 present_mode: PresentMode,
503 profiler_enabled: bool,
504}
505
506/// Experimental shader modes.
507///
508/// You can enable these using Voxygen's `settings.ron`. See
509/// [here](https://book.veloren.net/players/voxygen.html#experimental-shaders) for more information.
510#[derive(
511 Clone,
512 Debug,
513 PartialEq,
514 Eq,
515 Hash,
516 Serialize,
517 Deserialize,
518 strum::EnumIter,
519 strum::Display,
520 strum::EnumString,
521)]
522pub enum ExperimentalShader {
523 /// Add brick-like normal mapping to the world.
524 Brickloren,
525 /// Remove the default procedural noise from terrain.
526 NoNoise,
527 /// Add a sobel filter that draws lines in post-process by detecting edges
528 /// inbetween colors. This does perform 8 times more texture samples in
529 /// post-processing so there is potentially a significant performance
530 /// impact especially with anti aliasing enabled.
531 Sobel,
532 /// Like Sobel, but on the gradient texture instead of the color texture.
533 GradientSobel,
534 /// Simulate a curved world.
535 CurvedWorld,
536 /// Adds extra detail to distant LoD (Level of Detail) terrain procedurally.
537 ProceduralLodDetail,
538 /// Add a warping effect when underwater.
539 Underwarper,
540 /// Remove caustics from underwater terrain when shiny water is enabled.
541 NoCaustics,
542 /// Don't dither color in post-processing.
543 NoDither,
544 /// Don't use the nonlinear srgb space for dithering color.
545 NonSrgbDither,
546 /// Use triangle PDF noise for dithering instead of uniform noise.
547 TriangleNoiseDither,
548 /// Removes as many effects (including lighting) as possible in the name of
549 /// performance.
550 BareMinimum,
551 /// Lowers strength of the glow effect for lights near the camera.
552 LowGlowNearCamera,
553 /// Disable the fake voxel effect on LoD features.
554 NoLodVoxels,
555 /// Enable a 'pop-in' effect when loading terrain.
556 TerrainPop,
557 /// Display grid lines to visualize the distribution of shadow map texels
558 /// for the directional light from the sun.
559 DirectionalShadowMapTexelGrid,
560 /// Disable rainbows
561 NoRainbows,
562 /// Add extra detailing to puddles.
563 PuddleDetails,
564 /// Show gbuffer surface normals.
565 ViewNormals,
566 /// Show gbuffer materials.
567 ViewMaterials,
568 /// Show gbuffer depth.
569 ViewDepth,
570 /// Rather than fading out screen-space reflections at view space borders,
571 /// smear screen space to cover the reflection vector.
572 SmearReflections,
573 /// Apply the point shadows from cheap shadows on top of shadow mapping.
574 PointShadowsWithShadowMapping,
575 /// Make the UI uses nearest neighbor filtering for scaling images instead
576 /// of trying to filter based on the coverage of the sampled pixels.
577 UiNearestScaling,
578 /// Prefer using physically-based values for various rendering parameters,
579 /// where possible.
580 Photorealistic,
581 /// A noisy newspaper effect.
582 Newspaper,
583 /// A colorful dithering effect.
584 ColorDithering,
585 /// Cinematic color grading.
586 Cinematic,
587 /// Glittering snow.
588 SnowGlitter,
589 /// Enables optimizations when shaderc is processing shaders (currently on
590 /// by default, but keep this for now in case we have to switch back to
591 /// being off by default).
592 EnableShadercOptimization,
593 /// Disables optimizations when shaderc is processing shaders (has priority
594 /// over `EnableShadercOptimization`).
595 DisableShadercOptimization,
596 /// Switches some transparency rendering to use discarding.
597 DiscardTransparency,
598 /// Display chunk borders for easier debugging.
599 ShowChunkBorders,
600 /// Adds a layer of colour quantization to the output, giving a retro
601 /// palette feel.
602 ColorQuantization,
603 /// Adds outlines around the perimeters of objects for a pixel art feel.
604 /// Best at lower internal resolutions.
605 Outlines,
606 /// Display various kinds of mesh in wireframe mode.
607 Wireframe,
608 /// Disable haze due to Rayleigh scattering.
609 NoHaze,
610 /// Disable cloud rendering entirely.
611 NoClouds,
612}
613
614impl ExperimentalShader {
615 pub fn is_supported(&self) -> bool {
616 match self {
617 Self::Wireframe => cfg!(debug_assertions),
618 _ => true,
619 }
620 }
621}