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 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}