Skip to main content

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 clouds. As cheap as it gets.
139    None,
140    /// Clouds, but barely. Ideally, any machine should be able to handle this
141    /// just fine.
142    Minimal,
143    /// Enough visual detail to be pleasing, but generally using poor-but-cheap
144    /// approximations to derive parameters
145    Low,
146    /// More detail. Enough to look good in most cases. For those that value
147    /// looks but also high framerates.
148    Medium,
149    /// High, but with extra compute power thrown at it to smooth out subtle
150    /// imperfections
151    Ultra,
152    /// Lots of detail with good-but-costly derivation of parameters.
153    #[serde(other)]
154    #[default]
155    High,
156}
157
158impl CloudMode {
159    pub fn is_enabled(&self) -> bool { *self != CloudMode::None }
160}
161
162/// Fluid modes
163#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
164pub enum FluidMode {
165    /// "Low" water.  This water implements no waves, no reflections, no
166    /// diffraction, and no light attenuation through water.  As a result,
167    /// it can be much cheaper than shiny reflection.
168    Low,
169    High,
170    /// This water implements waves on the surfaces, some attempt at
171    /// reflections, and tries to compute accurate light attenuation through
172    /// water (this is what results in the colors changing as you descend
173    /// into deep water).
174    ///
175    /// Unfortunately, the way the engine is currently set up, calculating
176    /// accurate attenuation is a bit difficult; we use estimates from
177    /// horizon maps for the current water altitude, which can both be off
178    /// by up to (max_altitude / 255) meters, only has per-chunk horizontal
179    /// resolution, and cannot handle edge cases like horizontal water (e.g.
180    /// waterfalls) well.  We are okay with the latter, and will try to fix
181    /// the former soon.
182    ///
183    /// Another issue is that we don't always know whether light is *blocked*,
184    /// which causes attenuation to be computed incorrectly; this can be
185    /// addressed by using shadow maps (at least for terrain).
186    #[serde(other)]
187    #[default]
188    Medium,
189}
190
191/// Reflection modes
192#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
193pub enum ReflectionMode {
194    /// No or minimal reflections.
195    Low,
196    /// High quality reflections with screen-space raycasting and
197    /// all the bells & whistles.
198    #[default]
199    High,
200    // Medium quality screen-space reflections.
201    #[serde(other)]
202    Medium,
203}
204
205/// Lighting modes
206#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
207pub enum LightingMode {
208    /// Ashikhmin-Shirley BRDF lighting model.  Attempts to generate a
209    /// physically plausible (to some extent) lighting distribution.
210    ///
211    /// This model may not work as well with purely directional lighting, and is
212    /// more expensive than the other models.
213    Ashikhmin,
214    /// Standard Lambertian lighting model, with only diffuse reflections.  The
215    /// cheapest lighting model by a decent margin, but the performance
216    /// difference between it and Blinn-Phong will probably only be
217    /// significant on low-end machines that are bottlenecked on fragment
218    /// shading.
219    Lambertian,
220    /// Standard Blinn-Phong shading, combing Lambertian diffuse reflections and
221    /// specular highlights.
222    #[serde(other)]
223    #[default]
224    BlinnPhong,
225}
226
227/// Shadow map settings.
228#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
229pub struct ShadowMapMode {
230    /// Multiple of default resolution (default, which is 1.0, is currently
231    /// the closest higher power of two above the length of the longest
232    /// diagonal of the screen resolution, but this may change).
233    pub resolution: f32,
234}
235
236impl Default for ShadowMapMode {
237    fn default() -> Self { Self { resolution: 1.0 } }
238}
239
240/// Shadow modes
241#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
242pub enum ShadowMode {
243    /// No shadows at all.  By far the cheapest option.
244    None,
245    /// Shadow map (render the scene from each light source, and also renders
246    /// LOD shadows using horizon maps).
247    Map(ShadowMapMode),
248    /// Point shadows (draw circles under figures, up to a configured maximum;
249    /// also render LOD shadows using horizon maps).  Can be expensive on
250    /// some machines, probably mostly due to horizon mapping; the point
251    /// shadows are not rendered too efficiently, but that can probably
252    /// be addressed later.
253    #[serde(other)] // Would normally be on `Map`, but only allowed on unit variants
254    Cheap,
255}
256
257impl Default for ShadowMode {
258    fn default() -> Self { ShadowMode::Map(Default::default()) }
259}
260
261impl TryFrom<ShadowMode> for ShadowMapMode {
262    type Error = ();
263
264    /// Get the shadow map details if they exist.
265    fn try_from(value: ShadowMode) -> Result<Self, Self::Error> {
266        if let ShadowMode::Map(map) = value {
267            Ok(map)
268        } else {
269            Err(())
270        }
271    }
272}
273
274impl ShadowMode {
275    pub fn is_map(&self) -> bool { matches!(self, Self::Map(_)) }
276}
277
278/// Upscale mode settings.
279#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
280pub struct UpscaleMode {
281    // Determines non-UI graphics upscaling. 0.25 to 2.0.
282    pub factor: f32,
283}
284
285impl Default for UpscaleMode {
286    fn default() -> Self { Self { factor: 1.0 } }
287}
288
289/// Present modes
290/// See <https://docs.rs/wgpu/0.7.0/wgpu/enum.PresentMode.html>
291#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
292pub enum PresentMode {
293    Mailbox,
294    Immediate,
295    FifoRelaxed,
296    #[default]
297    #[serde(other)]
298    Fifo, // has to be last for `#[serde(other)]`
299}
300
301impl From<PresentMode> for wgpu::PresentMode {
302    fn from(mode: PresentMode) -> Self {
303        match mode {
304            PresentMode::Fifo => wgpu::PresentMode::Fifo,
305            PresentMode::FifoRelaxed => wgpu::PresentMode::FifoRelaxed,
306            PresentMode::Mailbox => wgpu::PresentMode::Mailbox,
307            PresentMode::Immediate => wgpu::PresentMode::Immediate,
308        }
309    }
310}
311
312impl TryFrom<wgpu::PresentMode> for PresentMode {
313    type Error = ();
314
315    fn try_from(mode: wgpu::PresentMode) -> Result<Self, ()> {
316        match mode {
317            wgpu::PresentMode::Fifo => Ok(PresentMode::Fifo),
318            wgpu::PresentMode::FifoRelaxed => Ok(PresentMode::FifoRelaxed),
319            wgpu::PresentMode::Mailbox => Ok(PresentMode::Mailbox),
320            wgpu::PresentMode::Immediate => Ok(PresentMode::Immediate),
321            _ => Err(()),
322        }
323    }
324}
325
326/// Bloom factor
327/// Controls fraction of output image luminosity that is blurred bloom
328#[derive(Default, PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
329pub enum BloomFactor {
330    Low,
331    High,
332    /// Max valid value is 1.0
333    Custom(f32),
334    // other variant has to be placed last
335    #[serde(other)]
336    #[default]
337    Medium,
338}
339
340impl BloomFactor {
341    /// Fraction of output image luminosity that is blurred bloom
342    pub fn fraction(self) -> f32 {
343        match self {
344            Self::Low => 0.1,
345            Self::Medium => 0.2,
346            Self::High => 0.3,
347            Self::Custom(val) => val.clamp(0.0, 1.0),
348        }
349    }
350}
351
352/// Bloom settings
353#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
354pub struct BloomConfig {
355    /// Controls fraction of output image luminosity that is blurred bloom
356    ///
357    /// Defaults to `Medium`
358    pub factor: BloomFactor,
359    /// Turning this on make the bloom blur less sharply concentrated around the
360    /// high intensity phenomena (removes adding in less blurred layers to the
361    /// final blur)
362    ///
363    /// Defaults to `false`
364    pub uniform_blur: bool,
365    // TODO: allow configuring the blur radius and/or the number of passes
366}
367
368#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
369pub enum BloomMode {
370    On(BloomConfig),
371    #[serde(other)]
372    Off,
373}
374
375impl Default for BloomMode {
376    fn default() -> Self {
377        Self::On(BloomConfig {
378            factor: BloomFactor::default(),
379            uniform_blur: false,
380        })
381    }
382}
383
384impl BloomMode {
385    fn is_on(&self) -> bool { matches!(self, BloomMode::On(_)) }
386}
387
388/// Render modes
389#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
390#[serde(default)]
391pub struct RenderMode {
392    pub aa: AaMode,
393    pub cloud: CloudMode,
394    pub reflection: ReflectionMode,
395    pub fluid: FluidMode,
396    pub lighting: LightingMode,
397    pub shadow: ShadowMode,
398    pub rain_enabled: bool,
399    pub rain_occlusion: ShadowMapMode,
400    pub bloom: BloomMode,
401    /// 0.0..1.0
402    pub point_glow: f32,
403
404    pub flashing_lights_enabled: bool,
405
406    pub experimental_shaders: HashSet<ExperimentalShader>,
407
408    pub upscale_mode: UpscaleMode,
409    pub present_mode: PresentMode,
410    pub profiler_enabled: bool,
411    #[serde(skip)]
412    pub enable_naga: bool,
413}
414
415impl Default for RenderMode {
416    fn default() -> Self {
417        Self {
418            aa: AaMode::default(),
419            cloud: CloudMode::default(),
420            fluid: FluidMode::default(),
421            reflection: ReflectionMode::default(),
422            lighting: LightingMode::default(),
423            shadow: ShadowMode::default(),
424            rain_enabled: true,
425            rain_occlusion: ShadowMapMode::default(),
426            bloom: BloomMode::default(),
427            point_glow: 0.35,
428            flashing_lights_enabled: true,
429            experimental_shaders: HashSet::default(),
430            upscale_mode: UpscaleMode::default(),
431            present_mode: PresentMode::default(),
432            profiler_enabled: false,
433            enable_naga: std::env::var("VELOREN_DISABLE_NAGA_SHADERS").is_err(),
434        }
435    }
436}
437
438impl RenderMode {
439    fn split(self) -> (PipelineModes, OtherModes) {
440        (
441            PipelineModes {
442                aa: self.aa,
443                cloud: self.cloud,
444                fluid: self.fluid,
445                reflection: self.reflection,
446                lighting: self.lighting,
447                shadow: self.shadow,
448                rain_enabled: self.rain_enabled,
449                rain_occlusion: self.rain_occlusion,
450                bloom: self.bloom,
451                point_glow: self.point_glow,
452                flashing_lights_enabled: self.flashing_lights_enabled,
453                experimental_shaders: self.experimental_shaders,
454                enable_naga: self.enable_naga,
455            },
456            OtherModes {
457                upscale_mode: self.upscale_mode,
458                present_mode: self.present_mode,
459                profiler_enabled: self.profiler_enabled,
460            },
461        )
462    }
463}
464
465/// Render modes that require pipeline recreation (e.g. shader recompilation)
466/// when changed
467#[derive(PartialEq, Clone, Debug)]
468pub struct PipelineModes {
469    aa: AaMode,
470    pub cloud: CloudMode,
471    fluid: FluidMode,
472    reflection: ReflectionMode,
473    lighting: LightingMode,
474    pub shadow: ShadowMode,
475    pub rain_enabled: bool,
476    pub rain_occlusion: ShadowMapMode,
477    bloom: BloomMode,
478    point_glow: f32,
479    flashing_lights_enabled: bool,
480    experimental_shaders: HashSet<ExperimentalShader>,
481    enable_naga: bool,
482}
483
484impl PipelineModes {
485    pub fn remove_unsupported(&mut self) {
486        // Only enable experimental shaders that are supported by the game's current
487        // state
488        self.experimental_shaders.retain(|s| {
489            if s.is_supported() {
490                true
491            } else {
492                warn!(
493                    "Experimental shader {s:?} is not currently supported and will not be enabled."
494                );
495                false
496            }
497        });
498    }
499}
500
501/// Other render modes that don't effect pipelines
502#[derive(PartialEq, Clone, Debug)]
503struct OtherModes {
504    upscale_mode: UpscaleMode,
505    present_mode: PresentMode,
506    profiler_enabled: bool,
507}
508
509/// Experimental shader modes.
510///
511/// You can enable these using Voxygen's `settings.ron`. See
512/// [here](https://book.veloren.net/players/voxygen.html#experimental-shaders) for more information.
513#[derive(
514    Clone,
515    Debug,
516    PartialEq,
517    Eq,
518    Hash,
519    Serialize,
520    Deserialize,
521    strum::EnumIter,
522    strum::Display,
523    strum::EnumString,
524)]
525pub enum ExperimentalShader {
526    /// Add brick-like normal mapping to the world.
527    Brickloren,
528    /// Remove the default procedural noise from terrain.
529    NoNoise,
530    /// Add a sobel filter that draws lines in post-process by detecting edges
531    /// inbetween colors. This does perform 8 times more texture samples in
532    /// post-processing so there is potentially a significant performance
533    /// impact especially with anti aliasing enabled.
534    Sobel,
535    /// Like Sobel, but on the gradient texture instead of the color texture.
536    GradientSobel,
537    /// Simulate a curved world.
538    CurvedWorld,
539    /// Adds extra detail to distant LoD (Level of Detail) terrain procedurally.
540    ProceduralLodDetail,
541    /// Add a warping effect when underwater.
542    Underwarper,
543    /// Remove caustics from underwater terrain when shiny water is enabled.
544    NoCaustics,
545    /// Don't dither color in post-processing.
546    NoDither,
547    /// Don't use the nonlinear srgb space for dithering color.
548    NonSrgbDither,
549    /// Use triangle PDF noise for dithering instead of uniform noise.
550    TriangleNoiseDither,
551    /// Removes as many effects (including lighting) as possible in the name of
552    /// performance.
553    BareMinimum,
554    /// Lowers strength of the glow effect for lights near the camera.
555    LowGlowNearCamera,
556    /// Disable the fake voxel effect on LoD features.
557    NoLodVoxels,
558    /// Enable a 'pop-in' effect when loading terrain.
559    TerrainPop,
560    /// Display grid lines to visualize the distribution of shadow map texels
561    /// for the directional light from the sun.
562    DirectionalShadowMapTexelGrid,
563    /// Disable rainbows
564    NoRainbows,
565    /// Add extra detailing to puddles.
566    PuddleDetails,
567    /// Show gbuffer surface normals.
568    ViewNormals,
569    /// Show gbuffer materials.
570    ViewMaterials,
571    /// Show gbuffer depth.
572    ViewDepth,
573    /// Rather than fading out screen-space reflections at view space borders,
574    /// smear screen space to cover the reflection vector.
575    SmearReflections,
576    /// Apply the point shadows from cheap shadows on top of shadow mapping.
577    PointShadowsWithShadowMapping,
578    /// Make the UI uses nearest neighbor filtering for scaling images instead
579    /// of trying to filter based on the coverage of the sampled pixels.
580    UiNearestScaling,
581    /// Prefer using physically-based values for various rendering parameters,
582    /// where possible.
583    Photorealistic,
584    /// A noisy newspaper effect.
585    Newspaper,
586    /// A colorful dithering effect.
587    ColorDithering,
588    /// Cinematic color grading.
589    Cinematic,
590    /// Glittering snow.
591    SnowGlitter,
592    /// Enables optimizations when shaderc is processing shaders (currently on
593    /// by default, but keep this for now in case we have to switch back to
594    /// being off by default).
595    EnableShadercOptimization,
596    /// Disables optimizations when shaderc is processing shaders (has priority
597    /// over `EnableShadercOptimization`).
598    DisableShadercOptimization,
599    /// Switches some transparency rendering to use discarding.
600    DiscardTransparency,
601    /// Display chunk borders for easier debugging.
602    ShowChunkBorders,
603    /// Adds a layer of colour quantization to the output, giving a retro
604    /// palette feel.
605    ColorQuantization,
606    /// Adds outlines around the perimeters of objects for a pixel art feel.
607    /// Best at lower internal resolutions.
608    Outlines,
609    /// Display various kinds of mesh in wireframe mode.
610    Wireframe,
611}
612
613impl ExperimentalShader {
614    pub fn is_supported(&self) -> bool {
615        match self {
616            Self::Wireframe => cfg!(debug_assertions),
617            _ => true,
618        }
619    }
620}