diff --git a/Redot-Documentation-Tests/DocRendererServiceTests.cs b/Redot-Documentation-Tests/DocRendererServiceTests.cs index b135cff..e28a17a 100644 --- a/Redot-Documentation-Tests/DocRendererServiceTests.cs +++ b/Redot-Documentation-Tests/DocRendererServiceTests.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.FileProviders; using Redot_Documentation.Services; using Redot_Documentation.Versioning; +using System.Text.RegularExpressions; namespace Redot_Documentation_Tests; @@ -69,6 +70,34 @@ await File.WriteAllTextAsync( Assert.Contains($"href=\"{expectedHref}\"", html); } + [Fact] + public async Task RenderToHtmlAsync_RendersNestedTabsFromInnermostBlockOutward() + { + Directory.CreateDirectory(Path.Combine(contentRootPath, "docs")); + await File.WriteAllTextAsync( + Path.Combine(contentRootPath, "docs", "source.md"), + """ + + + + + Nested content. + + + + + """); + + var renderer = new DocRendererService(new TestWebHostEnvironment(contentRootPath)); + + string html = await renderer.RenderToHtmlAsync("source.md", CreateVersionProvider()); + + Assert.Equal(2, Regex.Matches(html, "class=\"doc-tabs\"").Count); + Assert.Contains("Nested content.", html); + Assert.DoesNotContain("", html); + Assert.DoesNotContain("(manager.LoadContent); + + Assert.Contains("Duplicate documentation slug 'doc_example' in version 'latest'", exception.Message); + Assert.Contains("/en/latest/example.md", exception.Message); + Assert.Contains("/en/latest/example/index.md", exception.Message); + } + [Fact] public void LoadContent_RejectsLegacyStringArray() { diff --git a/Redot-Documentation/Services/DocRendererService.cs b/Redot-Documentation/Services/DocRendererService.cs index e870906..5a02fc3 100644 --- a/Redot-Documentation/Services/DocRendererService.cs +++ b/Redot-Documentation/Services/DocRendererService.cs @@ -245,16 +245,53 @@ private static (string name, string url) SplitMarkdownLink(string matchValue) private static string TransformTabsBlocks(string markdown, Dictionary htmlPlaceholders) { var tabsIndex = 0; - return Regex.Replace( - markdown, - @"(.*?)", - match => TransformSingleTabsBlock(match, tabsIndex++, htmlPlaceholders), - RegexOptions.IgnoreCase | RegexOptions.Singleline); + while (TryFindInnermostTabsBlock(markdown, out var startIndex, out var endIndex, out var contentStartIndex)) + { + var tabsContent = markdown[contentStartIndex..endIndex]; + var transformedTabs = TransformSingleTabsBlock(tabsContent, tabsIndex++, htmlPlaceholders); + markdown = string.Concat( + markdown.AsSpan(0, startIndex), + transformedTabs, + markdown.AsSpan(endIndex + "".Length)); + } + + return markdown; + } + + private static bool TryFindInnermostTabsBlock( + string markdown, + out int startIndex, + out int endIndex, + out int contentStartIndex) + { + var openings = new Stack(); + foreach (Match tag in Regex.Matches(markdown, @"]*>", RegexOptions.IgnoreCase)) + { + if (tag.Value.StartsWith(" htmlPlaceholders) + private static string TransformSingleTabsBlock(string tabsContent, int tabsIndex, Dictionary htmlPlaceholders) { - var tabsContent = tabsBlockMatch.Groups[1].Value; var tabItemMatches = Regex.Matches( tabsContent, @"]*)>(.*?)", @@ -262,7 +299,7 @@ private static string TransformSingleTabsBlock(Match tabsBlockMatch, int tabsInd if (tabItemMatches.Count == 0) { - return tabsBlockMatch.Value; + return $"{tabsContent}"; } var tabButtonsMarkup = new List(tabItemMatches.Count); diff --git a/Redot-Documentation/Versioning/RankingConfig.cs b/Redot-Documentation/Versioning/RankingConfig.cs index 9dadc51..49f52ad 100644 --- a/Redot-Documentation/Versioning/RankingConfig.cs +++ b/Redot-Documentation/Versioning/RankingConfig.cs @@ -2,6 +2,7 @@ namespace Redot_Documentation.Versioning; public class RankingConfig { + public string? Slug { get; set; } public bool IntermingleArticles { get; set; } = false; public string SlugPrefix { get; set; } = "doc_"; public Dictionary RankingPriorities { get; set; } = new(); @@ -12,4 +13,4 @@ public RankingConfig() ExcludedItems.Add("img"); ExcludedItems.Add("video"); } -} \ No newline at end of file +} diff --git a/Redot-Documentation/Versioning/Section.cs b/Redot-Documentation/Versioning/Section.cs index 31ce976..35e60d5 100644 --- a/Redot-Documentation/Versioning/Section.cs +++ b/Redot-Documentation/Versioning/Section.cs @@ -53,6 +53,8 @@ public void LoadAndParse() rankingPriorities = configData.RankingPriorities; excludedItems = configData.ExcludedItems; SlugPrefix = configData.SlugPrefix; + if (!string.IsNullOrWhiteSpace(configData.Slug)) + Slug = configData.Slug; IntermingleArticles = configData.IntermingleArticles; } else diff --git a/Redot-Documentation/Versioning/VersionProvider.cs b/Redot-Documentation/Versioning/VersionProvider.cs index 6f72041..9c7217b 100644 --- a/Redot-Documentation/Versioning/VersionProvider.cs +++ b/Redot-Documentation/Versioning/VersionProvider.cs @@ -80,7 +80,7 @@ public void ParseSlugs() ParseSlugs(subSection, slugLookupTable); else { - slugLookupTable.Add(ranking.Slug, GetReferentialPath(ranking.Path)); + AddSlug(ranking, slugLookupTable); } } @@ -101,16 +101,26 @@ public string GetReferentialPath(string path) private void ParseSlugs(Section section, IDictionary slugLookupTable) { if (section.IndexArticle != null) - slugLookupTable.Add(section.IndexArticle.Slug, GetReferentialPath(section.IndexArticle.Path)); + AddSlug(section.IndexArticle, slugLookupTable); foreach (IRanking ranking in section.GetSortedRankings()) { if (ranking is Section subSection) ParseSlugs(subSection, slugLookupTable); else - slugLookupTable.Add(ranking.Slug, GetReferentialPath(ranking.Path)); + AddSlug(ranking, slugLookupTable); } } + private void AddSlug(IRanking ranking, IDictionary slugLookupTable) + { + string path = GetReferentialPath(ranking.Path); + if (slugLookupTable.TryGetValue(ranking.Slug, out string? existingPath)) + throw new InvalidOperationException( + $"Duplicate documentation slug '{ranking.Slug}' in version '{Version.Slug}': '{existingPath}' and '{path}'."); + + slugLookupTable.Add(ranking.Slug, path); + } + public string GetPathFromSlug(string slug) => Volatile.Read(ref _slugLookupTable)[slug]; diff --git a/Redot-Documentation/docs/26.1/Tutorials/2d/2d_antialiasing.md b/Redot-Documentation/docs/26.1/Tutorials/2d/2d_antialiasing.md new file mode 100644 index 0000000..ca34d71 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/2d/2d_antialiasing.md @@ -0,0 +1,88 @@ + +# 2D antialiasing + +:::info + +Redot also supports antialiasing in 3D rendering. This is covered on the +[doc_3d_antialiasing](../3d/3d_antialiasing.md) page. + +::: + +## Introduction + +Due to their limited resolution, scenes rendered in 2D can exhibit aliasing +artifacts. These artifacts usually manifest in the form of a "staircase" effect on +geometry edges, and are most noticeable when using nodes such as [class_Line2D](class_Line2D), +[class_Polygon2D](class_Polygon2D) or [class_TextureProgressBar](class_TextureProgressBar). [doc_custom_drawing_in_2d](custom_drawing_in_2d.md) +can also have aliasing artifacts for methods that don't support antialiasing. + +In the example below, you can notice how +edges have a blocky appearance: + +![Image](/img/Tutorials/2d/img/antialiasing_none_scaled.webp) + + Image is scaled by 2× with nearest-neighbor filtering to make aliasing more noticeable. + +To combat this, Redot supports several methods of enabling antialiasing on 2D rendering. + +## Antialiasing property in Line2D and custom drawing + +This is the recommended method, as it has a lower performance impact in most cases. + +Line2D has an **Antialiased** property which you can enable in the inspector. +Also, several methods for [doc_custom_drawing_in_2d](custom_drawing_in_2d.md) support an optional +``antialiased`` parameter, which can be set to ``true`` when calling the +function. + +These methods do not require MSAA to be enabled, which makes their *baseline* +performance cost low. In other words, there is no permanent added cost if you're +not drawing any antialiased geometry at some point. + +The downside of these antialiasing methods is that they work by generating +additional geometry. If you're generating complex 2D geometry that's updated +every frame, this may be a bottleneck. Also, Polygon2D, TextureProgressBar, and +several custom drawing methods don't feature an antialiased property. For these +nodes, you can use 2D multisample antialiasing instead. + +## Multisample antialiasing (MSAA) + +*This is only available in the Forward+ and Mobile renderers, not the +Compatibility renderer.* + +Before enabling MSAA in 2D, it's important to understand what MSAA will operate +on. MSAA in 2D follows similar restrictions as in 3D. While it does not +introduce any blurriness, its scope of application is limited. The main +applications of 2D MSAA are: + +- Geometry edges, such as line and polygon drawing. +- Sprite edges *only for pixels touching one of the texture's edges*. This works + for both linear and nearest-neighbor filtering. Sprite edges created using + transparency on the image are not affected by MSAA. + +The downside of MSAA is that it only operates on edges. This is because MSAA +increases the number of *coverage* samples, but not the number of *color* +samples. However, since the number of color samples did not increase, fragment +shaders are still run for each pixel only once. As a result, MSAA will **not +affect** the following kinds of aliasing in any way: + +- Aliasing *within* nearest-neighbor filtered textures (pixel art). +- Aliasing caused by custom 2D shaders. +- Specular aliasing when using Light2D. +- Aliasing in font rendering. + +MSAA can be enabled in the Project Settings by changing the value of the +[Rendering > Anti Aliasing > Quality > MSAA 2D](class_ProjectSettings_property_rendering/anti_aliasing/quality/msaa_2d) +setting. It's important to change the value of the **MSAA 2D** setting and not **MSAA 3D**, as these are entirely +separate settings. + +Comparison between no antialiasing (left) and various MSAA levels (right). The +top-left corner contains a Line2D node, the top-right corner contains 2 +TextureProgressBar nodes. The bottom contains 8 pixel art sprites, with 4 of +them touching the edges (green background) and 4 of them not touching the edges +(Redot logo): + +![Image](/img/Tutorials/2d/img/antialiasing_msaa_2x.webp) + +![Image](/img/Tutorials/2d/img/antialiasing_msaa_4x.webp) + +![Image](/img/Tutorials/2d/img/antialiasing_msaa_8x.webp) \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/2d/2d_lights_and_shadows.md b/Redot-Documentation/docs/26.1/Tutorials/2d/2d_lights_and_shadows.md new file mode 100644 index 0000000..173c5c4 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/2d/2d_lights_and_shadows.md @@ -0,0 +1,342 @@ + +# 2D lights and shadows + +## Introduction + +By default, 2D scenes in Redot are unshaded, with no lights and shadows visible. +While this is fast to render, unshaded scenes can look bland. Redot provides the +ability to use real-time 2D lighting and shadows, which can greatly enhance the +sense of depth in your project. + +![Image](/img/Tutorials/2d/img/2d_lights_and_shadows_disabled.webp) + + No 2D lights or shadows, scene is unshaded + +![Image](/img/Tutorials/2d/img/2d_lights_and_shadows_enabled_no_shadows.webp) + + 2D lights enabled (without shadows) + +![Image](/img/Tutorials/2d/img/2d_lights_and_shadows_enabled.webp) + + 2D lights and shadows enabled + +## Nodes + +There are several nodes involved in a complete 2D lighting setup: + +- [CanvasModulate ](class_CanvasModulate) (to darken the rest of the scene) +- [PointLight2D ](class_PointLight2D) (for omnidirectional or spot lights) +- [DirectionalLight2D ](class_DirectionalLight2D) (for sunlight or moonlight) +- [LightOccluder2D ](class_LightOccluder2D) (for light shadow casters) +- Other 2D nodes that receive lighting, such as Sprite2D or TileMapLayer. + +[CanvasModulate ](class_CanvasModulate) is used to darken the scene by +specifying a color that will act as the base "ambient" color. This is the final +lighting color in areas that are *not* reached by any 2D light. Without a +CanvasModulate node, the final scene would look too bright as 2D lights would +only brighten the existing unshaded appearance (which appears fully lit). + +[Sprite2Ds ](class_Sprite2D) are used to display the textures for the light +blobs, the background, and for the shadow casters. + +[PointLight2Ds ](class_PointLight2D) are used to light the scene. The way a +light typically works is by adding a selected texture over the rest of the scene +to simulate lighting. + +[LightOccluder2Ds ](class_LightOccluder2D) are used to tell the shader +which parts of the scene cast shadows. These occluders can be placed as +independent nodes or can be part of a TileMapLayer node. + +The shadows appear only on areas covered by the [PointLight2D ](class_PointLight2D) and their direction is based on the center of the +[Light ](class_PointLight2D). + +:::note + +The background color does **not** receive any lighting. If you want light to +be cast on the background, you need to add a visual representation for the +background, such as a Sprite2D. + +The Sprite2D's **Region** properties can be helpful to quickly create a +repeating background texture, but remember to also set **Texture > Repeat** to +**Enabled** in the Sprite2D's properties. + +::: + +## Point lights + +Point lights (also called positional lights) are the most common element in 2D +lighting. Point lights can be used to represent light from torches, fire, +projectiles, etc. + +PointLight2D offers the following properties to tweak in the inspector: + +- **Texture:** The texture to use as a light source. The texture's size + determines the size of the light. The texture may have an alpha channel, which + is useful when using Light2D's **Mix** blend mode, but it is not required if + using the **Add** (default) or **Subtract** blend modes. +- **Offset:** The offset for the light texture. Unlike when you move the light + node, changing the offset does *not* cause shadows to move. +- **Texture Scale:** The multiplier for the light's size. Higher values will + make the light extend out further. Larger lights have a higher performance + cost as they affect more pixels on screen, so consider this before increasing + a light's size. +- **Height:** The light's virtual height with regards to normal mapping. By + default, the light is very close to surfaces receiving lights. This will make + lighting hardly visible if normal mapping is used, so consider increasing this + value. Adjusting the light's height only makes a visible difference on + surfaces that use normal mapping. + +If you don't have a pre-made texture to use in a light, you can use this "neutral" +point light texture (right-click > **Save Image As…**): + +![Image](/img/Tutorials/2d/img/2d_lights_and_shadows_neutral_point_light.webp) + + Neutral point light texture + +If you need different falloff, you can procedurally create a texture by assigning +a **New GradientTexture2D** on the light's **Texture** property. After creating +the resource, expand its **Fill** section and set the fill mode to **Radial**. +You will then have to adjust the gradient itself to start from opaque white to +transparent white, and move its starting location to be in the center. + +## Directional light + +New in Redot 4.0 is the ability to have directional lighting in 2D. Directional +lighting is used to represent sunlight or moonlight. Light rays are casted +parallel to each other, as if the sun or moon was infinitely far away from the +surface that is receiving the light. + +DirectionalLight2D offers the following properties: + +- **Height:** The light's virtual height with regards to normal mapping (``0.0`` + = parallel to surfaces, ``1.0`` = perpendicular to surfaces). By default, the + light is fully parallel with the surfaces receiving lights. This will make + lighting hardly visible if normal mapping is used, so consider increasing this + value. Adjusting the light's height only makes a visual difference on surfaces + that use normal mapping. **Height** does not affect shadows' appearance. +- **Max Distance:** The maximum distance from the camera center objects can be + before their shadows are culled (in pixels). Decreasing this value can prevent + objects located outside the camera from casting shadows (while also improving + performance). Camera2D zoom is not taken into account by **Max Distance**, + which means that at higher zoom values, shadows will appear to fade out sooner + when zooming onto a given point. + +:::note + +Directional shadows will always appear to be infinitely long, regardless +of the value of the **Height** property. This is a limitation of the shadow +rendering method used for 2D lights in Redot. + +To have directional shadows that are not infinitely long, you should disable +shadows in the DirectionalLight2D and use a custom shader that reads from +the 2D signed distance field instead. This distance field is automatically +generated from LightOccluder2D nodes present in the scene. + +::: + +## Common light properties + +Both PointLight2D and DirectionalLight2D offer common properties, which are part +of the Light2D base class: + +- **Enabled:** Allows toggling the light's visibility. Unlike hiding the light + node, disabling this property will not hide the light's children. +- **Editor Only:** If enabled, the light is only visible within the editor. It + will be automatically disabled in the running project. +- **Color:** The light's color. +- **Energy:** The light's intensity multiplier. Higher values result in a brighter light. +- **Blend Mode:** The blending formula used for light computations. The default + **Add** is suited for most use cases. **Subtract** can be used for negative + lights, which are not physically accurate but can be used for special effects. + The **Mix** blend mode mixes the value of pixels corresponding to the light's + texture with the values of pixels under it by linear interpolation. +- **Range > Z Min:** The lowest Z index affected by the light. +- **Range > Z Max:** The highest Z index affected by the light. +- **Range > Layer Min:** The lowest visual layer affected by the light. +- **Range > Layer Max:** The highest visual layer affected by the light. +- **Range > Item Cull Mask:** Controls which nodes receive light from this node, + depending on the other nodes' enabled visual layers **Occluder Light Mask**. + This can be used to prevent certain objects from receiving light. + +## Setting up shadows + +After enabling the **Shadow > Enabled** property on a PointLight2D or +DirectionalLight2D node, you will not see any visual difference initially. This +is because no nodes in your scene have any *occluders* yet, which are used as a +basis for shadow casting. + +For shadows to appear in the scene, LightOccluder2D nodes must be added to the +scene. These nodes must also have occluder polygons that are designed to match +the sprite's outline. + +Along with their polygon resource (which must be set to have any visual effect), +LightOccluder2D nodes have 2 properties: + +- **SDF Collision:** If enabled, the occluder will be part of a real-time + generated *signed distance field* that can be used in custom shaders. When not + using custom shaders that read from this SDF, enabling this makes no visual + difference and has no performance cost, so this is enabled by default for + convenience. +- **Occluder Light Mask:** This is used in tandem with PointLight2D and + DirectionalLight2D's **Shadow > Item Cull Mask** property to control which + objects cast shadows for each light. This can be used to prevent specific + objects from casting shadows. + +There are two ways to create light occluders: + +### Automatically generating a light occluder + +Occluders can be created automatically from Sprite2D nodes by selecting the +node, clicking the **Sprite2D** menu at the top of the 2D editor then choosing +**Create LightOccluder2D Sibling**. + +In the dialog that appears, an outline will surround your sprite's edges. If the +outline matches the sprite's edges closely, you can click **OK**. If the outline +is too far away from the sprite's edges (or is "eating" into the sprite's +edges), adjust **Grow (pixels)** and **Shrink (pixels)**, then click **Update +Preview**. Repeat this operation until you get satisfactory results. + +### Manually drawing a light occluder + +Create a LightOccluder2D node, then select the node and click the "+" button at +the top of the 2D editor. When asked to create a polygon resource, answer +**Yes**. You can then start drawing an occluder polygon by clicking to create +new points. You can remove existing points by right-clicking them, and you can +create new points from the existing line by clicking on the line then dragging. + +The following properties can be adjusted on 2D lights that have shadows enabled: + +- **Color:** The color of shaded areas. By default, shaded areas are fully + black, but this can be changed for artistic purposes. The color's alpha + channel controls how much the shadow is tinted by the specified color. +- **Filter:** The filter mode to use for shadows. The default **None** is the + fastest to render, and is well suited for games with a pixel art aesthetic + (due to its "blocky" visuals). If you want a soft shadow, use **PCF5** + instead. **PCF13** is even softer, but is the most demanding to render. PCF13 + should only be used for a few lights at once due to its high rendering cost. +- **Filter Smooth:** Controls how much softening is applied to shadows when + **Filter** is set to **PCF5** or **PCF13**. Higher values result in a softer + shadow, but may cause banding artifacts to be visible (especially with PCF5). +- **Item Cull Mask:** Controls which LightOccluder2D nodes cast shadows, + depending on their respective **Occluder Light Mask** properties. + +![Image](/img/Tutorials/2d/img/2d_lights_and_shadows_hard_shadow.webp) + + Hard shadows + +![Image](/img/Tutorials/2d/img/2d_lights_and_shadows_soft_shadow.webp) + + Soft shadows (PCF13, Filter Smooth 1.5) + +![Image](/img/Tutorials/2d/img/2d_lights_and_shadows_soft_shadow_streaks.webp) + + Soft shadows with streaking artifacts due to Filter Smooth being too high (PCF5, Filter Smooth 4) + +### Occluder draw order + +**LightOccluder2Ds follows the usual 2D drawing order.** This is important for 2D +lighting, as this is how you control whether the occluder should occlude the +sprite itself or not. + +If the LightOccluder2D node is a *sibling* of the sprite, the occluder will +occlude the sprite itself if it's placed *below* the sprite in the scene tree. + +If the LightOccluder2D node is a *child* of the sprite, the occluder will +occlude the sprite itself if **Show Behind Parent** is disabled on the +LightOccluder2D node (which is the default). + +## Normal and specular maps + +Normal maps and specular maps can greatly enhance the sense of depth of your 2D +lighting. Similar to how these work in 3D rendering, normal maps can help make +lighting look less flat by varying its intensity depending on the direction of +the surface receiving light (on a per-pixel basis). Specular maps further help +improve visuals by making some of the light reflect back to the viewer. + +Both PointLight2D and DirectionalLight2D support normal mapping and specular +mapping. Since Redot 4.0, normal and specular maps can be assigned to any 2D +element, including nodes that inherit from Node2D or Control. + +A normal map represents the direction in which each pixel is "pointing" towards. +This information is then used by the engine to correctly apply lighting to 2D +surfaces in a physically plausible way. Normal maps are typically created from +hand-painted height maps, but they can also be automatically generated from +other textures. + +A specular map defines how much each pixel should reflect light (and in which +color, if the specular map contains color). Brighter values will result in a +brighter reflection at that given spot on the texture. Specular maps are +typically created with manual editing, using the diffuse texture as a base. + +:::tip + +If you don't have normal or specular maps for your sprites, you can generate +them using the free and open source [Laigter ](https://azagaya.itch.io/laigter) +tool. + +::: + +To set up normal maps and/or specular maps on a 2D node, create a new +CanvasTexture resource for the property that draws the node's texture. For +example, on a Sprite2D: + +![Image](/img/Tutorials/2d/img/2d_lights_and_shadows_create_canvastexture.webp) + + Creating a CanvasTexture resource for a Sprite2D node + +Expand the newly created resource. You can find several properties you will need +to adjust: + +- **Diffuse > Texture:** The base color texture. In this property, load the + texture you're using for the sprite itself. +- **Normal Map > Texture:** The normal map texture. In this property, load a + normal map texture you've generated from a height map (see the tip above). +- **Specular > Texture:** The specular map texture, which controls the specular + intensity of each pixel on the diffuse texture. The specular map is usually + grayscale, but it can also contain color to multiply the color of reflections + accordingly. In this property, load a specular map texture you've created (see + the tip above). +- **Specular > Color:** The color multiplier for specular reflections. +- **Specular > Shininess:** The specular exponent to use for reflections. Lower + values will increase the brightness of reflections and make them more diffuse, + while higher values will make reflections more localized. High values are more + suited for wet-looking surfaces. +- **Texture > Filter:** Can be set to override the texture filtering mode, + regardless of what the node's property is set to (or the + **Rendering > Textures > Canvas Textures > Default Texture Filter** project + setting). +- **Texture > Repeat:** Can be set to override the texture filtering mode, + regardless of what the node's property is set to (or the + **Rendering > Textures > Canvas Textures > Default Texture Repeat** project + setting). + +After enabling normal mapping, you may notice that your lights appear to be +weaker. To resolve this, increase the **Height** property on your PointLight2D +and DirectionalLight2D nodes. You may also want to increase the lights's +**Energy** property slightly to get closer to how your lighting's intensity +looked prior to enabling normal mapping. + +## Using additive sprites as a faster alternative to 2D lights + +If you run into performance issues when using 2D lights, it may be worth +replacing some of them with Sprite2D nodes that use additive blending. This is +particularly suited for short-lived dynamic effects, such as bullets or explosions. + +Additive sprites are much faster to render, since they don't need to go through +a separate rendering pipeline. Additionally, it is possible to use this approach +with AnimatedSprite2D (or Sprite2D + AnimationPlayer), which allows for animated +2D "lights" to be created. + +However, additive sprites have a few downsides compared to 2D lights: + +- The blending formula is inaccurate compared to "actual" 2D lighting. This is + usually not a problem in sufficiently lit areas, but this prevents additive + sprites from correctly lighting up areas that are fully dark. +- Additive sprites cannot cast shadows, since they are not lights. +- Additive sprites ignore normal and specular maps used on other sprites. + +To display a sprite with additive blending, create a Sprite2D node and assign a +texture to it. In the inspector, scroll down to the **CanvasItem > Material** +section, unfold it and click the dropdown next to the **Material** property. +Choose **New CanvasItemMaterial**, click the newly created material to edit it, +then set **Blend Mode** to **Add**. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/2d/2d_meshes.md b/Redot-Documentation/docs/26.1/Tutorials/2d/2d_meshes.md new file mode 100644 index 0000000..9f86029 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/2d/2d_meshes.md @@ -0,0 +1,53 @@ +:::warning +This page is marked as outdated and may not reflect current Redot behavior. +::: + +# 2D meshes + +## Introduction + +In 3D, meshes are used to display the world. In 2D, they are rare as images are used more often. +Redot's 2D engine is a pure two-dimensional engine, so it can't really display 3D meshes directly (although it can be done +via ``Viewport`` and ``ViewportTexture``). + +:::info +If you are interested in displaying 3D meshes on a 2D viewport, see the :ref:`doc_viewport_as_texture` tutorial. + +::: + +2D meshes are meshes that contain two-dimensional geometry (Z can be omitted or ignored) instead of 3D. +You can experiment creating them yourself using ``SurfaceTool`` from code and displaying them in a ``MeshInstance2D`` node. + +Currently, the only way to generate a 2D mesh within the editor is by either importing an OBJ file as a mesh, or converting it from a Sprite2D. + +## Optimizing pixels drawn + +This workflow is useful for optimizing 2D drawing in some situations. When drawing large images with transparency, Redot will draw the whole quad to the screen. The large transparent areas will still be drawn. + +This can affect performance, especially on mobile devices, when drawing very large images (generally screen sized), +or layering multiple images on top of each other with large transparent areas (for example, when using ``ParallaxBackground``). + +Converting to a mesh will ensure that only the opaque parts will be drawn and the rest will be ignored. + +## Converting Sprite2Ds to 2D meshes + +You can take advantage of this optimization by converting a ``Sprite2D`` to a ``MeshInstance2D``. +Start with an image that contains large amounts of transparency on the edges, like this tree: + +![Image](/img/Tutorials/2d/img/mesh2d1.png) + +Put it in a ``Sprite2D`` and select "Convert to 2D Mesh" from the menu: + +![Image](/img/Tutorials/2d/img/mesh2d2.png) + +A dialog will appear, showing a preview of how the 2D mesh will be created: + +![Image](/img/Tutorials/2d/img/mesh2d3.png) + +The default values are good enough for many cases, but you can change growth and simplification according to your needs: + +![Image](/img/Tutorials/2d/img/mesh2d4.png) + +Finally, push the ``Convert 2D Mesh`` button and your Sprite2D will be replaced: + +![Image](/img/Tutorials/2d/img/mesh2d5.png) \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/2d/2d_movement.md b/Redot-Documentation/docs/26.1/Tutorials/2d/2d_movement.md new file mode 100644 index 0000000..2545834 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/2d/2d_movement.md @@ -0,0 +1,354 @@ + +# 2D movement overview + +## Introduction + +Every beginner has been there: "How do I move my character?" Depending on the +style of game you're making, you may have special requirements, but in general +the movement in most 2D games is based on a small number of designs. + +We'll use [CharacterBody2D ](class_CharacterBody2D) for these examples, +but the principles will apply to other node types (Area2D, RigidBody2D) as well. + +## Setup + +Each example below uses the same scene setup. Start with a ``CharacterBody2D`` with two +children: ``Sprite2D`` and ``CollisionShape2D``. You can use the Redot icon ("icon.png") +for the Sprite2D's texture or use any other 2D image you have. + +Open ``Project -> Project Settings`` and select the "Input Map" tab. Add the following +input actions (see [InputEvent ](../inputs/inputevent.md) for details): + +![Image](/img/Tutorials/2d/img/movement_inputs.webp) + +## 8-way movement + +In this scenario, you want the user to press the four directional keys (up/left/down/right +or W/A/S/D) and move in the selected direction. The name "8-way movement" comes from the +fact that the player can move diagonally by pressing two keys at the same time. + +![Image](/img/Tutorials/2d/img/movement_8way.gif) + +Add a script to the character body and add the following code: + + + + + +```gdscript +extends CharacterBody2D + +@export var speed = 400 + +func get_input(): + var input_direction = Input.get_vector("left", "right", "up", "down") + velocity = input_direction * speed + +func _physics_process(delta): + get_input() + move_and_slide() + +``` + + + + + +```csharp +using Godot; + +public partial class Movement : CharacterBody2D +{ + [Export] + public int Speed { get; set; } = 400; + + public void GetInput() + { + + Vector2 inputDirection = Input.GetVector("left", "right", "up", "down"); + Velocity = inputDirection * Speed; + } + + public override void _PhysicsProcess(double delta) + { + GetInput(); + MoveAndSlide(); + } +} + +``` + + + + + +In the ``get_input()`` function, we use [Input ](class_Input) ``get_vector()`` to check for the +four key events and sum return a direction vector. + +We can then set our velocity by multiplying this direction vector, which has a +length of ``1``, by our desired speed. + +:::tip +If you've never used vector math before, or need a refresher, +you can see an explanation of vector usage in Redot at [doc_vector_math](../math/vector_math.md). + +::: + +:::note + +If the code above does nothing when you press the keys, double-check that +you've set up input actions correctly as described in the +[doc_2d_movement_setup](doc_2d_movement_setup) part of this tutorial. + +::: + +## Rotation + movement + +This type of movement is sometimes called "Asteroids-style" because it resembles +how that classic arcade game worked. Pressing left/right rotates the character, +while up/down moves it forward or backward in whatever direction it's facing. + +![Image](/img/Tutorials/2d/img/movement_rotate1.gif) + + + + + +```gdscript +extends CharacterBody2D + +@export var speed = 400 +@export var rotation_speed = 1.5 + +var rotation_direction = 0 + +func get_input(): + rotation_direction = Input.get_axis("left", "right") + velocity = transform.x * Input.get_axis("down", "up") * speed + +func _physics_process(delta): + get_input() + rotation += rotation_direction * rotation_speed * delta + move_and_slide() + +``` + + + + + +```csharp +using Godot; + +public partial class Movement : CharacterBody2D +{ + [Export] + public int Speed { get; set; } = 400; + + [Export] + public float RotationSpeed { get; set; } = 1.5f; + + private float _rotationDirection; + + public void GetInput() + { + _rotationDirection = Input.GetAxis("left", "right"); + Velocity = Transform.X * Input.GetAxis("down", "up") * Speed; + } + + public override void _PhysicsProcess(double delta) + { + GetInput(); + Rotation += _rotationDirection * RotationSpeed * (float)delta; + MoveAndSlide(); + } +} + +``` + + + + + +Here we've added two variables to track our rotation direction and speed. +The rotation is applied directly to the body's ``rotation`` property. + +To set the velocity, we use the body's ``transform.x`` which is a vector pointing +in the body's "forward" direction, and multiply that by the speed. + +## Rotation + movement (mouse) + +This style of movement is a variation of the previous one. This time, the direction +is set by the mouse position instead of the keyboard. The character will always +"look at" the mouse pointer. The forward/back inputs remain the same, however. + +![Image](/img/Tutorials/2d/img/movement_rotate2.gif) + + + + + +```gdscript +extends CharacterBody2D + +@export var speed = 400 + +func get_input(): + look_at(get_global_mouse_position()) + velocity = transform.x * Input.get_axis("down", "up") * speed + +func _physics_process(delta): + get_input() + move_and_slide() + +``` + + + + + +```csharp +using Godot; + +public partial class Movement : CharacterBody2D +{ + [Export] + public int Speed { get; set; } = 400; + + public void GetInput() + { + LookAt(GetGlobalMousePosition()); + Velocity = Transform.X * Input.GetAxis("down", "up") * Speed; + } + + public override void _PhysicsProcess(double delta) + { + GetInput(); + MoveAndSlide(); + } +} + +``` + + + + + +Here we're using the [Node2D ](class_Node2D) ``look_at()`` method to +point the player towards the mouse's position. Without this function, you +could get the same effect by setting the angle like this: + + + + + +```gdscript +rotation = get_global_mouse_position().angle_to_point(position) + +``` + + + + + +```csharp +var rotation = GetGlobalMousePosition().AngleToPoint(Position); + +``` + + + + + +## Click-and-move + +This last example uses only the mouse to control the character. Clicking +on the screen will cause the player to move to the target location. + +![Image](/img/Tutorials/2d/img/movement_click.gif) + + + + + +```gdscript +extends CharacterBody2D + +@export var speed = 400 + +var target = position + +func _input(event): + # Use is_action_pressed to only accept single taps as input instead of mouse drags. + if event.is_action_pressed(&"click"): + target = get_global_mouse_position() + +func _physics_process(delta): + velocity = position.direction_to(target) * speed + # look_at(target) + if position.distance_to(target) > 10: + move_and_slide() + +``` + + + + + +```csharp +using Godot; + +public partial class Movement : CharacterBody2D +{ + [Export] + public int Speed { get; set; } = 400; + + private Vector2 _target; + + public override void _Input(InputEvent @event) + { + // Use IsActionPressed to only accept single taps as input instead of mouse drags. + if (@event.IsActionPressed("click")) + { + _target = GetGlobalMousePosition(); + } + } + + public override void _PhysicsProcess(double delta) + { + Velocity = Position.DirectionTo(_target) * Speed; + // LookAt(_target); + if (Position.DistanceTo(_target) > 10) + { + MoveAndSlide(); + } + } +} + +``` + + + + + +Note the ``distance_to()`` check we make prior to movement. Without this test, +the body would "jitter" upon reaching the target position, as it moves +slightly past the position and tries to move back, only to move too far and +repeat. + +Uncommenting the ``look_at()`` line will also turn the body to point in its +direction of motion if you prefer. + +:::tip +This technique can also be used as the basis of a "following" character. +The ``target`` position can be that of any object you want to move to. + +::: + +## Summary + +You may find these code samples useful as starting points for your own projects. +Feel free to use them and experiment with them to see what you can make. + +You can download this sample project here: +[2d_movement_starter.zip ](https://github.com/redot-engine/redot-docs-site-project-starters/releases/download/latest-4.x/2d_movement_starter.zip) \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/2d/2d_parallax.md b/Redot-Documentation/docs/26.1/Tutorials/2d/2d_parallax.md new file mode 100644 index 0000000..afca743 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/2d/2d_parallax.md @@ -0,0 +1,224 @@ + +# 2D Parallax + +## Introduction + +Parallax is an effect used to simulate depth by having textures move at different speeds relative to the camera. Redot +provides the [Parallax2D](class_parallax2d) node to achieve this effect. It can still be easy to get tripped +up though, so this page provides in-depth descriptions of some properties and how to fix some common mistakes. + +:::note + +This page only covers how to use [Parallax2D](class_parallax2d). This node is still experimental, so the +implementation might change in future versions of Redot. However, it is still recommended to use over the +[ParallaxLayer](class_parallaxlayer) and [ParallaxBackground](class_parallaxbackground) nodes. + +::: + +## Scroll scale + +The backbone of the parallax effect is the [scroll_scale ](class_parallax2d_property_scroll_scale) property. +It works as a scroll-speed multiplier, allowing layers to move at a different speed than the camera for each axis set. +A value of 1 makes the parallax node scroll at the same speed as the camera. If you want your image to look further away +when scrolling, use a value lower than 1, with 0 bringing it to a complete stop. If you want something to appear closer +to the camera, use a value higher than 1, making it scroll faster. + +![Image](/img/Tutorials/2d/img/2d_parallax_size_viewport.webp) + +The scene above is comprised of five layers. Some good [scroll_scale ](class_parallax2d_property_scroll_scale) +values might be: + +- ``(0.7, 1)`` - Forest +- ``(0.5, 1)`` - Hills +- ``(0.3, 1)`` - Lower Clouds +- ``(0.2, 1)`` - Higher Clouds +- ``(0.1, 1)`` - Sky + +The video below displays how these values affect scrolling while in-game: + + + +## Infinite repeat + +[Parallax2D](class_parallax2d) provides a bonus effect that gives textures the illusion of repeating infinitely. +[repeat_size](class_parallax2d_property_repeat_size) tells the node to snap its position forward or back when the +camera scrolls by the set value. This effect is achieved by adding a single repeat to all the child canvas items offset +by the value. While the camera scrolls between the image and its repeat, it invisibly snaps back giving the appearance +of a looping image. + +![Image](/img/Tutorials/2d/img/2d_parallax_scroll.gif) + +Being a delicate effect, it's easy for unfamiliar users to make mistakes with their setup. Let's go over the "how" and +"why" of a few common problems users encounter. + +### Poor sizing + +The infinite repeat effect is easiest to work with when you have an image designed to repeat seamlessly and is the same +size or larger than your viewport **before** setting the [repeat_size](class_parallax2d_property_repeat_size). If +you aren't able to obtain assets that are designed for this task, there are some other things you can do to better +prepare your image in regards to size. + +Here is an example of a texture that is too small for its viewport: + +![Image](/img/Tutorials/2d/img/2d_parallax_size_bad.webp) + +We can see that the viewport size is 500x300 but the texture is 288x208. If we set the +[repeat_size](class_parallax2d_property_repeat_size) to the size of our image, the infinite repeat effect doesn't +scroll properly because the original texture doesn't cover the viewport. If we set the +[repeat_size](class_parallax2d_property_repeat_size) to the size of the viewport, we have a large gap. What can we +do? + +#### Make the viewport smaller + +The simplest answer is to make the viewport the same size or smaller than your textures. +In **Project Settings > Display > Window**, change the +[Viewport Width](class_ProjectSettings_property_display/window/size/viewport_width) +and [Viewport Height](class_ProjectSettings_property_display/window/size/viewport_height) +settings to match your background. + +![Image](/img/Tutorials/2d/img/2d_parallax_size_viewport.webp) + +#### Scale the Parallax2D + +If you're not aiming for a pixel-perfect style, or don't mind a little blurriness, you may opt to scale the textures +larger to fit your screen. Set the [scale](class_node2d_property_scale) of the [Parallax2D](class_parallax2d), +and all child textures scale with it. + +#### Scale the child nodes + +Similar to scaling the [Parallax2D](class_parallax2d), you can scale your [Sprite2D](class_sprite2d) nodes to +be large enough to cover the screen. Keep in mind that some settings like +[Parallax2D.repeat_size](class_parallax2d_property_repeat_size) and +[Sprite2D.region_rect](class_sprite2d_property_region_rect) do not take scaling into account, so it's necessary to +adjust these values based on the scale. + +![Image](/img/Tutorials/2d/img/2d_parallax_size_scale.webp) + +#### Repeat the textures + +You can also start off on the right foot by preparing child nodes earlier in the process. If you have a +[Sprite2D](class_sprite2d) you'd like to repeat, but is too small, you can do the following to repeat it: + +- set [texture_repeat](class_canvasitem_property_texture_repeat) to [CanvasItem.TEXTURE_REPEAT_ENABLED](class_canvasitem_constant_TEXTURE_REPEAT_ENABLED) +- set [region_enabled](class_sprite2d_property_region_enabled) to ``true`` +- set the [region_rect](class_sprite2d_property_region_rect) to a multiple of the size of your texture large enough to cover the viewport. + +Below, you can see that repeating the image twice makes it large enough to cover the screen. + +![Image](/img/Tutorials/2d/img/2d_parallax_size_repeat.webp) + +### Poor positioning + +It's common to see users mistakenly set all of their textures to be centered at ``(0,0)``: + +![Image](/img/Tutorials/2d/img/2d_parallax_single_centered.webp) + +This creates problems with the infinite repeat effect and should be avoided. The "infinite repeat canvas" starts at +``(0,0)`` and expands down and to the right to the size of the [repeat_size](class_parallax2d_property_repeat_size) +value. + +![Image](/img/Tutorials/2d/img/2d_parallax_single_expand.webp) + +If the textures are centered on the ``(0,0)`` crossing, the infinite repeat canvas is only partly covered, so it +only partly repeats. + +#### Would increasing ``repeat_times`` fix this? + +Increasing [repeat_times](class_parallax2d_property_repeat_times) technically *would* work in some scenarios, but +is a brute force solution and not the problem it is designed to solve (we'll go over this in a bit). A better fix is to +understand how the repeat effect works and set up the parallax textures appropriately to begin with. + +First, check to see if any textures are spilling over onto the negative parts of the canvas. Make sure the textures +used in the parallax nodes fit inside the "infinite repeat canvas" starting at ``(0,0)``. That way, if +[Parallax2D.repeat_size](class_parallax2d_property_repeat_size) is set correctly, it should look something like +this, with one single loop of the image the same size or larger than the viewport: + +![Image](/img/Tutorials/2d/img/2d_parallax_repeat_good_norect.webp) + +If you think of how the image scrolls across the screen, it starts by displaying what's inside the red rectangle +(determined by [repeat_size](class_parallax2d_property_repeat_size)), and when it reaches what's inside the yellow +rectangle it zips the image forward to give the illusion of scrolling forever. + +![Image](/img/Tutorials/2d/img/2d_parallax_repeat_good.webp) + +If you have the image positioned away from the "infinite repeat canvas", when the camera reaches the yellow rectangle, +half of the image is cut off before it jumps forward like in the image below: + +![Image](/img/Tutorials/2d/img/2d_parallax_repeat_bad.webp) + +## Scroll offset + +If your parallax textures are already working correctly, but you prefer it to start at a different point, +[Parallax2D](class_parallax2d) comes with a [scroll_offset](class_parallax2d_property_scroll_offset) property +used to offset where the infinite repeat canvas starts. As an example, if your image is 288x208, setting +the [scroll_offset](class_parallax2d_property_scroll_offset) to ``(-144,0)`` or ``(144,0)`` allows it to begin +halfway across the image. + +## Repeat times + +Ideally, following this guide, your parallax textures are large enough to cover the screen even when zoomed out. +Until now, we have had a perfectly fitting 288x208 texture inside of a 288x208 viewport. However, problems +occur when we zoom out by setting the [Camera2D.zoom](class_camera2d_property_zoom) to ``(0.5, 0.5)``: + +![Image](/img/Tutorials/2d/img/2d_parallax_zoom_single.webp) + +Even though everything is correctly set for the viewport at the default zoom level, zooming out makes it smaller than +the viewport, breaking the infinite repeat effect. This is where +[repeat_times](class_parallax2d_property_repeat_times) can help out. Setting a value of ``3`` (one extra +repeat behind and in front), it is now large enough to accommodate the infinite repeat effect. + +![Image](/img/Tutorials/2d/img/2d_parallax_zoom_repeat_times.webp) + +If these textures were meant to be repeated vertically, we would have specified a ``y`` value for the +[repeat_size](class_parallax2d_property_repeat_size). The +[repeat_times](class_parallax2d_property_repeat_times) would automatically add a repeat above and below as well. +This is only a horizontal parallax, so it leaves an empty block above and below the image. How do we solve this? We +need to get creative! In this example, we stretch the sky higher, and grass sprite lower. The textures now support the +normal zoom level and zooming out to half size. + +![Image](/img/Tutorials/2d/img/2d_parallax_zoom_repeat_adjusted.webp) + +## Split screen + +Most tutorials for making a split screen game in Redot begin by writing a small script to assign +the [Viewport.world_2d](class_viewport_property_world_2d) of the first SubViewport to the second, so they have a +shared display. Questions often pop up about how to share a parallax effect between both screens. + +The parallax effect fakes a perspective by moving the positions of different textures in relation to the camera. This is +understandably problematic if you have multiple cameras, because your textures can't be in two places at once! + +This is still achievable by cloning the parallax nodes into the second (or third or fourth) +[SubViewport](class_subviewport). Here's how a setup looks for a two player game: + +![Image](/img/Tutorials/2d/img/2d_parallax_splitscreen.webp) + +Of course, now both backgrounds show in both SubViewports. What we want is for each parallax to only show in their +corresponding viewport. We can achieve this by doing the following: + +- Leave all parallax nodes at their default [visibility_layer](class_canvasitem_property_visibility_layer) of 1. +- Set the first SubViewport's [canvas_cull_mask](class_viewport_property_canvas_cull_mask) to only layers 1 and 2. +- Do the same for the second SubViewport but use layers 1 and 3. +- Give your parallax nodes in the first SubViewport a common parent and set its [visibility_layer](class_canvasitem_property_visibility_layer) to 2. +- Do the same for the second SubViewport's parallax nodes, but use a layer of 3. + +How does this work? If a canvas item has a [visibility_layer](class_canvasitem_property_visibility_layer) that +doesn't match the SubViewport's [canvas_cull_mask](class_viewport_property_canvas_cull_mask), it will hide all +children, even if they do. We use this to our advantage, letting the SubViewports cut off rendering of parallax nodes +whose parent doesn't have a supported [visibility_layer](class_canvasitem_property_visibility_layer). + +## Previewing in the editor + +Prior to 4.3, the recommendation was to place every layer in their own +[ParallaxBackground](class_parallaxbackground), enable the +[follow_viewport_enabled](class_canvaslayer_property_follow_viewport_enabled) property, and scale the individual +layer. This method has always been tricky to get right, but is still achievable by using a +[CanvasLayer](class_canvaslayer) instead of a [ParallaxBackground](class_parallaxbackground). + +:::note + +Another recommendation is [KoBeWi's "Parallax2D Preview" addon ](https://github.com/KoBeWi/Godot-Parallax2D-Preview). +It provides a few different preview modes and is very handy! + +::: diff --git a/Redot-Documentation/docs/26.1/Tutorials/2d/2d_sprite_animation.md b/Redot-Documentation/docs/26.1/Tutorials/2d/2d_sprite_animation.md new file mode 100644 index 0000000..4c46972 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/2d/2d_sprite_animation.md @@ -0,0 +1,313 @@ + +# 2D sprite animation + +## Introduction + +In this tutorial, you'll learn how to create 2D animated +characters with the AnimatedSprite2D class and the AnimationPlayer. +Typically, when you create or download an animated character, +it will come in one of two ways: as individual images or as a single sprite sheet +containing all the animation's frames. +Both can be animated in Redot with the AnimatedSprite2D class. + +First, we'll use [AnimatedSprite2D ](class_AnimatedSprite2D) to +animate a collection of individual images. +Then we will animate a sprite sheet using this class. +Finally, we will learn another way to animate a sprite sheet +with [AnimationPlayer ](class_AnimationPlayer) and the *Animation* +property of [Sprite2D ](class_Sprite2D). + +:::note +Art for the following examples by https://opengameart.org/users/ansimuz and tgfcoder. + +::: + +## Individual images with AnimatedSprite2D + +In this scenario, you have a collection of images, each containing one of your +character's animation frames. For this example, we'll use the following +animation: + +![Image](/img/Tutorials/2d/img/2d_animation_run_preview.gif) + +You can download the images here: +[2d_sprite_animation_assets.zip ](https://github.com/redot-engine/redot-docs-site-project-starters/releases/download/latest-4.x/2d_sprite_animation_assets.zip) + +Unzip the images and place them in your project folder. Set up your scene tree +with the following nodes: + +![Image](/img/Tutorials/2d/img/2d_animation_tree1.webp) + +:::note +The root node could also be :ref:`Area2D ` or +[RigidBody2D ](class_RigidBody2D). The animation will still be +made in the same way. Once the animation is completed, you can +assign a shape to the CollisionShape2D. See +[Physics Introduction ](../physics/physics_introduction.md) for more +information. + +::: + +Now select the ``AnimatedSprite2D`` and in its *SpriteFrames* property, select +"New SpriteFrames". + +![Image](/img/Tutorials/2d/img/2d_animation_new_spriteframes.webp) + +Click on the new SpriteFrames resource and you'll see a new panel appear at the +bottom of the editor window: + +![Image](/img/Tutorials/2d/img/2d_animation_spriteframes.webp) + +From the FileSystem dock on the left side, drag the 8 individual images into +the center part of the SpriteFrames panel. On the left side, change the name +of the animation from "default" to "run". + +![Image](/img/Tutorials/2d/img/2d_animation_spriteframes_done.webp) + +Use the "Play" buttons on the top-right of the *Filter Animations* input to preview the animation. +You should now see the animation playing in the viewport. +However, it is a bit slow. To fix this, +change the *Speed (FPS)* setting in the SpriteFrames panel to 10. + +You can add additional animations by clicking the "Add Animation" button and +adding additional images. + +### Controlling the animation + +Once the animation is complete, you can control the animation via code using +the ``play()`` and ``stop()`` methods. Here is a brief example to play the +animation while the right arrow key is held, and stop it when the key is +released. + + + + + +```gdscript +extends CharacterBody2D + +@onready var _animated_sprite = $AnimatedSprite2D + +func _process(_delta): + if Input.is_action_pressed("ui_right"): + _animated_sprite.play("run") + else: + _animated_sprite.stop() + +``` + + + + + +```csharp +using Godot; + +public partial class Character : CharacterBody2D +{ + private AnimatedSprite2D _animatedSprite; + + public override void _Ready() + { + _animatedSprite = GetNode("AnimatedSprite2D"); + } + + public override _Process(float _delta) + { + if (Input.IsActionPressed("ui_right")) + { + _animatedSprite.Play("run"); + } + else + { + _animatedSprite.Stop(); + } + } +} + +``` + + + + + +## Sprite sheet with AnimatedSprite2D + +You can also easily animate from a sprite sheet with the class ``AnimatedSprite2D``. +We will use this public domain sprite sheet: + +![Image](/img/Tutorials/2d/img/2d_animation_frog_spritesheet.png) + +Right-click the image and choose "Save Image As" to download it, +and then copy the image into your project folder. + +Set up your scene tree the same way you did previously when using individual images. +Select the ``AnimatedSprite2D`` and in its *SpriteFrames* property, select "New SpriteFrames". + +Click on the new SpriteFrames resource. +This time, when the bottom panel appears, select "Add frames from a Sprite Sheet". + +![Image](/img/Tutorials/2d/img/2d_animation_add_from_spritesheet.webp) + +You will be prompted to open a file. Select your sprite sheet. + +A new window will open, showing your sprite sheet. +The first thing you will need to do is to change the number of vertical and horizontal images in your sprite sheet. +In this sprite sheet, we have four images horizontally and two images vertically. + +![Image](/img/Tutorials/2d/img/2d_animation_spritesheet_select_rows.webp) + +Next, select the frames from the sprite sheet that you want to include in your animation. +We will select the top four, then click "Add 4 frames" to create the animation. + +![Image](/img/Tutorials/2d/img/2d_animation_spritesheet_selectframes.webp) + +You will now see your animation under the list of animations in the bottom panel. +Double click on default to change the name of the animation to jump. + +![Image](/img/Tutorials/2d/img/2d_animation_spritesheet_animation.webp) + +Finally, check the play button on the SpriteFrames editor to see your frog jump! + +![Image](/img/Tutorials/2d/img/2d_animation_play_spritesheet_animation.webp) + +## Sprite sheet with AnimationPlayer + +Another way that you can animate when using a sprite sheet is to use a standard +[Sprite2D ](class_Sprite2D) node to display the texture, and then animating the +change from texture to texture with [AnimationPlayer ](class_AnimationPlayer). + +Consider this sprite sheet, which contains 6 frames of animation: + +![Image](/img/Tutorials/2d/img/2d_animation_player-run.png) + +Right-click the image and choose "Save Image As" to download, then copy the +image into your project folder. + +Our goal is to display these images one after another in a loop. Start by +setting up your scene tree: + +![Image](/img/Tutorials/2d/img/2d_animation_tree2.webp) + +:::note +The root node could also be :ref:`Area2D ` or +[RigidBody2D ](class_RigidBody2D). The animation will still be +made in the same way. Once the animation is completed, you can +assign a shape to the CollisionShape2D. See +[Physics Introduction ](../physics/physics_introduction.md) for more +information. + +::: + +Drag the spritesheet into the Sprite's *Texture* property, and you'll see the +whole sheet displayed on the screen. To slice it up into individual frames, +expand the *Animation* section in the Inspector and set the *Hframes* to ``6``. +*Hframes* and *Vframes* are the number of horizontal and vertical frames in +your sprite sheet. + +![Image](/img/Tutorials/2d/img/2d_animation_setframes.webp) + +Now try changing the value of the *Frame* property. You'll see that it ranges +from ``0`` to ``5`` and the image displayed by the Sprite2D changes accordingly. +This is the property we'll be animating. + +Select the ``AnimationPlayer`` and click the "Animation" button followed by +"New". Name the new animation "walk". Set the animation length to ``0.6`` and +click the "Loop" button so that our animation will repeat. + +![Image](/img/Tutorials/2d/img/2d_animation_new_animation.webp) + +Now select the ``Sprite2D`` node and click the key icon to add a new track. + +![Image](/img/Tutorials/2d/img/2d_animation_new_track.webp) + +Continue adding frames at each point in the timeline (``0.1`` seconds by +default), until you have all the frames from 0 to 5. You'll see the frames +actually appearing in the animation track: + +![Image](/img/Tutorials/2d/img/2d_animation_full_animation.webp) + +Press "Play" on the animation to see how it looks. + +![Image](/img/Tutorials/2d/img/2d_animation_running.gif) + +### Controlling an AnimationPlayer animation + +Like with AnimatedSprite2D, you can control the animation via code using +the ``play()`` and ``stop()`` methods. Again, here is an example to play the +animation while the right arrow key is held, and stop it when the key is +released. + + + + + +```gdscript +extends CharacterBody2D + +@onready var _animation_player = $AnimationPlayer + +func _process(_delta): + if Input.is_action_pressed("ui_right"): + _animation_player.play("walk") + else: + _animation_player.stop() + +``` + + + + + +```csharp +using Godot; + +public partial class Character : CharacterBody2D +{ + private AnimationPlayer _animationPlayer; + + public override void _Ready() + { + _animationPlayer = GetNode("AnimationPlayer"); + } + + public override void _Process(float _delta) + { + if (Input.IsActionPressed("ui_right")) + { + _animationPlayer.Play("walk"); + } + else + { + _animationPlayer.Stop(); + } + } +} + +``` + + + + + +:::note +If updating both an animation and a separate property at once +(for example, a platformer may update the sprite's ``h_flip``/``v_flip`` +properties when a character turns while starting a 'turning' animation), +it's important to keep in mind that ``play()`` isn't applied instantly. +Instead, it's applied the next time the [AnimationPlayer ](class_AnimationPlayer) is processed. +This may end up being on the next frame, causing a 'glitch' frame, +where the property change was applied, but the animation was not. +If this turns out to be a problem, after calling ``play()``, you can call ``advance(0)`` +to update the animation immediately. + +::: + +## Summary + +These examples illustrate the two classes you can use in Redot for 2D animation. +``AnimationPlayer`` is a bit more complex than ``AnimatedSprite2D``, +but it provides additional functionality, since you can also +animate other properties like position or scale. +The class ``AnimationPlayer`` can also be used with an ``AnimatedSprite2D``. +Experiment to see what works best for your needs. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/2d/2d_transforms.md b/Redot-Documentation/docs/26.1/Tutorials/2d/2d_transforms.md new file mode 100644 index 0000000..6eb3a44 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/2d/2d_transforms.md @@ -0,0 +1,158 @@ + +# Viewport and canvas transforms + +## Introduction + +This is an overview of the 2D transforms going on for nodes from the +moment they draw their content locally to the time they are drawn onto +the screen. This overview discusses very low-level details of the engine. + +The goal of this tutorial is to teach a way for feeding input events to the +Input with a position in the correct coordinate system. + +A more extensive description of all coordinate systems and 2d transforms is +available in [doc_2d_coordinate_systems](../../Contributing/Development/core_and_modules/2d_coordinate_systems.md). + +## Canvas transform + +As mentioned in the previous tutorial, [doc_canvas_layers](canvas_layers.md), every +CanvasItem node (remember that Node2D and Control based nodes use +CanvasItem as their common root) will reside in a *Canvas Layer*. Every +canvas layer has a transform (translation, rotation, scale, etc.) that +can be accessed as a [Transform2D ](class_Transform2D). + +Also covered in the previous tutorial, nodes are drawn by default in Layer 0, +in the built-in canvas. To put nodes in a different layer, a [CanvasLayer ](class_CanvasLayer) node can be used. + +## Global canvas transform + +Viewports also have a Global Canvas transform (also a +[Transform2D ](class_Transform2D)). This is the master transform and +affects all individual *Canvas Layer* transforms. Generally, this is primarily +used in Redot's CanvasItem Editor. + +## Stretch transform + +Finally, viewports have a *Stretch Transform*, which is used when +resizing or stretching the screen. This transform is used internally (as +described in [doc_multiple_resolutions](../rendering/multiple_resolutions.md)), but can also be manually set +on each viewport. + +Input events are multiplied by this transform, but lack the ones above. To +convert InputEvent coordinates to local CanvasItem coordinates, the +[CanvasItem.make_input_local() ](class_CanvasItem_method_make_input_local) +function was added for convenience. + +## Window transform + +The root viewport is a [Window ](class_Window). In order to scale and +position the *Window's* content as described in [doc_multiple_resolutions](../rendering/multiple_resolutions.md), +each [Window ](class_Window) contains a *window transform*. It is for +example responsible for the black bars at the *Window's* sides so that the +*Viewport* is displayed with a fixed aspect ratio. + +## Transform order + +To convert a CanvasItem local coordinate to an actual screen coordinate, +the following chain of transforms must be applied: + +![Image](/img/Tutorials/2d/img/viewport_transforms3.webp) + +## Transform functions + +The above graphic shows some available transform functions. All transforms are directed from right +to left, this means multiplying a transform with a coordinate results in a coordinate system +further to the left, multiplying the [affine inverse ](class_Transform2D_method_affine_inverse) +of a transform results in a coordinate system further to the right: + + + + + +```gdscript +# Called from a CanvasItem. +canvas_pos = get_global_transform() * local_pos +local_pos = get_global_transform().affine_inverse() * canvas_pos + +``` + + + + + +```csharp +// Called from a CanvasItem. +canvasPos = GetGlobalTransform() * localPos; +localPos = GetGlobalTransform().AffineInverse() * canvasPos; + +``` + + + + + +Finally, then, to convert a CanvasItem local coordinates to screen coordinates, just multiply in +the following order: + + + + + +```gdscript +var screen_coord = get_viewport().get_screen_transform() * get_global_transform_with_canvas() * local_pos + +``` + + + + + +```csharp +var screenCoord = GetViewport().GetScreenTransform() * GetGlobalTransformWithCanvas() * localPos; + +``` + + + + + +Keep in mind, however, that it is generally not desired to work with screen coordinates. The +recommended approach is to simply work in Canvas coordinates +(``CanvasItem.get_global_transform()``), to allow automatic screen resolution resizing to work +properly. + +## Feeding custom input events + +It is often desired to feed custom input events to the game. With the above knowledge, to correctly +do this in the focused window, it must be done the following way: + + + + + +```gdscript +var local_pos = Vector2(10, 20) # Local to Control/Node2D. +var ie = InputEventMouseButton.new() +ie.button_index = MOUSE_BUTTON_LEFT +ie.position = get_viewport().get_screen_transform() * get_global_transform_with_canvas() * local_pos +Input.parse_input_event(ie) + +``` + + + + + +```csharp +var localPos = new Vector2(10,20); // Local to Control/Node2D. +var ie = new InputEventMouseButton() +{ + ButtonIndex = MouseButton.Left, + Position = GetViewport().GetScreenTransform() * GetGlobalTransformWithCanvas() * localPos, +}; +Input.ParseInputEvent(ie); +``` + + + + diff --git a/Redot-Documentation/docs/26.1/Tutorials/2d/canvas_layers.md b/Redot-Documentation/docs/26.1/Tutorials/2d/canvas_layers.md new file mode 100644 index 0000000..28efc1a --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/2d/canvas_layers.md @@ -0,0 +1,63 @@ + +# Canvas layers + +## Viewport and Canvas items + +[CanvasItem ](class_CanvasItem) is the base for all 2D nodes, be it regular +2D nodes, such as [Node2D ](class_Node2D), or [Control ](class_Control). +Both inherit from [CanvasItem ](class_CanvasItem). +You can arrange canvas items in trees. Each item will inherit its parent's +transform: when the parent moves, its children move too. + +CanvasItem nodes, and nodes inheriting from them, are direct or indirect children of a +[Viewport ](class_Viewport), that displays them. + +The Viewport's property +[Viewport.canvas_transform ](class_Viewport_property_canvas_transform), +allows to apply a custom [Transform2D ](class_Transform2D) +transform to the CanvasItem hierarchy it contains. Nodes such as +[Camera2D ](class_Camera2D) work by changing that transform. + +To achieve effects like scrolling, manipulating the canvas transform property is +more efficient than moving the root canvas item and the entire scene with it. + +Usually though, we don't want *everything* in the game or app to be subject to the canvas +transform. For example: + +- **Parallax Backgrounds**: Backgrounds that move slower than the rest + of the stage. +- **UI**: Think of a user interface (UI) or head-up display (HUD) superimposed on our view of the game world. We want a life counter, score display and other elements to retain their screen positions even when our view of the game world changes. +- **Transitions**: We may want visual effects used for transitions (fades, blends) to remain at a fixed screen location. + +How to solve these problems in a single scene tree? + +## CanvasLayers + +The answer is [CanvasLayer ](class_CanvasLayer), +which is a node that adds a separate 2D rendering layer for all its +children and grand-children. Viewport children will draw by default at +layer "0", while a CanvasLayer will draw at any numeric layer. Layers +with a greater number will be drawn above those with a smaller number. +CanvasLayers also have their own transform and do not depend on the +transform of other layers. This allows the UI to be fixed in screen-space +while our view on the game world changes. + +An example of this is creating a parallax background. This can be done +with a CanvasLayer at layer "-1". The screen with the points, life +counter and pause button can also be created at layer "1". + +Here's a diagram of how it looks: + +![Image](/img/Tutorials/2d/img/canvaslayers.png) + +CanvasLayers are independent of tree order, and they only depend on +their layer number, so they can be instantiated when needed. + +:::note +CanvasLayers aren't necessary to control the drawing order of nodes. +The standard way to ensuring that a node is correctly drawn 'in front' or 'behind' others is to manipulate the +order of the nodes in the scene panel. Perhaps counterintuitively, the topmost nodes in the scene panel are drawn +on *behind* lower ones in the viewport. 2D nodes also have the [CanvasItem.z_index ](class_CanvasItem_property_z_index) +property for controlling their drawing order. + +::: diff --git a/Redot-Documentation/docs/26.1/Tutorials/2d/custom_drawing_in_2d.md b/Redot-Documentation/docs/26.1/Tutorials/2d/custom_drawing_in_2d.md new file mode 100644 index 0000000..026d20d --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/2d/custom_drawing_in_2d.md @@ -0,0 +1,1304 @@ + +# Custom drawing in 2D + +## Introduction + +Redot has nodes to draw sprites, polygons, particles, text, and many other +common game development needs. However, if you need something specific +not covered with the standard nodes you can make any 2D node (for example, +[Control ](class_Control) or [Node2D ](class_Node2D)-based) +draw on screen using custom commands. + +Custom drawing in a 2D node is *really* useful. Here are some use cases: + +- Drawing shapes or logic that existing nodes can't do, such as an image + with trails or a special animated polygon. +- Drawing a large number of simple objects, such as a grid or a board + for a 2d game. Custom drawing avoids the overhead of using a large number + of nodes, possibly lowering memory usage and improving performance. +- Making a custom UI control. There are plenty of controls available, + but when you have unusual needs, you will likely need a custom + control. + +## Drawing + +Add a script to any [CanvasItem ](class_CanvasItem) +derived node, like [Control ](class_Control) or +[Node2D ](class_Node2D). Then override the +[_draw()](class_CanvasItem_private_method__draw) function. + + + + + +```gdscript +extends Node2D + +func _draw(): + pass # Your draw commands here. + +``` + + + + + +```csharp +using Godot; + +public partial class MyNode2D : Node2D +{ + public override void _Draw() + { + // Your draw commands here. + } +} + +``` + + + + + +Draw commands are described in the [CanvasItem ](class_CanvasItem) +class reference. There are plenty of them and we will see some of them +in the examples below. + +## Updating + +The [_draw ](class_CanvasItem_private_method__draw) function is only called +once, and then the draw commands are cached and remembered, so further calls +are unnecessary. + +If re-drawing is required because a variable or something else changed, +call [CanvasItem.queue_redraw ](class_CanvasItem_method_queue_redraw) +in that same node and a new ``_draw()`` call will happen. + +Here is a little more complex example, where we have a texture variable +that can be modified at any time, and using a +[setter](doc_gdscript_basics_setters_getters), it forces a redraw +of the texture when modified: + + + + + +```gdscript +extends Node2D + +@export var texture : Texture2D: + set(value): + texture = value + queue_redraw() + +func _draw(): + draw_texture(texture, Vector2()) + +``` + + + + + +```csharp +using Godot; + +public partial class MyNode2D : Node2D +{ + private Texture2D _texture; + + [Export] + public Texture2D Texture + { + get + { + return _texture; + } + + set + { + _texture = value; + QueueRedraw(); + } + } + + public override void _Draw() + { + DrawTexture(_texture, new Vector2()); + } +} + +``` + + + + + +To see it in action, you can set the texture to be the Redot icon on the +editor by dragging and dropping the default ``icon.svg`` from the +``FileSystem`` tab to the Texture property on the ``Inspector`` tab. +When changing the ``Texture`` property value while the previous script is +running, the texture will also change automatically. + +In some cases, we may need to redraw every frame. For this, +call [queue_redraw ](class_CanvasItem_method_queue_redraw) +from the [_process ](class_Node_private_method__process) method, like this: + + + + + +```gdscript +extends Node2D + +func _draw(): + pass # Your draw commands here. + +func _process(_delta): + queue_redraw() + +``` + + + + + +```csharp +using Godot; + +public partial class MyNode2D : Node2D +{ + public override void _Draw() + { + // Your draw commands here. + } + + public override void _Process(double delta) + { + QueueRedraw(); + } +} + +``` + + + + + +## Coordinates and line width alignment + +The drawing API uses the CanvasItem's coordinate system, not necessarily pixel +coordinates. This means ``_draw()`` uses the coordinate space created after +applying the CanvasItem's transform. Additionally, you can apply a custom +transform on top of it by using +[draw_set_transform](class_CanvasItem_method_draw_set_transform) or +[draw_set_transform_matrix](class_CanvasItem_method_draw_set_transform_matrix). + +When using [draw_line ](class_CanvasItem_method_draw_line), you should +consider the width of the line. When using a width that is an odd size, the +position of the start and end points should be shifted by ``0.5`` to keep the +line centered, as shown below. + +![Image](/img/Tutorials/2d/img/draw_line.png) + + + + + +```gdscript +func _draw(): + draw_line(Vector2(1.5, 1.0), Vector2(1.5, 4.0), Color.GREEN, 1.0) + draw_line(Vector2(4.0, 1.0), Vector2(4.0, 4.0), Color.GREEN, 2.0) + draw_line(Vector2(7.5, 1.0), Vector2(7.5, 4.0), Color.GREEN, 3.0) + +``` + + + + + +```csharp +public override void _Draw() +{ + DrawLine(new Vector2(1.5f, 1.0f), new Vector2(1.5f, 4.0f), Colors.Green, 1.0f); + DrawLine(new Vector2(4.0f, 1.0f), new Vector2(4.0f, 4.0f), Colors.Green, 2.0f); + DrawLine(new Vector2(7.5f, 1.0f), new Vector2(7.5f, 4.0f), Colors.Green, 3.0f); +} + +``` + + + + + +The same applies to the [draw_rect ](class_CanvasItem_method_draw_rect) +method with ``filled = false``. + +![Image](/img/Tutorials/2d/img/draw_rect.png) + + + + + +```gdscript +func _draw(): + draw_rect(Rect2(1.0, 1.0, 3.0, 3.0), Color.GREEN) + draw_rect(Rect2(5.5, 1.5, 2.0, 2.0), Color.GREEN, false, 1.0) + draw_rect(Rect2(9.0, 1.0, 5.0, 5.0), Color.GREEN) + draw_rect(Rect2(16.0, 2.0, 3.0, 3.0), Color.GREEN, false, 2.0) + +``` + + + + + +```csharp +public override void _Draw() +{ + DrawRect(new Rect2(1.0f, 1.0f, 3.0f, 3.0f), Colors.Green); + DrawRect(new Rect2(5.5f, 1.5f, 2.0f, 2.0f), Colors.Green, false, 1.0f); + DrawRect(new Rect2(9.0f, 1.0f, 5.0f, 5.0f), Colors.Green); + DrawRect(new Rect2(16.0f, 2.0f, 3.0f, 3.0f), Colors.Green, false, 2.0f); +} + +``` + + + + + +## Antialiased drawing + +Redot offers method parameters in [draw_line](class_CanvasItem_method_draw_line) +to enable antialiasing, but not all custom drawing methods offer this ``antialiased`` +parameter. + +For custom drawing methods that don't provide an ``antialiased`` parameter, +you can enable 2D MSAA instead, which affects rendering in the entire viewport. +This provides high-quality antialiasing, but a higher performance cost and only +on specific elements. See [doc_2d_antialiasing](2d_antialiasing.md) for more information. + +Here is a comparison of a line of minimal width (``width=-1``) drawn with +``antialiased=false``, ``antialiased=true``, and ``antialiased=false`` with +2D MSAA 2x, 4x, and 8x enabled. + +![Image](/img/Tutorials/2d/img/draw_antialiasing_options.webp) + +## Tools + +Drawing your own nodes might also be desired while running them in the +editor. This can be used as a preview or visualization of some feature or +behavior. + +To do this, you can use the [tool annotation](doc_gdscript_tool_mode) +on both GDScript and C#. See +[the example below](doc_draw_show_drawing_while_editing_example) and +[doc_running_code_in_the_editor](../plugins/running_code_in_the_editor.md) for more information. + +## Example 1: drawing a custom shape + +We will now use the custom drawing functionality of the Redot Engine to draw +something that Redot doesn't provide functions for. We will recreate the Redot +logo but with code- only using drawing functions. + +You will have to code a function to perform this and draw it yourself. + +:::note + +The following instructions use a fixed set of coordinates that could be too small +for high resolution screens (larger than 1080p). If that is your case, and the +drawing is too small consider increasing your window scale in the project setting +[Display > Window > Stretch > Scale](class_ProjectSettings_property_display/window/stretch/scale) +to adjust the project to a higher resolution (a 2 or 4 scale tends to work well). + +::: + +### Drawing a custom polygon shape + +While there is a dedicated node to draw custom polygons ( +[Polygon2D ](class_Polygon2D)), we will use in this case exclusively lower +level drawing functions to combine them on the same node and be able to create +more complex shapes later on. + +First, we will define a set of points -or X and Y coordinates- that will form +the base of our shape: + + + + + +```gdscript +extends Node2D + +var coords_head : Array = [ + [ 22.952, 83.271 ], [ 28.385, 98.623 ], + [ 53.168, 107.647 ], [ 72.998, 107.647 ], + [ 99.546, 98.623 ], [ 105.048, 83.271 ], + [ 105.029, 55.237 ], [ 110.740, 47.082 ], + [ 102.364, 36.104 ], [ 94.050, 40.940 ], + [ 85.189, 34.445 ], [ 85.963, 24.194 ], + [ 73.507, 19.930 ], [ 68.883, 28.936 ], + [ 59.118, 28.936 ], [ 54.494, 19.930 ], + [ 42.039, 24.194 ], [ 42.814, 34.445 ], + [ 33.951, 40.940 ], [ 25.637, 36.104 ], + [ 17.262, 47.082 ], [ 22.973, 55.237 ] +] + +``` + + + + + +```csharp +using Godot; + +public partial class MyNode2D : Node2D +{ + private float[,] _coordsHead = + { + { 22.952f, 83.271f }, { 28.385f, 98.623f }, + { 53.168f, 107.647f }, { 72.998f, 107.647f }, + { 99.546f, 98.623f }, { 105.048f, 83.271f }, + { 105.029f, 55.237f }, { 110.740f, 47.082f }, + { 102.364f, 36.104f }, { 94.050f, 40.940f }, + { 85.189f, 34.445f }, { 85.963f, 24.194f }, + { 73.507f, 19.930f }, { 68.883f, 28.936f }, + { 59.118f, 28.936f }, { 54.494f, 19.930f }, + { 42.039f, 24.194f }, { 42.814f, 34.445f }, + { 33.951f, 40.940f }, { 25.637f, 36.104f }, + { 17.262f, 47.082f }, { 22.973f, 55.237f } + }; +} + +``` + + + + + +This format, while compact, is not the one that Redot understands to +draw a polygon. In a different scenario we could have to load +these coordinates from a file or calculate the positions while the +application is running, so some transformation may be needed. + +To transform these coordinates into the right format, we will create a new +method ``float_array_to_Vector2Array()``. Then we will override the ``_ready()`` +function, which Redot will call only once -at the start of the execution- +to load those coordinates into a variable: + + + + + +```gdscript +var head : PackedVector2Array + +func float_array_to_Vector2Array(coords : Array) -> PackedVector2Array: + # Convert the array of floats into a PackedVector2Array. + var array : PackedVector2Array = [] + for coord in coords: + array.append(Vector2(coord[0], coord[1])) + return array + +func _ready(): + head = float_array_to_Vector2Array(coords_head); + +``` + + + + + +```csharp +private Vector2[] _head; + +private Vector2[] FloatArrayToVector2Array(float[,] coords) +{ + // Convert the array of floats into an array of Vector2. + int size = coords.GetUpperBound(0); + Vector2[] array = new Vector2[size + 1]; + for (int i = 0; i <= size; i++) + { + array[i] = new Vector2(coords[i, 0], coords[i, 1]); + } + return array; +} + +public override void _Ready() +{ + _head = FloatArrayToVector2Array(_coordsHead); +} + +``` + + + + + +To finally draw our first shape, we will use the method +[draw_polygon ](class_CanvasItem_method_draw_polygon) +and pass the points (as an array of Vector2 coordinates) and its color, +like this: + + + + + +```gdscript +func _draw(): + # We are going to paint with this color. + var Redot_blue : Color = Color("478cbf") + # We pass the PackedVector2Array to draw the shape. + draw_polygon(head, [ Redot_blue ]) + +``` + + + + + +```csharp +public override void _Draw() +{ + // We are going to paint with this color. + Color RedotBlue = new Color("478cbf"); + // We pass the array of Vector2 to draw the shape. + DrawPolygon(_head, [RedotBlue]); +} + +``` + + + + + +When running it you should see something like this: + +![Image](/img/Tutorials/2d/img/draw_godot_logo_polygon.webp) + +Note the lower part of the logo looks segmented- this is because a low +amount of points were used to define that part. To simulate a smooth curve, +we could add more points to our array, or maybe use a mathematical function to +interpolate a curve and create a smooth shape from code (see +[example 2](doc_draw_custom_example_2)). + +Polygons will always **connect its last defined point to its first +one** in order to have a closed shape. + +### Drawing connected lines + +Drawing a sequence of connected lines that don't close down to form a polygon +is very similar to the previous method. We will use a connected set of lines to +draw Redot's logo mouth. + +First, we will define the list of coordinates that form the mouth shape, like this: + + + + + +```gdscript +var coords_mouth = [ + [ 22.817, 81.100 ], [ 38.522, 82.740 ], + [ 39.001, 90.887 ], [ 54.465, 92.204 ], + [ 55.641, 84.260 ], [ 72.418, 84.177 ], + [ 73.629, 92.158 ], [ 88.895, 90.923 ], + [ 89.556, 82.673 ], [ 105.005, 81.100 ] +] + +``` + + + + + +```csharp +private float[,] _coordsMouth = +{ + { 22.817f, 81.100f }, { 38.522f, 82.740f }, + { 39.001f, 90.887f }, { 54.465f, 92.204f }, + { 55.641f, 84.260f }, { 72.418f, 84.177f }, + { 73.629f, 92.158f }, { 88.895f, 90.923f }, + { 89.556f, 82.673f }, { 105.005f, 81.100f } +}; + +``` + + + + + +We will load these coordinates into a variable and define an additional +variable with the configurable line thickness: + + + + + +```gdscript +var mouth : PackedVector2Array +var _mouth_width : float = 4.4 + +func _ready(): + head = float_array_to_Vector2Array(coords_head); + mouth = float_array_to_Vector2Array(coords_mouth); + +``` + + + + + +```csharp +private Vector2[] _mouth; +private float _mouthWidth = 4.4f; + +public override void _Ready() +{ + _head = FloatArrayToVector2Array(_coordsHead); + _mouth = FloatArrayToVector2Array(_coordsMouth); +} + +``` + + + + + +And finally we will use the method +[draw_polyline ](class_CanvasItem_method_draw_polyline) to actually +draw the line, like this: + + + + + +```gdscript +func _draw(): + # We will use white to draw the line. + var white : Color = Color.WHITE + var Redot_blue : Color = Color("478cbf") + + draw_polygon(head, [ Redot_blue ]) + + # We draw the while line on top of the previous shape. + draw_polyline(mouth, white, _mouth_width) + +``` + + + + + +```csharp +public override void _Draw() +{ + // We will use white to draw the line. + Color white = Colors.White; + Color RedotBlue = new Color("478cbf"); + + DrawPolygon(_head, [RedotBlue]); + + // We draw the while line on top of the previous shape. + DrawPolyline(_mouth, white, _mouthWidth); +} + +``` + + + + + +You should get the following output: + +![Image](/img/Tutorials/2d/img/draw_godot_logo_polyline.webp) + +Unlike ``draw_polygon()``, polylines can only have a single unique color +for all its points (the second argument). This method has 2 additional +arguments: the width of the line (which is as small as possible by default) +and enabling or disabling the antialiasing (it is disabled by default). + +The order of the ``_draw`` calls is important- like with the Node positions on +the tree hierarchy, the different shapes will be drawn from top to bottom, +resulting in the latest shapes hiding earlier ones if they overlap. In this +case we want the mouth drawn over the head, so we put it afterwards. + +Notice how we can define colors in different ways, either with a hexadecimal +code or a predefined color name. Check the class [Color ](class_Color) for other +constants and ways to define Colors. + +### Drawing circles + +To create the eyes, we are going to add 4 additional calls to draw the eye +shapes, in different sizes, colors and positions. + +To draw a circle, you position it based on its center using the +[draw_circle ](class_CanvasItem_method_draw_circle) method. The first +parameter is a [Vector2](class_Vector2) with the coordinates of its center, the second is +its radius, and the third is its color: + + + + + +```gdscript +func _draw(): + var white : Color = Color.WHITE + var Redot_blue : Color = Color("478cbf") + var grey : Color = Color("414042") + + draw_polygon(head, [ Redot_blue ]) + draw_polyline(mouth, white, _mouth_width) + + # Four circles for the 2 eyes: 2 white, 2 grey. + draw_circle(Vector2(42.479, 65.4825), 9.3905, white) + draw_circle(Vector2(85.524, 65.4825), 9.3905, white) + draw_circle(Vector2(43.423, 65.92), 6.246, grey) + draw_circle(Vector2(84.626, 66.008), 6.246, grey) + +``` + + + + + +```csharp +public override void _Draw() +{ + Color white = Colors.White; + Color RedotBlue = new Color("478cbf"); + Color grey = new Color("414042"); + + DrawPolygon(_head, [RedotBlue]); + DrawPolyline(_mouth, white, _mouthWidth); + + // Four circles for the 2 eyes: 2 white, 2 grey. + DrawCircle(new Vector2(42.479f, 65.4825f), 9.3905f, white); + DrawCircle(new Vector2(85.524f, 65.4825f), 9.3905f, white); + DrawCircle(new Vector2(43.423f, 65.92f), 6.246f, grey); + DrawCircle(new Vector2(84.626f, 66.008f), 6.246f, grey); +} + +``` + + + + + +When executing it, you should have something like this: + +![Image](/img/Tutorials/2d/img/draw_godot_logo_circle.webp) + +For partial, unfilled arcs (portions of a circle shape between certain +arbitrary angles), you can use the method +[draw_arc ](class_CanvasItem_method_draw_arc). + +### Drawing lines + +To draw the final shape (the nose) we will use a line to approximate it. + +[draw_line ](class_CanvasItem_method_draw_line) can be used to draw +a single segment by providing its start and end coordinates as arguments, +like this: + + + + + +```gdscript +func _draw(): + var white : Color = Color.WHITE + var Redot_blue : Color = Color("478cbf") + var grey : Color = Color("414042") + + draw_polygon(head, [ Redot_blue ]) + draw_polyline(mouth, white, _mouth_width) + draw_circle(Vector2(42.479, 65.4825), 9.3905, white) + draw_circle(Vector2(85.524, 65.4825), 9.3905, white) + draw_circle(Vector2(43.423, 65.92), 6.246, grey) + draw_circle(Vector2(84.626, 66.008), 6.246, grey) + + # Draw a short but thick white vertical line for the nose. + draw_line(Vector2(64.273, 60.564), Vector2(64.273, 74.349), white, 5.8) + +``` + + + + + +```csharp +public override void _Draw() +{ + Color white = Colors.White; + Color RedotBlue = new Color("478cbf"); + Color grey = new Color("414042"); + + DrawPolygon(_head, [RedotBlue]); + DrawPolyline(_mouth, white, _mouthWidth); + DrawCircle(new Vector2(42.479f, 65.4825f), 9.3905f, white); + DrawCircle(new Vector2(85.524f, 65.4825f), 9.3905f, white); + DrawCircle(new Vector2(43.423f, 65.92f), 6.246f, grey); + DrawCircle(new Vector2(84.626f, 66.008f), 6.246f, grey); + + // Draw a short but thick white vertical line for the nose. + DrawLine(new Vector2(64.273f, 60.564f), new Vector2(64.273f, 74.349f), + white, 5.8f); +} + +``` + + + + + +You should now be able to see the following shape on screen: + +![Image](/img/Tutorials/2d/img/draw_godot_logo_line.webp) + +Note that if multiple unconnected lines are going to be drawn at the same time, +you may get additional performance by drawing all of them in a single call, using +the [draw_multiline ](class_CanvasItem_method_draw_multiline) method. + +### Drawing text + +While using the [Label ](class_Label) Node is the most common way to add +text to your application, the low-level `_draw` function includes functionality +to add text to your custom Node drawing. We will use it to add the name "Redot" +under the robot head. + +We will use the [draw_string ](class_CanvasItem_method_draw_string) method +to do it, like this: + + + + + +```gdscript +var default_font : Font = ThemeDB.fallback_font; + +func _draw(): + var white : Color = Color.WHITE + var Redot_blue : Color = Color("478cbf") + var grey : Color = Color("414042") + + draw_polygon(head, [ Redot_blue ]) + draw_polyline(mouth, white, _mouth_width) + draw_circle(Vector2(42.479, 65.4825), 9.3905, white) + draw_circle(Vector2(85.524, 65.4825), 9.3905, white) + draw_circle(Vector2(43.423, 65.92), 6.246, grey) + draw_circle(Vector2(84.626, 66.008), 6.246, grey) + draw_line(Vector2(64.273, 60.564), Vector2(64.273, 74.349), white, 5.8) + + # Draw Redot text below the logo with the default font, size 22. + draw_string(default_font, Vector2(20, 130), "Redot", + HORIZONTAL_ALIGNMENT_CENTER, 90, 22) + +``` + + + + + +```csharp +private Font _defaultFont = ThemeDB.FallbackFont; + +public override void _Draw() +{ + Color white = Colors.White; + Color RedotBlue = new Color("478cbf"); + Color grey = new Color("414042"); + + DrawPolygon(_head, [RedotBlue]); + DrawPolyline(_mouth, white, _mouthWidth); + DrawCircle(new Vector2(42.479f, 65.4825f), 9.3905f, white); + DrawCircle(new Vector2(85.524f, 65.4825f), 9.3905f, white); + DrawCircle(new Vector2(43.423f, 65.92f), 6.246f, grey); + DrawCircle(new Vector2(84.626f, 66.008f), 6.246f, grey); + DrawLine(new Vector2(64.273f, 60.564f), new Vector2(64.273f, 74.349f), + white, 5.8f); + + // Draw Redot text below the logo with the default font, size 22. + DrawString(_defaultFont, new Vector2(20f, 130f), "Redot", + HorizontalAlignment.Center, 90, 22); +} + +``` + + + + + +Here we first load into the defaultFont variable the configured default theme +font (a custom one can be set instead) and then we pass the following +parameters: font, position, text, horizontal alignment, width, and font size. + +You should see the following on your screen: + +![Image](/img/Tutorials/2d/img/draw_godot_logo_text.webp) + +Additional parameters as well as other methods related to text and characters +can be found on the [CanvasItem ](class_CanvasItem) class reference. + +### Show the drawing while editing + +While the code so far is able to draw the logo on a running window, it will +not show up on the ``2D view`` on the editor. In certain cases you would +also like to show your custom Node2D or control on the editor, to position +and scale it appropriately, like most other nodes do. + +To show the logo directly on the editor (without running it), you can use the +[@tool](doc_gdscript_tool_mode) annotation to request the custom drawing +of the node to also appear while editing, like this: + + + + + +```gdscript +@tool +extends Node2D + +``` + + + + + +```csharp +using Godot; + +[Tool] +public partial class MyNode2D : Node2D + +``` + + + + + +You will need to save your scene, rebuild your project (for C# only) and reload +the current scene manually at the menu option ``Scene > Reload Saved Scene`` +to refresh the current node in the ``2D`` view the first time you add or remove +the ``@tool`` annotation. + +### Animation + +If we wanted to make the custom shape change at runtime, we could modify the +methods called or its arguments at execution time, or apply a transform. + +For example, if we want the custom shape we just designed to rotate, we could add +the following variable and code to the ``_ready`` and ``_process`` methods: + + + + + +```gdscript +extends Node2D + +@export var rotation_speed : float = 1 # In radians per second. + +func _ready(): + rotation = 0 + ... + +func _process(delta: float): + rotation -= rotation_speed * delta + +``` + + + + + +```csharp +[Export] +public float RotationSpeed { get; set; } = 1.0f; // In radians per second. + +public override void _Ready() +{ + Rotation = 0; + ... +} + +public override void _Process(double delta) +{ + Rotation -= RotationSpeed * (float)delta; +} + +``` + + + + + +The problem with the above code is that because we have created the points +approximately on a rectangle starting from the upper left corner, the ``(0, 0)`` +coordinate and extending to the right and down, we see that the rotation is done +using the top left corner as pivot. A position transform change on the node +won't help us here, as the rotation transform is applied first. + +While we could rewrite all of the points' coordinates to be centered around +``(0, 0)``, including negative coordinates, that would be a lot of work. + +One possible way to work around this is to use the lower level +[draw_set_transform](class_CanvasItem_method_draw_set_transform) +method to fix this issue, translating all points in the CanvasItem's own space, +and then moving it back to its original place with a regular node transform, +either in the editor or in code, like this: + + + + + +```gdscript +func _ready(): + rotation = 0 + position = Vector2(60, 60) + ... + +func _draw(): + draw_set_transform(Vector2(-60, -60)) + ... + +``` + + + + + +```csharp +public override void _Ready() +{ + Rotation = 0; + Position = new Vector2(60, 60); + ... +} + +public override void _Draw() +{ + DrawSetTransform(new Vector2(-60.0f, -60.0f)); + ... +} + +``` + + + + + +This is the result, rotating around a pivot now on ``(60, 60)``: + +![Image](/img/Tutorials/2d/img/draw_godot_rotation.webp) + +If what we wanted to animate was a property inside the ``_draw()`` call, we must remember to +call ``queue_redraw()`` to force a refresh, as otherwise it would not be updated on screen. + +For example, this is how we can make the robot appear to open and close its mouth, by +changing the width of its mouth line follow a sinusoidal ([sin](class_@globalscope_method_sin)) curve: + + + + + +```gdscript +var _mouth_width : float = 4.4 +var _max_width : float = 7 +var _time : float = 0 + +func _process(delta : float): + _time += delta + _mouth_width = abs(sin(_time) * _max_width) + queue_redraw() + +func _draw(): + ... + draw_polyline(mouth, white, _mouth_width) + ... + +``` + + + + + +```csharp +private float _mouthWidth = 4.4f; +private float _maxWidth = 7f; +private float _time = 0f; + +public override void _Process(double delta) +{ + _time += (float)delta; + _mouthWidth = Mathf.Abs(Mathf.Sin(_time) * _maxWidth); + QueueRedraw(); +} + +public override void _Draw() +{ + ... + DrawPolyline(_mouth, white, _mouthWidth); + ... +} + +``` + + + + + +It will look somewhat like this when run: + +![Image](/img/Tutorials/2d/img/draw_godot_mouth_animation.webp) + +Please note that ``_mouth_width`` is a user defined property like any other +and it or any other used as a drawing argument can be animated using more +standard and high-level methods such as a [Tween](class_Tween) or an +[AnimationPlayer](class_AnimationPlayer) Node. The only difference is +that a ``queue_redraw()`` call is needed to apply those changes so they get +shown on screen. + +## Example 2: drawing a dynamic line + +The previous example was useful to learn how to draw and modify nodes with +custom shapes and animations. This could have some advantages, such as using +exact coordinates and vectors for drawing, rather than bitmaps -which means +they will scale well when transformed on screen. In some cases, similar results +could be achieved composing higher level functionality with nodes such as +[sprites](class_Sprite2D) or +[AnimatedSprites](class_AnimatedSprite2D) loading SVG resources (which are +also images defined with vectors) and the +[AnimationPlayer](class_AnimationPlayer) node. + +In other cases that will not be possible because we will not know what the +resulting graphical representation will be before running the code. Here we +will see how to draw a dynamic line whose coordinates are not known beforehand, +and are affected by the user's input. + +### Drawing a straight line between 2 points + +Let's assume we want to draw a straight line between 2 points, the first one +will be fixed on the upper left corner ``(0, 0)`` and the second will be defined +by the cursor position on screen. + +We could draw a dynamic line between those 2 points like this: + + + + + +```gdscript +extends Node2D + +var point1 : Vector2 = Vector2(0, 0) +var width : int = 10 +var color : Color = Color.GREEN + +var _point2 : Vector2 + +func _process(_delta): + var mouse_position = get_viewport().get_mouse_position() + if mouse_position != _point2: + _point2 = mouse_position + queue_redraw() + +func _draw(): + draw_line(point1, _point2, color, width) + +``` + + + + + +```csharp +using Godot; +using System; + +public partial class MyNode2DLine : Node2D +{ + public Vector2 Point1 { get; set; } = new Vector2(0f, 0f); + public int Width { get; set; } = 10; + public Color Color { get; set; } = Colors.Green; + + private Vector2 _point2; + + public override void _Process(double delta) + { + Vector2 mousePosition = GetViewport().GetMousePosition(); + if (mousePosition != _point2) + { + _point2 = mousePosition; + QueueRedraw(); + } + } + + public override void _Draw() + { + DrawLine(Point1, _point2, Color, Width); + } +} + +``` + + + + + +In this example we obtain the position of the mouse in the default viewport +every frame with the method +[get_mouse_position ](class_Viewport_method_get_mouse_position). If the +position has changed since the last draw request (a small optimization to +avoid redrawing on every frame)- we will schedule a redraw. Our ``_draw()`` +method only has one line: requesting the drawing of a green line of +width 10 pixels between the top left corner and that obtained position. + +The width, color, and position of the starting point can be configured with +with the corresponding properties. + +It should look like this when run: + +![Image](/img/Tutorials/2d/img/draw_line_between_2_points.webp) + +### Drawing an arc between 2 points + +The above example works, but we may want to join those 2 points with a +different shape or function, other than a straight line. + +Let's try now creating an arc (a portion of a circumference) between +both points. + +Exporting the line starting point, segments, width, color, and antialiasing will +allow us to modify those properties very easily directly from the editor +inspector panel: + + + + + +```gdscript +extends Node2D + +@export var point1 : Vector2 = Vector2(0, 0) +@export_range(1, 1000) var segments : int = 100 +@export var width : int = 10 +@export var color : Color = Color.GREEN +@export var antialiasing : bool = false + +var _point2 : Vector2 + +``` + + + + + +```csharp +using Godot; +using System; + +public partial class MyNode2DLine : Node2D +{ + [Export] + public Vector2 Point1 { get; set; } = new Vector2(0f, 0f); + [Export] + public float Length { get; set; } = 350f; + [Export(PropertyHint.Range, "1,1000,")] + public int Segments { get; set; } = 100; + [Export] + public int Width { get; set; } = 10; + [Export] + public Color Color { get; set; } = Colors.Green; + [Export] + public bool AntiAliasing { get; set; } = false; + + private Vector2 _point2; +} + +``` + + + + + +![Image](/img/Tutorials/2d/img/draw_dynamic_exported_properties.webp) + +To draw the arc, we can use the method +[draw_arc](class_CanvasItem_method_draw_arc). There are many +arcs that pass through 2 points, so we will chose for this example +the semicircle that has its center in the middle point between the 2 initial +points. + +Calculating this arc will be more complex than in the case of the line: + + + + + +```gdscript +func _draw(): + # Calculate the arc parameters. + var center : Vector2 = Vector2((_point2.x - point1.x) / 2, + (_point2.y - point1.y) / 2) + var radius : float = point1.distance_to(_point2) / 2 + var start_angle : float = (_point2 - point1).angle() + var end_angle : float = (point1 - _point2).angle() + if end_angle < 0: # end_angle is likely negative, normalize it. + end_angle += TAU + + # Finally, draw the arc. + draw_arc(center, radius, start_angle, end_angle, segments, color, + width, antialiasing) + +``` + + + + + +```csharp +public override void _Draw() +{ + // Calculate the arc parameters. + Vector2 center = new Vector2((_point2.X - Point1.X) / 2.0f, + (_point2.Y - Point1.Y) / 2.0f); + float radius = Point1.DistanceTo(_point2) / 2.0f; + float startAngle = (_point2 - Point1).Angle(); + float endAngle = (Point1 - _point2).Angle(); + if (endAngle < 0.0f) // endAngle is likely negative, normalize it. + { + endAngle += Mathf.Tau; + } + + // Finally, draw the arc. + DrawArc(center, radius, startAngle, endAngle, Segments, Color, + Width, AntiAliasing); +} + +``` + + + + + +The center of the semicircle will be the middle point between both points. +The radius will be half the distance between both points. +The start and end angles will be the angles of the vector from point1 +to point2 and vice-versa. +Note we had to normalize the ``end_angle`` in positive values because if +``end_angle`` is less than ``start_angle``, the arc will be drawn +counter-clockwise, which we don't want in this case (the arc would be +upside-down). + +The result should be something like this, with the arc going down and +between the points: + +![Image](/img/Tutorials/2d/img/draw_arc_between_2_points.webp) + +Feel free to play with the parameters in the inspector to obtain different +results: change the color, the width, the antialiasing, and increase the +number of segments to increase the curve smoothness, at the cost of extra +performance. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/2d/index.md b/Redot-Documentation/docs/26.1/Tutorials/2d/index.md new file mode 100644 index 0000000..cd27586 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/2d/index.md @@ -0,0 +1,20 @@ +# 2D + +This section contains tutorials and documentation about 2d in Redot Engine. + +## Articles + +- [2D antialiasing](2d_antialiasing) +- [2D lights and shadows](2d_lights_and_shadows) +- [2D meshes](2d_meshes) +- [2D movement overview](2d_movement) +- [2D Parallax](2d_parallax) +- [2D sprite animation](2d_sprite_animation) +- [Viewport and canvas transforms](2d_transforms) +- [Canvas layers](canvas_layers) +- [Custom drawing in 2D](custom_drawing_in_2d) +- [Introduction to 2D](introduction_to_2d) +- [2D particle systems](particle_systems_2d) +- [Using TileMaps](using_tilemaps) +- [Using TileSets](using_tilesets) + diff --git a/Redot-Documentation/docs/26.1/Tutorials/2d/introduction_to_2d.md b/Redot-Documentation/docs/26.1/Tutorials/2d/introduction_to_2d.md new file mode 100644 index 0000000..f54b4fc --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/2d/introduction_to_2d.md @@ -0,0 +1,314 @@ + +# Introduction to 2D + +Redot's 2D game development tools include a dedicated 2D rendering engine, physics system, +and features tailored specifically for creating 2D experiences. You can efficiently design +levels with the TileMap system, animate characters with 2D sprite or Cutout animation, +and leverage 2D lighting for dynamic scene illumination. The built-in 2D particle system +allows you to create complex visual effects, and Redot also supports custom shaders to +enhance your graphics. These features, combined with Redot's accessibility and +flexibility, provide a solid foundation for creating engaging 2D games. + +![Image](/img/Tutorials/2d/img/2d_platformer_demo.webp) + + 2D Platformer Demo available on the Asset Library. + +This page will show you the 2D workspace and how you can get to know it. + +:::tip +If you would like to get an introduction to 3D, see :ref:`doc_introduction_to_3d`. + +::: + +## 2D workspace + +You will use the 2D workspace to work with 2D scenes, design levels, or create user +interfaces. +To switch to the 2D workspace, you can either select a 2D node from the scene tree, +or use the workspace selector located at the top edge of the editor: + +![Image](/img/Tutorials/2d/img/2d_editor_viewport.webp) + +Similar to 3D, you can use the tabs below the workspace selector to change between currently +opened scenes or create a new one using the plus (+) button. The left and right docks should +be familiar from [editor introduction ](toc-editor-interface). + +Below the scene selector is the main toolbar, and beneath the main toolbar +is the 2D viewport. + +You can drag and drop compatible nodes from the FileSystem dock to add them to the +viewport as nodes. +Dragging and dropping adds the dragged node as a sibling of the selected node +(if the root node is selected, adds as a child). +Keeping `Shift` pressed when dropping adds the node as a child of the selected node. +Holding `Alt` when dropping adds the node as a child of the root node. +If `Alt + Shift` is held when dropping, the node type can be selected if +applicable. + +### Main toolbar + +Some buttons in the main toolbar are the same as those in the 3D workspace. A brief explanation +is given with the shortcut if the mouse cursor is hovered over a button for one second. +Some buttons may have additional functionality if another keypress is performed. +A recap of main functionality of each button with its default shortcut is provided below +from left to right: + +![Image](/img/Tutorials/2d/img/2d_toolbar.webp) + +- **Select Mode** (`Q`): Allows selection of nodes in the viewport. Left clicking on a node + in the viewport selects it. + Left clicking and dragging a rectangle selects all nodes within the rectangle's boundaries, + once released. + Holding `Shift` while selecting adds more nodes to the selection. + Clicking on a selected node while holding `Shift` deselects the node. + In this mode, you can drag the selected node(s) to move, press `Ctrl` to switch to the + rotation mode temporarily, or use the red circles to scale it. If multiple nodes are + selected, only movement and rotation are possible. In this mode, rotation and scaling + will not use the snapping options if snapping is enabled. +- **Move Mode** (`W`): Enables move (or translate) mode for the selected nodes. See + [doc_introduction_to_2d_the_viewport](doc_introduction_to_2d_the_viewport) for more details. +- **Rotate Mode** (`E`): Enables rotation mode for the selected nodes. See + [doc_introduction_to_2d_the_viewport](doc_introduction_to_2d_the_viewport) for more details. +- **Scale Mode** (`S`): Enables scaling and displays scaling gizmos in both + axes for the selected node(s). See [doc_introduction_to_2d_the_viewport](doc_introduction_to_2d_the_viewport) for more details. +- **Show list of selectable nodes at position clicked**: As the description suggests, + this provides a list of selectable nodes at the clicked position as a context menu, if + there is more than one node in the clicked area. +- **Rotation pivot**: Sets the rotation pivot to rotate node(s) around. + An added node has its rotation pivot at ``x: 0``, ``y: 0``, by default, with + exceptions. For example, the default pivot for a [Sprite2D ](class_Sprite2D) is its + center if the ``centered`` property is set to ``true``. If you would like to change the + rotation pivot of a node, click this button and choose a new location by left clicking. + The node rotates considering this point. If you have multiple nodes selected, this icon + will add a temporary pivot to be used commonly by all selected nodes. Pressing `Shift` + and clicking this button will create the pivot at the center of selected nodes. If any of + the snap options are enabled, the pivot will also snap to them it when dragged. +- **Pan Mode** (`G`): Allows you to navigate in the viewport without accidentally selecting any nodes. + In other modes, you can also hold `Space` and drag with the left mouse button to do the same. +- **Ruler Mode**: After enabling, click on the viewport to display the current global + x and y coordinates. Dragging from a position to another one measures the distance in pixels. + If you drag diagonally, it will draw a triangle and show the separate distances in terms + of x, y, and total distance to the target, including the angles to the axes in degrees. + The `R` key activates the ruler. If snapping is enabled, it also displays the + measurements in terms of grid count: + +![Image](/img/Tutorials/2d/img/2d_ruler_with_snap.webp) + + Using ruler with snapping enabled. + +- **Use Smart Snap**: Toggles smart snapping for move, rotate, and scale modes; and + the rotation pivot. Customize it using the three-dot menu next to the snap tools. +- **Use Grid Snap**: Toggles snapping to grid for move and scale mode, rotation pivot, + and the ruler. Customize it using the three-dot menu next to the snap tools. + +You can customize the grid settings so that move mode, rotate mode, scale mode, ruler, +and rotation pivot uses snapping. +Use the three-dot menu for this: + +![Image](/img/Tutorials/2d/img/2d_snapping_options_menu.webp) + +- **Use Rotation Snap**: Toggles snapping using the configured rotation setting. +- **Use Scale Snap**: Toggles snapping using the configured scaling step setting. +- **Snap Relative**: Toggles the usage of snapping based on the selected node's current + transform values. For example, if the grids are set to 32x32 pixels and if the selected node + is located at ``x: 1, y: 1``, then, enabling this option will temporarily shift the grids by + ``x: 1, y: 1``. +- **Use Pixel Snap**: Toggles the use of subpixels for snapping. If enabled, the position values + will be integers, disabling will enable subpixel movement as decimal values. For the runtime + property, consider checking `Project Settings > Rendering > 2D > Snapping` property for + Node2D nodes, and `Project Settings > GUI > General > Snap Controls to Pixels` for + Control nodes. +- **Smart Snapping**: Provides a set of options to snap to specific positions if they are enabled: + + - Snap to Parent: Snaps to parent's edges. For example, scaling a child control node while + this is enabled will snap to the boundaries of the parent. + - Snap to Node Anchor: Snaps to the node's anchor. For example, if anchors of a control + node is positioned at different positions, enabling this will snap to the sides and + corners of the anchor. + - Snap to Node Sides: Snaps to the node's sides, such as for the rotation pivot or anchor + positioning. + - Snap to Node Center: Snaps to the node's center, such as for the rotation pivot or + anchor positioning. + - Snap to Other Nodes: Snaps to other nodes while moving or scaling. Useful to align nodes + in the editor. + - Snap to Guides: Snaps to custom guides drawn using the horizontal or vertical ruler. More + on the ruler and guides below. + +![Image](/img/Tutorials/2d/img/2d_snapping_options.webp) + +- **Configure Snap**: Opens the window shown above, offering a set of snapping parameters. + + - Grid Offset: Allows you to shift grids with respect to the origin. ``x`` and ``y`` can + be adjusted separately. + - Grid Step: The distance between each grid in pixels. ``x`` and ``y`` can be adjusted separately. + - Primary Line Every: The number of grids in-between to draw infinite lines as indication of + main lines. + - Rotation Offset: Sets the offset to shift rotational snapping. + - Rotation Step: Defines the snapping degree. E.g., 15 means the node will rotate and snap + at multiples of 15 degrees if rotation snap is enabled and the rotate mode is used. + - Scale Step: Determines the scaling increment factor. For example, if it is 0.1, it will + change the scaling at 0.1 steps if scaling snap is enabled and the scaling mode is used. + +- **Lock selected nodes** (`Ctrl + L`). Locks the selected nodes, preventing selection and movement in the + viewport. Clicking the button again (or using `Ctrl + Shift + L`) unlocks the selected + nodes. Locked nodes can only be selected in the scene tree. + They can easily be identified by a padlock next to their node names in the scene tree. + Clicking on this padlock also unlocks the nodes. +- **Group selected nodes** (`Ctrl + G`). This allows selection of the root node if any + of the children are selected. Using `Ctrl + G` ungroups them. Additionally, clicking + the ungroup button in the scene tree performs the same action. +- **Skeleton Options**: Provides options to work with Skeleton2D and Bone2D. + + - Show Bones: Toggles the visibility of bones for the selected node. + - Make Bone2D Node(s) from Node(s): Converts selected node(s) into Bone2D. + +:::info +To learn more about Skeletons, see :ref:`doc_cutout_animation`. + +::: + +- **Project Camera Override**: Temporarily replaces the active camera in the level + (e.g., the camera following the player) with the camera in the editor's viewport, allowing + you to move freely and inspect the level's different parts, while the game is running. + +- **View** menu: Provides options to control the viewport view. Since its options + depend heavily on the viewport, it is covered in the [doc_introduction_to_2d_the_viewport](doc_introduction_to_2d_the_viewport) + section. + +Next to the View menu, additional buttons may be visible. In the toolbar image +at the beginning of this chapter, an additional *Sprite2D* button appears because a +Sprite2D is selected. This menu provides some quick actions and tools to +work on a specific node or selection. For example, while drawing a polygon, it +provides buttons to add, modify, or remove points. + +### Coordinate system + +In the 2D editor, unlike 3D, there are only two axes: ``x`` and ``y``. Also, the viewing +angle is fixed. + +In the viewport, you will see two lines in two colors going across the screen infinitely: +red for the x-axis, and green for the y-axis. +In Redot, going right and down are positive directions. +Where these two lines intersect is the origin: ``x: 0, y: 0``. + +A root node will have its origin at this position once added. +Switching to the `move` or `scale` modes after selecting a node will display the gizmos at the +node's offset position. +The gizmos will point to the positive directions of the x and y axes. +In the move mode, you can drag the green line to move only in the ``y`` axis. +Similarly, you can hold the red line to move only in the ``x`` axis. + +In the scale mode, the gizmos will have a square shape. You can hold and drag the green and +red squares to scale the nodes in the ``y`` or ``x`` axes. +Dragging in a negative direction flips the node horizontally or vertically. + +### 2D Viewport + +The viewport will be the area you spend the most time if you plan to design levels or user +interfaces visually: + +![Image](/img/Tutorials/2d/img/2d_editor_viewport_with_viewmenu.webp) + +Middle-clicking and dragging the mouse will pan the view. +The scrollbars on the right or bottom of the viewport also move the view. +Alternatively, the `G` or `Space` keys can be used. +If you enable `Editor Settings > Editors > Panning > Simple Panning`, you can activate +panning directly with `Space` only, without requiring dragging. + +The viewport has buttons on the top-left. +**Center View** centers the selected node(s) in the screen. Useful if you have a large scene +with many nodes, and want to see the node selected in the scene tree. +Next to it are the zoom controls. **-** zooms out, **+** zooms in, and clicking on the number +with percentage defaults to 100%. +Alternatively, you can use middle-mouse scrolling to zoom in (scroll up) and out (scroll down). + +The black bars at the viewport's left and top edges are the **rulers**. You can use them to +orient yourself in the viewport. +By default, the rulers will display the pixel coordinates of the viewport, numbered at +100 pixel steps. Changing the zoom factor will change the shown values. +Enabling `Grid Snap` or changing the snapping options will update the ruler's scaling and +the shown values. + +You can also create multiple custom guides to help you make measurements or align +nodes with them: + +![Image](/img/Tutorials/2d/img/2d_editor_guidelines.webp) + +If you have at least one node in the scene, you can create guides by dragging from the horizontal +or vertical ruler towards the viewport. A purple guide will appear, showing its position, and will +remain there when you release the mouse. You can create both horizontal and vertical guides +simultaneously by dragging from the gray square at the rulers' intersection. Guides can be +repositioned by dragging them back to their respective rulers, and they can be removed by +dragging them all the way back to the ruler. + +You can also enable snapping to the created guides using the `Smart Snap` menu. + +:::note +If you cannot create a line, or do not see previously created guides, make sure that +they are visible by checking the `View` menu of the viewport. `Y` toggles their visibility, +by default. Also, make sure you have at least one node in the scene. + +::: + +Depending on the tool chosen in the toolbar, left-clicking will have a primary action in the +viewport. +For example, the `Select Mode` will select the left-clicked node in the viewport. +Sometimes, left-clicking can be combined with a modifier (e.g., `Ctrl`, or `Shift`) to +perform secondary actions. +For example, keeping `Shift` pressed while dragging a node in the Select or Move modes will +try to snap the node in a single axis while moving. + +Right clicking in the viewport provides two options to create a node or instantiate a scene +at the chosen position. +If at least one node is selected, right clicking also provides the option to move the selected +node(s) to this position. + +Viewport has a **View** menu which provides several options to change the look of the viewport: + +- **Grid**: Allows you to show grids all the time, only when using snapping, or not at all. You + can also toggle them with the provided option. +- **Show Helpers**: Toggles the temporary display of an outline of the node, with the previous + transform properties (position, scaling, or rotation) if a transform operation has been + initiated. For `Control` nodes, it also shows the sizing parameters. Useful to see the deltas. +- **Show Rulers**: Toggles the visibility of horizontal and vertical rulers. See + [doc_introduction_to_2d_the_viewport](doc_introduction_to_2d_the_viewport) more on rulers. +- **Show Guides**: Toggles the visibility of created guides. See + [doc_introduction_to_2d_the_viewport](doc_introduction_to_2d_the_viewport) for on how to create them. +- **Show Origin**: Toggles the display of the green and red origin lines drawn at ``x: 0, y: 0``. +- **Show Viewport**: Toggles the visibility of the game's default + viewport, indicated by an indigo-colored rectangle. It is also the default window size on desktop + platforms, which can be changed by going to `Project Settings > Display > Window > Size` and + setting `Viewport Width` and `Viewport Height`. +- **Gizmos**: Toggles the visibility of `Position` (shown with cross icon), `Lock` + (shown with padlock), `Groups` (shown with two squares), and `Transformation` (shown with + green and red lines) indicators. +- **Center Selection**: The same as the **Center View** button inside the viewport. Centers the selected + node(s) in the view. `F` is the default shortcut. +- **Frame to Selection**: Similar to `Center Selection`, but also changes the zoom factor to fit the + contents in the screen. `Shift + F` is the default shortcut. +- **Clear Guides**: Deletes all guides from the screen. You will need to recreate them if + you plan to use them later. +- **Preview Canvas Scale**: Toggles the preview for scaling of canvas in the editor when the zoom + factor or view of the viewport changes. Useful to see how the controls will look like after scaling + and moving, without running the game. +- **Preview Theme**: Allows to choose from the available themes to change the look of control items + in the editor, without requiring to run the game. + +## Node2D and Control node + +[CanvasItem ](class_CanvasItem) is the base node for 2D. [Node2D ](class_Node2D) is the base node +for 2D game objects, and [Control ](class_Control) is the base node +for everything GUI. For 3D, Redot uses the [Node3D ](class_Node3D) node. + +## 3D in 2D + +It is possible to display 3D scenes in 2D screen. This is achieved by adding a +[SubViewport ](class_SubViewport) as a child. +Then, you can drag a 3D scene as a child of the SubViewport: + +![Image](/img/Tutorials/2d/img/3d_in_2d_demo_editor.webp) + +:::info +You can check the demo on: `3D in 2D Viewport demo `__. + +::: diff --git a/Redot-Documentation/docs/26.1/Tutorials/2d/particle_systems_2d.md b/Redot-Documentation/docs/26.1/Tutorials/2d/particle_systems_2d.md new file mode 100644 index 0000000..b2911cc --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/2d/particle_systems_2d.md @@ -0,0 +1,442 @@ +:::warning +This page is marked as outdated and may not reflect current Redot behavior. +::: + +# 2D particle systems + +## Intro + +Particle systems are used to simulate complex physical effects, +such as sparks, fire, magic particles, smoke, mist, etc. + +The idea is that a "particle" is emitted at a fixed interval and with a +fixed lifetime. During its lifetime, every particle will have the same +base behavior. What makes each particle different from the rest and provides a more +organic look is the "randomness" associated with each parameter. In +essence, creating a particle system means setting base physics +parameters and then adding randomness to them. + +### Particle nodes + +Redot provides two different nodes for 2D particles, [class_GPUParticles2D](class_GPUParticles2D) +and [class_CPUParticles2D](class_CPUParticles2D). GPUParticles2D is more advanced and uses the +GPU to process particle effects. CPUParticles2D is a CPU-driven option with +near-feature parity with GPUParticles2D, but lower performance when using large +amounts of particles. On the other hand, CPUParticles2D may perform better on +low-end systems or in GPU-bottlenecked situations. + +While GPUParticles2D is configured via a [class_ParticleProcessMaterial](class_ParticleProcessMaterial) +(and optionally with a custom shader), the matching options are provided via +node properties in CPUParticles2D (with the exception of the trail settings). + +You can convert a GPUParticles2D node into a CPUParticles2D node by clicking on +the node in the inspector, selecting the 2D viewport, and selecting +**GPUParticles2D > Convert to CPUParticles2D** in the viewport toolbar. + +![Image](/img/Tutorials/2d/img/particles_convert.webp) + +The rest of this tutorial is going to use the GPUParticles2D node. First, add a GPUParticles2D +node to your scene. After creating that node you will notice that only a white dot was created, +and that there is a warning icon next to your GPUParticles2D node in the scene dock. This +is because the node needs a ParticleProcessMaterial to function. + +### ParticleProcessMaterial + +To add a process material to your particles node, go to ``Process Material`` in +your inspector panel. Click on the box next to ``Material``, and from the dropdown +menu select ``New ParticleProcessMaterial``. + +![Image](/img/Tutorials/2d/img/particles_material.png) + +Your GPUParticles2D node should now be emitting +white points downward. + +![Image](/img/Tutorials/2d/img/particles1.png) + +### Texture + +A particle system can use a single texture or an animation *flipbook*. A +flipbook is a texture that contains several frames of animation that can be +played back, or chosen at random during emission. This is equivalent to a +spritesheet for particles. + +The texture is set via the **Texture** property: + +![Image](/img/Tutorials/2d/img/particles2.png) + +#### Using an animation flipbook + +Particle flipbooks are suited to reproduce complex effects such as smoke, fire, +explosions. They can also be used to introduce random texture variation, by +making every particle use a different texture. You can find existing particle +flipbook images online, or pre-render them using external tools such as `Blender + Generate Visibility Rect``. Redot will simulate the Particles2D node emitting particles for a few seconds and set the rectangle to fit the surface the particles take. + +You can control the emit duration with the ``Generation Time (sec)`` option. The maximum value is 25 seconds. If you need more time for your particles to move around, you can temporarily change the ``preprocess`` duration on the Particles2D node. + +### Local Coords + +By default this option is on, and it means that the space that particles +are emitted to is relative to the node. If the node is moved, all +particles are moved with it: + +![Image](/img/Tutorials/2d/img/paranim20.gif) + +If disabled, particles will emit to global space, meaning that if the +node is moved, already emitted particles are not affected: + +![Image](/img/Tutorials/2d/img/paranim21.gif) + +### Draw Order + +This controls the order in which individual particles are drawn. ``Index`` +means particles are drawn according to their emission order (default). +``Lifetime`` means they are drawn in order of remaining lifetime. + +## ParticleProcessMaterial settings + +### Direction + +This is the base direction at which particles emit. The default is +``Vector3(1, 0, 0)`` which makes particles emit to the right. However, +with the default gravity settings, particles will go straight down. + +![Image](/img/Tutorials/2d/img/direction1.png) + +For this property to be noticeable, you need an *initial velocity* greater +than 0. Here, we set the initial velocity to 40. You'll notice that +particles emit toward the right, then go down because of gravity. + +![Image](/img/Tutorials/2d/img/direction2.png) + +### Spread + +This parameter is the angle in degrees which will be randomly added in +either direction to the base ``Direction``. A spread of ``180`` will emit +in all directions (+/- 180). For spread to do anything the "Initial Velocity" +parameter must be greater than 0. + +![Image](/img/Tutorials/2d/img/paranim3.gif) + +### Flatness + +This property is only useful for 3D particles. + +### Gravity + +The gravity applied to every particle. + +![Image](/img/Tutorials/2d/img/paranim7.gif) + +### Initial Velocity + +Initial velocity is the speed at which particles will be emitted (in +pixels/sec). Speed might later be modified by gravity or other +accelerations (as described further below). + +![Image](/img/Tutorials/2d/img/paranim4.gif) + +### Angular Velocity + +Angular velocity is the initial angular velocity applied to particles. + +### Spin Velocity + +Spin velocity is the speed at which particles turn around their center +(in degrees/sec). + +![Image](/img/Tutorials/2d/img/paranim5.gif) + +### Orbit Velocity + +Orbit velocity is used to make particles turn around their center. + +![Image](/img/Tutorials/2d/img/paranim6.gif) + +### Linear Acceleration + +The linear acceleration applied to each particle. + +### Radial Acceleration + +If this acceleration is positive, particles are accelerated away from +the center. If negative, they are absorbed towards it. + +![Image](/img/Tutorials/2d/img/paranim8.gif) + +### Tangential Acceleration + +This acceleration will use the tangent vector to the center. Combining +with radial acceleration can do nice effects. + +![Image](/img/Tutorials/2d/img/paranim9.gif) + +### Damping + +Damping applies friction to the particles, forcing them to stop. It is +especially useful for sparks or explosions, which usually begin with a +high linear velocity and then stop as they fade. + +![Image](/img/Tutorials/2d/img/paranim10.gif) + +### Angle + +Determines the initial angle of the particle (in degrees). This parameter +is mostly useful randomized. + +![Image](/img/Tutorials/2d/img/paranim11.gif) + +### Scale + +Determines the initial scale of the particles. + +![Image](/img/Tutorials/2d/img/paranim12.gif) + +### Color + +Used to change the color of the particles being emitted. + +### Hue Variation + +The ``Variation`` value sets the initial hue variation applied to each +particle. The ``Variation Random`` value controls the hue variation +randomness ratio. + +### Animation + +:::note + +Particle flipbook animation is only effective if the CanvasItemMaterial used +on the GPUParticles2D or CPUParticles2D node has been +[configured accordingly ](doc_particle_systems_2d_using_flipbook). + +::: + +To set up the particle flipbook for linear playback, set the **Speed Min** and **Speed Max** values to 1: + +![Image](/img/Tutorials/2d/img/particles_flipbook_configure_animation_speed.webp) + + Setting up particle animation for playback during the particle's lifetime + +By default, looping is disabled. If the particle is done playing before its +lifetime ends, the particle will keep using the flipbook's last frame (which may +be fully transparent depending on how the flipbook texture is designed). If +looping is enabled, the animation will loop back to the first frame and resume +playing. + +Depending on how many images your sprite sheet contains and for how long your +particle is alive, the animation might not look smooth. The relationship between +particle lifetime, animation speed, and number of images in the sprite sheet is +this: + +:::note + +At an animation speed of ``1.0``, the animation will reach the last image +in the sequence just as the particle's lifetime ends. + +$$ +Animation\ FPS = \frac{Number\ of\ images}{Lifetime} +$$ + +::: + +If you wish the particle flipbook to be used as a source of random particle +textures for every particle, keep the speed values at 0 and set **Offset Max** +to 1 instead: + +![Image](/img/Tutorials/2d/img/particles_flipbook_configure_animation_offset.webp) + + Setting up particle animation for random offset on emission + +Note that the GPUParticles2D node's **Fixed FPS** also affects animation +playback. For smooth animation playback, it's recommended to set it to 0 so that +the particle is simulated on every rendered frame. If this is not an option for +your use case, set **Fixed FPS** to be equal to the effective framerate used by +the flipbook animation (see above for the formula). + +## Emission Shapes + +ParticleProcessMaterials allow you to set an Emission Mask, which dictates +the area and direction in which particles are emitted. +These can be generated from textures in your project. + +Ensure that a ParticleProcessMaterial is set, and the GPUParticles2D node is selected. +A "Particles" menu should appear in the Toolbar: + +![Image](/img/Tutorials/2d/img/emission_shapes1.png) + +Open it and select "Load Emission Mask": + +![Image](/img/Tutorials/2d/img/emission_shapes2.png) + +Then select which texture you want to use as your mask: + +![Image](/img/Tutorials/2d/img/emission_shapes3.png) + +A dialog box with several settings will appear. + +### Emission Mask + +Three types of emission masks can be generated from a texture: + +- Solid Pixels: Particles will spawn from any area of the texture, + excluding transparent areas. + +![Image](/img/Tutorials/2d/img/emission_mask_solid.gif) + +- Border Pixels: Particles will spawn from the outer edges of the texture. + +![Image](/img/Tutorials/2d/img/emission_mask_border.gif) + +- Directed Border Pixels: Similar to Border Pixels, but adds extra + information to the mask to give particles the ability to emit away + from the borders. Note that an ``Initial Velocity`` will need to + be set in order to utilize this. + +![Image](/img/Tutorials/2d/img/emission_mask_directed_border.gif) + +### Emission Colors + +``Capture from Pixel`` will cause the particles to inherit the color of the mask at their spawn points. + +Once you click "OK", the mask will be generated and set to the ParticleProcessMaterial, under the ``Emission Shape`` section: + +![Image](/img/Tutorials/2d/img/emission_shapes4.png) + +All of the values within this section have been automatically generated by the +"Load Emission Mask" menu, so they should generally be left alone. + +:::note +An image should not be added to ``Point Texture`` or ``Color Texture`` directly. +The "Load Emission Mask" menu should always be used instead. + +::: diff --git a/Redot-Documentation/docs/26.1/Tutorials/2d/using_tilemaps.md b/Redot-Documentation/docs/26.1/Tutorials/2d/using_tilemaps.md new file mode 100644 index 0000000..e212660 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/2d/using_tilemaps.md @@ -0,0 +1,439 @@ + +# Using TileMaps + +:::info + +This page assumes you have created or downloaded a TileSet already. If not, +please read [doc_using_tilesets](using_tilesets.md) first as you will need a TileSet +to create a TileMap. + +::: + +## Introduction + +A tilemap is a grid of tiles used to create a game's layout. There are several +benefits to using [TileMapLayer ](class_TileMapLayer) nodes to design your levels. +First, they make it possible to draw the layout by "painting" the tiles onto a +grid, which is much faster than placing individual [Sprite2D ](class_Sprite2D) +nodes one by one. Second, they allow for much larger levels because they are +optimized for drawing large numbers of tiles. Finally, you can add collision, +occlusion, and navigation shapes to tiles, adding greater functionality to +the TileMap. + +## Specifying the TileSet in the TileMapLayer + +If you've followed the previous page on [doc_using_tilesets](using_tilesets.md), you should +have a TileSet resource that is built into the TileMapLayer node. This is good for +prototyping, but in a real world project, you will generally have multiple +levels reusing the same tileset. + +The recommended way to reuse the same TileSet in several TileMapLayer nodes is to save +the TileSet to an external resource. To do so, click the dropdown next to the TileSet +resource and choose **Save**: + +![Image](/img/Tutorials/2d/img/using_tilemaps_save_tileset_to_resource.webp) + + Saving the built-in TileSet resource to an external resource file + +## Multiple TileMapLayers and settings + +When working with tilemaps it's generally advised that you use multiple TileMapLayer +nodes when appropriate. Using multiple layers can be advantageous, for example, +this allows you to distinguish foreground tiles from background tiles for better +organization. You can place one tile per layer at a given location, which allows you +to overlap several tiles together if you have more than one layer. + +Each TileMapLayer node has several properties you can adjust: + +- **Enabled:** If ``true``, the layer is visible in the editor and when running + the project. +- **TileSet** The tileset used by the TileMapLayer node. + +### Rendering + +- **Y Sort Origin:** The vertical offset to use for Y-sorting on each tile (in pixels). + Only effective if **Y Sort Enabled** under CanvasItem settings is ``true``. +- **X Draw Order Reversed** Reverses the order tiles are drawn on the X axis. Requires + that **Y Sort Enabled** under CanvasItem settings is ``true``. +- **Rendering Quadrant Size** A quadrant is a group of tiles drawn together on a single + CanvasItem for optimization purposes. This setting defines the length of a square's + side in the map's coordinate system. The quadrant size does not apply to a Y sorted + TileMapLayer since tiles are grouped by Y position in that case. + +### Physics +- **Collision Enabled** Enables or disables collision. +- **Use Kinematic Bodies** When true TileMapLayer collision shapes will be instantiated + as kinematic bodies. +- **Collision Visibility Mode** Whether or not the TileMapLayer's collision shapes are + visible. If set to default, then it depends on the show collision debug settings. + +### Navigation + +- **Navigation Enabled** Whether or not navigation regions are enabled. +- **Navigation Visible** Whether or not the TileMapLayer's navigation meshes are + visible. If set to default then it depends on the show navigation debug settings. + +:::tip + +TileMap built-in navigation has many practical limitations that result in inferior pathfinding performance and pathfollowing quality. + +After designing the TileMap consider baking it to a more optimized navigation mesh (and disabling the TileMap NavigationLayer) using a [NavigationRegion2D ](class_NavigationRegion2D) or the [NavigationServer2D ](class_NavigationServer2D). See [doc_navigation_using_navigationmeshes](../navigation/navigation_using_navigationmeshes.md) for additional information. + +::: + +:::warning + +2D navigation meshes can not be "layered" or stacked on top of each other like visuals or physic shapes. Attempting to stack navigation meshes on the same navigation map will result in merge and logical errors that break the pathfinding. + +::: + +### Reordering layers + +You can reorder layers by drag-and-dropping their node in the Scene tab. You can +also switch between which TileMapLayer node you're working on by using the buttons +in the top right corner of the TileMap editor. + +:::note + +You can create, rename or reorder layers in the future without affecting +existing tiles. Be careful though, as *removing* a layer will also remove +all tiles that were placed on the layer. + +::: + +## Opening the TileMap editor + +Select the TileMapLayer node, then open the TileMap panel at the bottom +of the editor: + +![Image](/img/Tutorials/2d/img/using_tilemaps_open_tilemap_editor.webp) + + Opening the TileMap panel at the bottom of the editor. The TileMapLayer node must be selected first. + +## Selecting tiles to use for painting + +First, if you've created additional layers above, make sure you've selected the +layer you wish to paint on: + +![Image](/img/Tutorials/2d/img/using_tilemaps_select_layer.webp) + + Selecting a layer to paint on in the TileMap editor + +:::tip + +In the 2D editor, the layers you aren't currently editing from the same +TileMapLayer node will appear grayed out while in the TileMap editor. You can +disable this behavior by clicking the icon next to the layer selection menu +(**Highlight Selected TileMap Layer** tooltip). + +::: + +You can skip the above step if you haven't created additional layers, as the +first layer is automatically selected when entering the TileMap editor. + +Before you can place tiles in the 2D editor, you must select one or more tiles +in the TileMap panel located at the bottom of the editor. To do so, click a tile +in the TileMap panel, or hold down the mouse button to select multiple tiles: + +![Image](/img/Tutorials/2d/img/using_tilemaps_select_single_tile_from_tileset.webp) + + Selecting a tile in the TileMap editor by clicking it + +:::tip + +Like in the 2D and TileSet editors, you can pan across the TileMap panel using +the middle or right mouse buttons, and zoom using the mouse wheel or buttons in +the top-left corner. + +::: + +You can also hold down `Shift` to append to the current selection. When +selecting more than one tile, multiple tiles will be placed every time you +perform a painting operation. This can be used to paint structures composed of +multiple tiles in a single click (such as large platforms or trees). + +The final selection does not have to be contiguous: if there is empty space +between selected tiles, it will be left empty in the pattern that will be +painted in the 2D editor. + +![Image](/img/Tutorials/2d/img/using_tilemaps_select_multiple_tiles_from_tileset.webp) + + Selecting multiple tiles in the TileMap editor by holding down the left mouse button + +If you've created alternative tiles in your TileSet, you can select them for +painting on the right of the base tiles: + +![Image](/img/Tutorials/2d/img/using_tilemaps_use_alternative_tile.webp) + + Selecting an alternative tile in the TileMap editor + +Lastly, if you've created a *scenes collection* in the TileSet, you can place scene tiles in the TileMap: + +![Image](/img/Tutorials/2d/img/using_tilemaps_placing_scene_tiles.webp) + + Placing a scene tile containing particles using the TileMap editor + +## Painting modes and tools + +Using the toolbar at the top of the TileMap editor, you can choose between +several painting modes and tools. These modes affect operation when clicking in +the 2D editor, **not** the TileMap panel itself. + +From left to right, the painting modes and tools you can choose are: + +### Selection + +Select tiles by clicking a single tile, or by holding down the left mouse button to +select multiple with a rectangle in the 2D editor. Note that empty space cannot be +selected: if you create a rectangle selection, only non-empty tiles will be selected. + +To append to the current selection, hold `Shift` then select a tile. +To remove from the current selection, hold `Ctrl` then select a tile. + +The selection can then be used in any other painting mode to quickly create copies +of an already-placed pattern. + +You can remove the selected tiles from the TileMap by pressing `Del`. + +You can toggle this mode temporarily while in Paint mode by holding `Ctrl` +then performing a selection. + +:::tip + +You can copy and paste tiles that were already placed by performing a +selection, pressing `Ctrl + C` then pressing `Ctrl + V`. +The selection will be pasted after left-clicking. You can press +`Ctrl + V` another time to perform more copies this way. +Right-click or press `Escape` to cancel pasting. + +::: + +### Paint + +The standard Paint mode allows you to place tiles by clicking or holding +down the left mouse button. + +If you right-click, the currently selected tile will be erased from the tilemap. +In other words, it will be replaced by empty space. + +If you have selected multiple tiles in the TileMap or using the Selection tool, +they will be placed every time you click or drag the mouse while holding down +the left mouse button. + +:::tip + +While in Paint mode, you can draw a line by holding `Shift` *before* +holding down the left mouse button, then dragging the mouse to the line's end +point. This is identical to using the Line tool described below. + +You can also draw a rectangle by holding `Ctrl` and `Shift` +*before* holding down the left mouse button, then dragging the mouse to the +rectangle's end point. This is identical to using the Rectangle tool +described below. + +Lastly, you can pick existing tiles in the 2D editor by holding `Ctrl` +then clicking on a tile (or holding and dragging the mouse). +This will switch the currently painted tile(s) to the tile(s) you've just clicked. +This is identical to using the Picker tool described below. + +::: + +### Line + +After selecting Line Paint mode, you can draw in a line that is +always 1 tile thick (no matter its orientation). + +If you right-click while in Line Paint mode, you will erase in a line. + +If you have selected multiple tiles in the TileMap or using the Selection tool, +you can place them in a repeating pattern across the line. + +You can toggle this mode temporarily while in Paint or Eraser mode by holding +`Shift` then drawing. + +![Image](/img/Tutorials/2d/img/using_tilesets_line_tool_multiple_tiles.webp) + + Using the line tool after selecting two tiles to draw platforms diagonally + +### Rectangle + +After selecting Rectangle Paint mode, you can draw in an axis-aligned +rectangle. + +If you right-click while in Rectangle Paint mode, you will erase in +an axis-aligned rectangle. + +If you have selected multiple tiles in the TileMap or using the Selection tool, +you can place them in a repeating pattern within the rectangle. + +You can toggle this mode temporarily while in Paint or Eraser mode by holding +`Ctrl` and `Shift` then drawing. + +### Bucket Fill + +After selecting Bucket Fill mode, you can choose whether painting should be +limited to contiguous areas only by toggling the **Contiguous** checkbox that +appears on the right of the toolbar. + +If you enable **Contiguous** (the default), only matching tiles that touch the +current selection will be replaced. This contiguous check is performed +horizontally and vertically, but *not* diagonally. + +If you disable **Contiguous**, all tiles with the same ID in the entire TileMap will +be replaced by the currently selected tile. If selecting an empty tile with +**Contiguous** unchecked, all tiles in the rectangle that encompasses the +TileMap's effective area will be replaced instead. + +If you right-click while in Bucket Fill mode, you will replace matching tiles +with empty tiles. + +If you have selected multiple tiles in the TileMap or using the Selection tool, +you can place them in a repeating pattern within the filled area. + +![Image](/img/Tutorials/2d/img/using_tilemaps_bucket_fill.webp) + + Using the Bucket Fill tool + +### Picker + +After selecting Picker mode, you can pick existing tiles in the 2D editor by +holding `Ctrl` then clicking on a tile. This will switch the currently +painted tile to the tile you've just clicked. You can also pick multiple tiles +at once by holding down the left mouse button and forming a rectangle selection. +Only non-empty tiles can be picked. + +You can toggle this mode temporarily while in Paint mode by holding `Ctrl` +then clicking or dragging the mouse. + +### Eraser + +This mode is combined with any other painting mode (Paint, Line, Rectangle, +Bucket Fill). When eraser mode is enabled, tiles will be replaced by empty tiles +instead of drawing new lines when left-clicking. + +You can toggle this mode temporarily while in any other mode by right-clicking +instead of left-clicking. + +## Painting randomly using scattering + +While painting, you can optionally enable *randomization*. When enabled, +a random tile will be chosen between all the currently selected tiles when +painting. This is supported with the Paint, Line, Rectangle and Bucket Fill +tools. For effective paint randomization, you must select multiple tiles +in the TileMap editor or use scattering (both approaches can be combined). + +If **Scattering** is set to a value greater than 0, there is a chance that no tile +will be placed when painting. This can be used to add occasional, non-repeating +detail to large areas (such as adding grass or crumbs on a large top-down +TileMap). + +Example when using Paint mode: + +![Image](/img/Tutorials/2d/img/using_tilemaps_scatter_tiles.webp) + + Selecting from several times to randomly choose, then painting by holding down the left mouse button + +Example when using Bucket Fill mode: + +![Image](/img/Tutorials/2d/img/using_tilemaps_bucket_fill_scatter.webp) + + Using Bucket Fill tool with a single tile, but with randomization and scattering enabled + +:::note + +Eraser mode does not take randomization and scattering into account. +All tiles within the selection are always removed. + +::: + +## Saving and loading premade tile placements using patterns + +While you can copy and paste tiles while in Select mode, you may wish to save +premade *patterns* of tiles to place together in a go. This can be done on a +per-TileMap basis by choosing the **Patterns** tab of the TileMap editor. + +To create a new pattern, switch to Select mode, perform a selection and press +`Ctrl + C`. Click on empty space within the Patterns tab (a blue focus +rectangle should appear around the empty space), then press `Ctrl + V`: + +![Image](/img/Tutorials/2d/img/using_tilemaps_create_pattern.webp) + + Creating a new pattern from a selection in the TileMap editor + +To use an existing pattern, click its image in the **Patterns** tab, switch to +any painting mode, then left-click somewhere in the 2D editor: + +![Image](/img/Tutorials/2d/img/using_tilemaps_use_pattern.webp) + + Placing an existing pattern using the TileMap editor + +Like multi-tile selections, patterns will be repeated if used with the Line, +Rectangle or Bucket Fill painting modes. + +:::note + +Despite being edited in the TileMap editor, patterns are stored in the +TileSet resource. This allows reusing patterns in different TileMapLayer nodes +after loading a TileSet resource saved to an external file. + +::: + +## Handling tile connections automatically using terrains + +To use terrains, the TileMapLayer node must feature at least one terrain set and a +terrain within this terrain set. See +[doc_using_tilesets_creating_terrain_sets](doc_using_tilesets_creating_terrain_sets) if you haven't created a terrain +set for the TileSet yet. + +There are 3 kinds of painting modes available for terrain connections: + +- **Connect**, where tiles are connected to surrounding tiles on the same + TileMapLayer. +- **Path**, where tiles are connected to tiles painted in the same stroke (until + the mouse button is released). +- Tile-specific overrides to resolve conflicts or handle situations not covered + by the terrain system. + +The Connect mode is easier to use, but Path is more flexible as it allows for +more artist control during painting. For instance, Path can allow roads to be +directly adjacent to each other without being connected to each other, while +Connect will force both roads to be connected. + +![Image](/img/Tutorials/2d/img/using_tilemaps_terrain_select_connect_mode.webp) + + Selecting Connect mode in the TileMap editor's Terrains tab + +![Image](/img/Tutorials/2d/img/using_tilemaps_terrain_select_path_mode.webp) + + Selecting Path mode in the TileMap editor's Terrains tab + +Lastly, you can select specific tiles from the terrain to resolve conflicts in +certain situations: + +![Image](/img/Tutorials/2d/img/using_tilemaps_terrain_paint_specific_tiles.webp) + + Painting with specific tiles in the TileMap editor's Terrains tab + +Any tile that has at least one of its bits set to a value set to the +corresponding terrain ID will appear in the list of tiles to choose from. + +## Handling missing tiles + +If you remove tiles in the TileSet that are referenced in a TileMap, the TileMap +will display a placeholder to indicate that an invalid tile ID is placed: + +![Image](/img/Tutorials/2d/img/using_tilemaps_missing_tiles.webp) + + Missing tiles in the TileMap editor due to the TileSet reference being broken + +These placeholders are **not** visible in the running project, but the tile data +is still persisted to disk. This allows you to safely close and reopen such +scenes. Once you re-add a tile with the matching ID, the tiles will appear with +the new tile's appearance. + +:::note + +Missing tile placeholders may not be visible until you select the TileMapLayer +node and open the TileMap editor. + +::: diff --git a/Redot-Documentation/docs/26.1/Tutorials/2d/using_tilesets.md b/Redot-Documentation/docs/26.1/Tutorials/2d/using_tilesets.md new file mode 100644 index 0000000..a5f4764 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/2d/using_tilesets.md @@ -0,0 +1,581 @@ + +# Using TileSets + +## Introduction + +A tilemap is a grid of tiles used to create a game's layout. There are several +benefits to using [TileMapLayer ](class_TileMapLayer) nodes to design your +levels. First, they let you draw a layout by "painting" tiles onto a grid, +which is much faster than placing individual [Sprite2D ](class_Sprite2D) nodes one by one. Second, they allow for larger levels +because they are optimized for drawing large numbers of tiles. +Finally, they allow you to add greater functionality to your tiles with +collision, occlusion, and navigation shapes. + +To use TileMapLayer nodes, you will need to create a TileSet first. A TileSet is a +collection of tiles that can be placed in a TileMapLayer node. After creating a +TileSet, you will be able to place them [using the TileMap editor ](using_tilemaps.md). + +To follow this guide, you will need an image containing your tiles where every +tile has the same size (large objects can be split into several tiles). This +image is called a *tilesheet*. Tiles do not have to be square: they can be +rectangular, hexagonal, or isometric (pseudo-3D perspective). + +## Creating a new TileSet + +### Using a tilesheet + +This demonstration will use the following tiles taken from +[Kenney's "Abstract Platformer" pack ](https://kenney.nl/assets/abstract-platformer). +We'll use this particular *tilesheet* from the set: + +![Image](/img/Tutorials/2d/img/using_tilesets_kenney_abstract_platformer_tile_sheet.webp) + + Tilesheet with 64×64 tiles. Credit: [Kenney ](https://kenney.nl/assets/abstract-platformer) + +Create a new **TileMapLayer** node, then select it and create a new TileSet resource in the inspector: + +![Image](/img/Tutorials/2d/img/using_tilesets_create_new_tileset.webp) + + Creating a new TileSet resource within the TileMapLayer node + +After creating the TileSet resource, click the value to unfold it in the +inspector. The default tile shape is Square, but you can also choose Isometric, +Half-Offset Square or Hexagon (depending on the shape of your tile images). If +using a tile shape other than Square, you may also need to adjust the **Tile +Layout** and **Tile Offset Axis** properties. Lastly, enabling the +**Rendering > UV Clipping** property may be useful if you wish tiles to be clipped +by their tile coordinates. This ensures tiles cannot draw outside their allocated +area on the tilesheet. + +Set the tile size to 64×64 in the inspector to match the example tilesheet: + +![Image](/img/Tutorials/2d/img/using_tilesets_specify_size_then_edit.webp) + + Setting the tile size to 64×64 to match the example tilesheet + +If relying on automatic tiles creation (like we're about to do here), you must +set the tile size **before** creating the *atlas*. The atlas will +determine which tiles from the tilesheet can be added to a TileMapLayer node +(as not every part of the image may be a valid tile). + +Open the **TileSet** panel at the bottom of the editor, then click and drag the +tilesheet image onto the panel. You will be asked whether to create tiles +automatically. Answer **Yes**: + +![Image](/img/Tutorials/2d/img/using_tilesets_create_tiles_automatically.webp) + + Automatically creating tiles based on tilesheet image content + +This will automatically create tiles according to the tile size you specified +earlier in the TileSet resource. This greatly speeds up initial tile setup. + +:::note + +When using automatic tile generation based on image contents, parts of the +tilesheet that are *fully* transparent will not have tiles generated. + +::: + +If there are tiles from the tilesheet you do not wish to be present in atlas, +choose the Eraser tool at the top of the tileset preview, then click the tiles +you wish to remove: + +![Image](/img/Tutorials/2d/img/using_tilesets_eraser_tool.webp) + + Using the Eraser tool to remove unwanted tiles from the TileSet atlas + +You can also right-click a tile and choose **Delete**, as an alternative to the +Eraser tool. + +:::tip + +Like in the 2D and TileMap editors, you can pan across the TileSet panel using +the middle or right mouse buttons, and zoom using the mouse wheel or buttons in +the top-left corner. + +::: + +If you wish to source tiles from several tilesheet images for a single TileSet, +create additional atlases and assign textures to each of them before continuing. +It is also possible to use one image per tile this way (although using +tilesheets is recommended for better usability). + +You can adjust properties for the atlas in the middle column: + +![Image](/img/Tutorials/2d/img/using_tilesets_properties.webp) + + Adjusting TileSet atlas properties in the dedicated inspector (part of the TileSet panel) + +The following properties can be adjusted on the atlas: + +- **ID:** The identifier (unique within this TileSet), used for sorting. +- **Name:** The human-readable name for the atlas. Use a descriptive name + here for organizational purposes (such as "terrain", "decoration", etc). +- **Margins:** The margins on the image's edges that should not be selectable as + tiles (in pixels). Increasing this can be useful if you download a tilesheet + image that has margins on the edges (e.g. for attribution). +- **Separation:** The separation between each tile on the atlas in pixels. + Increasing this can be useful if the tilesheet image you're using contains + guides (such as outlines between every tile). +- **Texture Region Size:** The size of each tile on the atlas in pixels. In most + cases, this should match the tile size defined in the TileMapLayer property + (although this is not strictly necessary). +- **Use Texture Padding:** If checked, adds a 1-pixel transparent edge around + each tile to prevent texture bleeding when filtering is enabled. + It's recommended to leave this enabled unless you're running into rendering issues + due to texture padding. + +Note that changing texture margin, separation and region size may cause tiles to +be lost (as some of them would be located outside the atlas image's +coordinates). To regenerate tiles automatically from the tilesheet, use the +three vertical dots menu button at the top of the TileSet editor and choose +**Create Tiles in Non-Transparent Texture Regions**: + +![Image](/img/Tutorials/2d/img/using_tilesets_recreate_tiles_automatically.webp) + + Recreating tiles automatically after changing atlas properties + +### Using a collection of scenes + +Since Redot 4.0, you can place actual *scenes* as tiles. This allows you to use +any collection of nodes as a tile. For example, you could use scene tiles to +place gameplay elements, such as shops the player may be able to interact with. +You could also use scene tiles to place AudioStreamPlayer2Ds (for ambient +sounds), particle effects, and more. + +:::warning + +Scene tiles come with a greater performance overhead compared to atlases, as +every scene is instanced individually for every placed tile. + +It's recommended to only use scene tiles when necessary. To draw sprites in a +tile without any kind of advanced manipulation, +[use atlases instead ](doc_creating_tilesets_using_tilesheet). + +::: + +For this example, we'll create a scene containing a CPUParticles2D root node. +Save this scene to a scene file (separate from the scene containing the +TileMapLayer), then switch to the scene containing the TileMapLayer node. Open the TileSet +editor, and create a new **Scenes Collection** in the left column: + +![Image](/img/Tutorials/2d/img/using_tilesets_creating_scene_collection.webp) + + Creating a scenes collection in the TileSet editor + +After creating a scenes collection, you can enter a descriptive name for the +scenes collection in the middle column if you wish. Select this scenes +collection then create a new scene slot: + +![Image](/img/Tutorials/2d/img/using_tilesets_scene_collection_create_scene_tile.webp) + + Creating a scene tile after selecting the scenes collection in the TileSet editor + +Select this scene slot in the right column, then use **Quick Load** (or +**Load**) to load the scene file containing the particles: + +![Image](/img/Tutorials/2d/img/using_tilesets_adding_scene_tile.webp) + + Creating a scene slot, then loading a scene file into it in the TileSet editor + +You now have a scene tile in your TileSet. Once you switch to the TileMap +editor, you'll be able to select it from the scenes collection and paint it like +any other tile. + +## Merging several atlases into a single atlas + +Using multiple atlases within a single TileSet resource can sometimes be useful, +but it can also be cumbersome in certain situations (especially if you're using +one image per tile). Redot allows you to merge several atlases into a single +atlas for easier organization. + +To do so, you must have more than one atlas created in the TileSet resource. +Use the "three vertical dots" menu button located at the bottom of the list of +atlases, then choose **Open Atlas Merging Tool**: + +![Image](/img/Tutorials/2d/img/using_tilesets_open_atlas_merging_tool.webp) + + Opening the atlas merging tool after creating multiple atlases + +This will open a dialog, in which you can select several atlases by holding +`Shift` or `Ctrl` then clicking on multiple elements: + +![Image](/img/Tutorials/2d/img/using_tilesets_atlas_merging_tool_dialog.webp) + + Using the atlas merging tool dialog + +Choose **Merge** to merge the selected atlases into a single atlas image (which +translates to a single atlas within the TileSet). The unmerged atlases will be +removed within the TileSet, but *the original tilesheet images will be kept on +the filesystem*. If you don't want the unmerged atlases to be removed from the +TileSet resource, choose **Merge (Keep Original Atlases)** instead. + +:::tip + +TileSet features a system of *tile proxies*. Tile proxies are a mapping +table that allows notifying the TileMap using a given TileSet that a given +set of tile identifiers should be replaced by another one. + +Tile proxies are automatically set up when merging different atlases, but +they can also be set manually thanks to the **Manage Tile Proxies** dialog +you can access using the "three vertical dots" menu mentioned above. + +Manually creating tile proxies may be useful when you changed an atlas ID or +want to replace all tiles from an atlas by the ones from another atlas. Note +that when editing a TileMap, you can replace all cells by their +corresponding mapped value. + +::: + +## Adding collision, navigation and occlusion to the TileSet + +We've now successfully created a basic TileSet. We could start using it in the +TileMapLayer node now, but it currently lacks any form of collision detection. +This means the player and other objects could walk straight through the floor or +walls. + +If you use [2D navigation ](../navigation/navigation_introduction_2d.md), you'll also need +to define navigation polygons for tiles to generate a navigation mesh that +agents can use for pathfinding. + +Lastly, if you use [doc_2d_lights_and_shadows](2d_lights_and_shadows.md) or GPUParticles2D, you may +also want your TileSet to be able to cast shadows and collide with particles. +This requires defining occluder polygons for "solid" tiles on the TileSet. + +To be able to define collision, navigation and occlusion shapes for each tile, +you will need to create a physics, navigation or occlusion layer for the TileSet +resource first. To do so, select the TileMapLayer node, click the TileSet property +value in the inspector to edit it then unfold **Physics Layers** and choose +**Add Element**: + +![Image](/img/Tutorials/2d/img/using_tilesets_create_physics_layer.webp) + + Creating a physics layer in the TileSet resource inspector (within the TileMapLayer node) + +If you also need navigation support, now is a good time to create a navigation layer: + +![Image](/img/Tutorials/2d/img/using_tilesets_create_navigation_layer.webp) + + Creating a navigation layer in the TileSet resource inspector (within the TileMapLayer node) + +If you need support for light polygon occluders, now is a good time to create an occlusion layer: + +![Image](/img/Tutorials/2d/img/using_tilesets_create_occlusion_layer.webp) + + Creating an occlusion layer in the TileSet resource inspector (within the TileMapLayer node) + +:::note + +Future steps in this tutorial are tailored to creating collision polygons, +but the procedure for navigation and occlusion is very similar. +Their respective polygon editors behave in the same way, so these steps are +not repeated for brevity. + +The only caveat is that the tile's occlusion polygon property is part of a +**Rendering** subsection in the atlas inspector. Make sure to unfold this +section so you can edit the polygon. + +::: + +After creating a physics layer, you have access to the **Physics Layer** section +in the TileSet atlas inspector: + +![Image](/img/Tutorials/2d/img/using_tilesets_selecting_collision_editor.webp) + + Opening the collision editor while in Select mode + +You can quickly create a rectangle collision shape by pressing `F` while +the TileSet editor is focused. If the keyboard shortcut doesn't work, try +clicking in the empty area around the polygon editor to focus it: + +![Image](/img/Tutorials/2d/img/using_tilesets_using_default_rectangle_collision.webp) + + Using default rectangle collision shape by pressing `F` + +In this tile collision editor, you have access to all the 2D polygon editing tools: + +- Use the toolbar above the polygon to toggle between creating a new polygon, + editing an existing polygon and removing points on the polygon. The "three vertical dots" + menu button offers additional options, such as rotating and flipping the polygon. +- Create new points by clicking and dragging a line between two points. +- Remove a point by right-clicking it (or using the Remove tool described above + and left-clicking). +- Pan in the editor by middle-clicking or right-clicking. (Right-click panning + can only be used in areas where there is no point nearby.) + +You can use the default rectangle shape to quickly create a triangle-shaped +collision shape by removing one of the points: + +![Image](/img/Tutorials/2d/img/using_tilesets_creating_triangle_collision.webp) + + Creating a triangle collision shape by right-clicking one of the corners to remove it + +You can also use the rectangle as a base for more complex shapes by adding more points: + +![Image](/img/Tutorials/2d/img/using_tilesets_drawing_custom_collision.webp) + + Drawing a custom collision for a complex tile shape + +:::tip + +If you have a large tileset, specifying the collision for each tile +individually could take a lot of time. This is especially true as TileMaps +tend to have many tiles with common collision patterns (such as solid blocks +or 45-degree slopes). To apply a similar collision shape to several tiles +quickly, use functionality to +[assign properties to multiple tiles at once ](doc_using_tilemaps_assigning_properties_to_multiple_tiles). + +::: + +## Assigning custom metadata to the TileSet's tiles + +You can assign custom data on a per-tile basis using *custom data layers*. +This can be useful to store information specific to your game, such as the damage +that a tile should deal when the player touches it, or whether a tile can be +destroyed using a weapon. + +The data is associated with the tile in the TileSet: all instances of the placed +tile will use the same custom data. If you need to create a variant of a tile +that has different custom data, this can be done by [creating an alternative tile ](doc_using_tilesets_creating_alternative_tiles) and changing +the custom data for the alternative tile only. + +![Image](/img/Tutorials/2d/img/using_tilesets_create_custom_data_layer.webp) + + Creating a custom data layer in the TileSet resource inspector (within the TileMapLayer node) + +![Image](/img/Tutorials/2d/img/using_tilesets_custom_data_layers_example.webp) + + Example of configured custom data layers with game-specific properties + +You can reorder custom data without breaking existing metadata: the TileSet +editor will update automatically after reordering custom data properties. + +With the custom data layers example shown above, we're assigning a tile to have the +``damage_per_second`` metadata set to ``25`` and the ``destructible`` metadata +to ``false``: + +![Image](/img/Tutorials/2d/img/using_tilesets_edit_custom_data.webp) + + Editing custom data in the TileSet editor while in Select mode + +[Tile property painting ](doc_using_tilemaps_using_tile_property_painting) +can also be used for custom data: + +![Image](/img/Tutorials/2d/img/using_tilesets_paint_custom_data.webp) + + Assigning custom data in the TileSet editor using tile property painting + +## Creating terrain sets (autotiling) + +:::note + +This functionality was implemented in a different form as *autotiling* in Redot 3.x. +Terrains are essentially a more powerful replacement of autotiles. Unlike +autotiles, terrains can support transitions from one terrain to another, as +a tile may define several terrains at once. + +Unlike before, where autotiles were a specific kind of tiles, terrains are +only a set of properties assigned to atlas tiles. These properties are then +used by a dedicated TileMap painting mode that selects tiles featuring +terrain data in a smart way. This means any terrain tile can be either +painted as terrain or as a single tile, like any other. + +::: + +A "polished" tileset generally features variations that you should use on +corners or edges of platforms, floors, etc. While these can be placed manually, +this quickly becomes tedious. Handling this situation with procedurally +generated levels can also be difficult and require a lot of code. + +Redot offers *terrains* to perform this kind of tile connection automatically. +This allows you to have the "correct" tile variants automatically used. + +Terrains are grouped into terrain sets. Each terrain set is assigned a mode from +**Match Corners and Sides**, **Match Corners** and **Match sides**. They define how +terrains are matched to each other in a terrain set. + +:::note + +The above modes correspond to the previous bitmask modes autotiles used in +Redot 3.x: 2×2, 3×3 or 3×3 minimal. This is also similar to what +the [Tiled ](https://www.mapeditor.org/) editor features. + +::: + +Select the TileMapLayer node, go to the inspector and create a new terrain set within the TileSet *resource*: + +![Image](/img/Tutorials/2d/img/using_tilesets_create_terrain_set.webp) + + Creating a terrain set in the TileSet resource inspector (within the TileMapLayer node) + +After creating a terrain set, you **must** create one or more terrains *within* the terrain set: + +![Image](/img/Tutorials/2d/img/using_tilesets_create_terrain.webp) + + Creating a terrain within the terrain set + +In the TileSet editor, switch to Select mode and click a tile. In the middle +column, unfold the **Terrains** section then assign a terrain set ID and a +terrain ID for the tile. ``-1`` means "no terrain set" or "no terrain", which +means you must set **Terrain Set** to ``0`` or greater before you can set +**Terrain** to ``0`` or greater. + +:::note + +Terrain set IDs and terrain IDs are independent from each other. They also +start from ``0``, not ``1``. + +::: + +![Image](/img/Tutorials/2d/img/using_tilesets_configure_terrain_on_tile.webp) + + Configuring terrain on a single tile in the TileSet editor's Select mode + +After doing so, you can now configure the **Terrain Peering Bits** section which +becomes visible in the middle column. The peering bits determine which tile will +be placed depending on neighboring tiles. ``-1`` is a special value which refers +to empty space. + +For example, if a tile has all its bits set to ``0`` or greater, it will only +appear if *all* 8 neighboring tiles are using a tile with the same terrain ID. +If a tile has its bits set to ``0`` or greater, +but the top-left, top and top-right bits are set to ``-1``, it will only appear +if there is empty space on top of it (including diagonally). + +![Image](/img/Tutorials/2d/img/using_tilesets_configure_terrain_peering_bits.webp) + + Configuring terrain peering bits on a single tile in the TileSet editor's Select mode + +An example configuration for a full tilesheet may look as follows: + +![Image](/img/Tutorials/2d/img/using_tilesets_terrain_example_tilesheet.webp) + + Example full tilesheet for a sidescrolling game + +![Image](/img/Tutorials/2d/img/using_tilesets_terrain_example_tilesheet_configuration.webp) + + Example full tilesheet for a sidescrolling game with terrain peering bits visible + +## Assigning properties to multiple tiles at once + +There are two ways to assign properties to multiple tiles at once. +Depending on your use cases, one method may be faster than the other: + +### Using multiple tile selection + +If you wish to configure various properties on several tiles at once, +choose the **Select** mode at the top of the TileSet editor: + +After doing this, you can select multiple tiles on the right column by holding +`Shift` then clicking on tiles. You can also perform rectangle selection by +holding down the left mouse button then dragging the mouse. Lastly, you can +deselect tiles that were already selected (without affecting the rest of the +selection) by holding `Shift` then clicking on a selected tile. + +You can then assign properties using the inspector in the middle column of the +TileSet editor. Only properties that you change here will be applied to all +selected tiles. Like in the editor's inspector, properties that differ on +selected tiles will remain different until you edit them. + +With numerical and color properties, you will also see a preview of the +property's value on all tiles in the atlas after editing a property: + +![Image](/img/Tutorials/2d/img/using_tilesets_select_and_set_tile_properties.webp) + + Selecting multiple tiles using the Select mode, then applying properties + +### Using tile property painting + +If you wish to apply a single property to several tiles at once, +you can use the *property painting* mode for this purpose. + +Configure a property to be painted in the middle column, then +click on tiles (or hold down the left mouse button) in the right column +to "paint" properties onto tiles. + +![Image](/img/Tutorials/2d/img/using_tilesets_paint_tile_properties.webp) + + Painting tile properties using the TileSet editor + +Tile property painting is especially useful with properties that are +time-consuming to set manually, such as collision shapes: + +![Image](/img/Tutorials/2d/img/using_tilesets_paint_tile_properties_collision.webp) + + Painting a collision polygon, then left-clicking tiles to apply it + +## Creating alternative tiles + +Sometimes, you want to use a single tile image (found only once within the +atlas), but configured in different ways. For example, you may want to use the +same tile image, but rotated, flipped, or modulated with a different color. This +can be done using *alternative tiles*. + +:::tip + +Since Redot 4.2, you don't have to create alternative tiles to rotate or +flip tiles anymore. You can rotate any tile while placing it in the +TileMap editor by using the rotation/flip buttons in the TileMap editor +toolbar. + +::: + +To create an alternative tile, right-click a base tile in the atlas displayed by +the TileSet editor, then choose **Create an Alternative Tile**: + +![Image](/img/Tutorials/2d/img/using_tilesets_create_alternative_tile.webp) + + Creating an alternative tile by right-clicking a base tile in the TileSet editor + +If currently in Select mode, the alternative tile will already be selected +for editing. If not currently in Select mode, you can still create alternative +tiles, but you will need to switch to Select mode and select the alternative +tile to edit it. + +If you don't see the alternative tile, pan over to the right of the atlas image, +as alternative tiles always appear on the right of base tiles of a given atlas +in the TileSet editor: + +![Image](/img/Tutorials/2d/img/using_tilesets_configure_alternative_tile.webp) + + Configuring an alternative tile after clicking it in the TileSet editor + +After selecting an alternative tile, you can change any properties using the +middle column like you would on a base tile. However, the list of exposed +properties is different compared to base tiles: + +- **Alternative ID:** The unique numerical identifier for this alternative tile. + Changing it will break existing TileMaps, so be careful! This ID also controls + the sorting in the list of alternative tiles displayed in the editor. +- **Rendering > Flip H:** If ``true``, the tile is horizontally flipped. +- **Rendering > Flip V:** If ``true``, the tile is vertically flipped. +- **Rendering > Transpose:** If ``true``, the tile is rotated 90 degrees + *counter-clockwise* and then flipped vertically. In practice, this means that + to rotate a tile by 90 degrees clockwise without flipping it, you should + enable **Flip H** and **Transpose**. To rotate a tile by 180 degrees + clockwise, enable **Flip H** and **Flip V**. To rotate a tile by 270 degrees + clockwise, enable **Flip V** and **Transpose**. +- **Rendering > Texture Origin:** The origin to use for drawing the tile. This + can be used to visually offset the tile compared to the base tile. +- **Rendering > Modulate:** The color multiplier to use when rendering the tile. +- **Rendering > Material:** The material to use for this tile. This can be used + to apply a different blend mode or custom shaders to a single tile. +- **Z Index:** The sorting order for this tile. Higher values will make the tile + render in front of others on the same layer. +- **Y Sort Origin:** The vertical offset to use for tile sorting based on its Y + coordinate (in pixels). This allows using layers as if they were on different + height for top-down games. Adjusting this can help alleviate issues with + sorting certain tiles. Only effective if **Y Sort Enabled** is ``true`` on + the TileMapLayer node under **CanvasItem > Ordering** + +You can create an additional alternative tile variant by clicking the large "+" +icon next to the alternative tile. This is equivalent to selecting the base tile +and right-clicking it to choose **Create an Alternative Tile** again. + +:::note + +When creating an alternative tile, none of the properties from the base tile +are inherited. You must set properties again on the alternative tile if you +wish those to be identical on the base tile and the alternative tile. + +::: diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/3d_antialiasing.md b/Redot-Documentation/docs/26.1/Tutorials/3d/3d_antialiasing.md new file mode 100644 index 0000000..ec3caa5 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/3d_antialiasing.md @@ -0,0 +1,292 @@ + +# 3D antialiasing + +:::info + +Redot also supports antialiasing in 2D rendering. This is covered on the +[doc_2d_antialiasing](../2d/2d_antialiasing.md) page. + +::: + +## Introduction + +Due to their limited resolution, scenes rendered in 3D can exhibit aliasing +artifacts. These artifacts commonly manifest as a "staircase" effect on surface +edges (edge aliasing) and as flickering and/or sparkles on reflective surfaces +(specular aliasing). + +In the example below, you can notice how +edges have a blocky appearance. The vegetation is also flickering in and out, +and thin lines on top of the box have almost disappeared: + +
+ Image is scaled by 2× with nearest-neighbor filtering to make aliasing more noticeable. +
+ Image is scaled by 2× with nearest-neighbor filtering to make aliasing more noticeable. +
+
+ +To combat this, various antialiasing techniques can be used in Redot. These are +detailed below. + +:::info + +You can compare antialiasing algorithms in action using the +[3D Antialiasing demo project](https://github.com/redot-engine/redot-demo-projects/tree/master/3d/antialiasing). + +::: + +## Multisample antialiasing (MSAA) + +*This is available in all renderers.* + +This technique is the "historical" way of dealing with aliasing. MSAA is very +effective on geometry edges (especially at higher levels). MSAA does not +introduce any blurriness whatsoever. + +MSAA is available in 3 levels: 2×, 4×, 8×. Higher levels are more effective at +antialiasing edges, but are significantly more demanding. In games with modern +visuals, sticking to 2× or 4× MSAA is highly recommended as 8× MSAA is usually +too demanding. + +The downside of MSAA is that it only operates on edges. This is because MSAA +increases the number of *coverage* samples, but not the number of *color* +samples. However, since the number of color samples did not increase, fragment +shaders are still run for each pixel only once. Therefore, MSAA does not reduce +transparency aliasing for materials using the **Alpha Scissor** transparency +mode (1-bit transparency). MSAA is also ineffective on specular aliasing. + +To mitigate aliasing on alpha scissor materials, +[alpha antialiasing](doc_standard_material_3d_alpha_antialiasing) +(also called *alpha to coverage*) can be enabled on specific materials in the +StandardMaterial3D or ORMMaterial3D properties. Alpha to coverage has a +moderate performance cost, but it's effective at reducing aliasing on +transparent materials without introducing any blurriness. + +To make specular aliasing less noticeable, use the [Screen-space roughness limiter](Screen-space roughness limiter), +which is enabled by default. + +MSAA can be enabled in the Project Settings by changing the value of the +[Rendering > Anti Aliasing > Quality > MSAA 3D](class_ProjectSettings_property_rendering/anti_aliasing/quality/msaa_3d) +setting. It's important to change the value of the **MSAA 3D** setting and not **MSAA 2D**, as these are entirely +separate settings. + +Comparison between no antialiasing (left) and various MSAA levels (right). +Note that alpha antialiasing is not used here: + +![Image](/img/Tutorials/3d/img/antialiasing_msaa_2x.webp) + +![Image](/img/Tutorials/3d/img/antialiasing_msaa_4x.webp) + +![Image](/img/Tutorials/3d/img/antialiasing_msaa_8x.webp) + +## Temporal antialiasing (TAA) + +*This is only available in the Forward+ renderer, not the Mobile or Compatibility +renderers.* + +Temporal antialiasing works by *converging* the result of previously rendered +frames into a single, high-quality frame. This is a continuous process that +works by jittering the position of all vertices in the scene every frame. This +jittering is done to capture sub-pixel detail and should be unnoticeable except +in extreme situations. + +This technique is commonly used in modern games, as it provides the most +effective form of antialiasing against specular aliasing and other +shader-induced artifacts. TAA also provides full support for transparency +antialiasing. + +TAA introduces a small amount of blur when enabled in still scenes, but this +blurring effect becomes more pronounced when the camera is moving. Another +downside of TAA is that it can exhibit *ghosting* artifacts behind moving +objects. Rendering at a higher framerate will allow TAA to converge faster, +therefore making those ghosting artifacts less visible. + +Temporal antialiasing can be enabled in the Project Settings by changing the value of the +[Rendering > Anti Aliasing > Quality > TAA](class_ProjectSettings_property_rendering/anti_aliasing/quality/use_taa) +setting. + +Comparison between no antialiasing (left) and TAA (right): + +![Image](/img/Tutorials/3d/img/antialiasing_taa.webp) + +## AMD FidelityFX Super Resolution 2.2 (FSR2) + +*This is only available in the Forward+ renderer, not the Mobile or Compatibility +renderers.* + +Since Redot 4.2, there is built-in support for +[AMD FidelityFX Super Resolution](https://www.amd.com/en/products/graphics/technologies/fidelityfx/super-resolution.html) +2.2. This is an [upscaling method](resolution_scaling.md) +compatible with all recent GPUs from any vendor. FSR2 is normally designed to +improve performance by lowering the internal 3D rendering resolution, +then upscaling to the output resolution. + +However, unlike FSR1, FSR2 also provides temporal antialiasing. This means FSR2 +can be used at native resolution for high-quality antialiasing, with the input +resolution being equal to the output resolution. In this situation, enabling +FSR2 will actually *decrease* performance, but it will significantly improve +rendering quality. + +Using FSR2 at native resolution is more demanding than using TAA at native +resolution, so its use is only recommended if you have significant GPU headroom. +On the bright side, FSR2 provides better antialiasing coverage with less +blurriness compared to TAA, especially in motion. + +Comparison between no antialiasing (left) and FSR2 at native resolution (right): + +![Image](/img/Tutorials/3d/img/antialiasing_fsr2_native.webp) + +:::note + +By default, the **FSR Sharpness** project setting is set to ``0.2`` (higher +values result in less sharpening). For the purposes of comparison, FSR +sharpening has been disabled by setting it to ``2.0`` on the above screenshot. + +::: + +## Fast approximate antialiasing (FXAA) + +*This is only available in the Forward+ and Mobile renderers, not the Compatibility +renderer.* + +Fast approximate antialiasing is a post-processing antialiasing solution. It is +faster to run than any other antialiasing technique and also supports +antialiasing transparency. However, since it lacks temporal information, it will +not do much against specular aliasing. + +This technique is still sometimes used in mobile games. However, on desktop +platforms, FXAA generally fell out of fashion in favor of temporal antialiasing, +which is much more effective against specular aliasing. Nonetheless, exposing FXAA +as an in-game option may still be worthwhile for players with low-end GPUs. + +FXAA introduces a moderate amount of blur when enabled (more than TAA when +still, but less than TAA when the camera is moving). + +FXAA can be enabled in the Project Settings by changing the value of the +[Rendering > Anti Aliasing > Quality > Screen Space AA](class_ProjectSettings_property_rendering/anti_aliasing/quality/screen_space_aa) +setting to ``FXAA``. + +Comparison between no antialiasing (left) and FXAA (right): + +![Image](/img/Tutorials/3d/img/antialiasing_fxaa.webp) + +## Supersample antialiasing (SSAA) + +*This is available in all renderers.* + +Supersampling provides the highest quality of antialiasing possible, but it's +also the most expensive. It works by shading every pixel in the scene multiple +times. This allows SSAA to antialias edges, transparency *and* specular aliasing +at the same time, without introducing potential ghosting artifacts. + +The downside of SSAA is its *extremely* high cost. This cost generally makes +SSAA difficult to use for game purposes, but you may still find supersampling +useful for [offline rendering](../animation/creating_movies.md). + +Supersample antialiasing is performed by increasing the +[Rendering > Scaling 3D > Scale](class_ProjectSettings_property_rendering/scaling_3d/scale) +advanced project setting above ``1.0`` while ensuring +[Rendering > Scaling 3D > Mode](class_ProjectSettings_property_rendering/scaling_3d/mode) +is set to ``Bilinear`` (the default). +Since the scale factor is defined per-axis, a scale factor of ``1.5`` will result +in 2.25× SSAA while a scale factor of ``2.0`` will result in 4× SSAA. Since Redot +uses the hardware's own bilinear filtering to perform the downsampling, the result +will look crisper at integer scale factors (namely, ``2.0``). + +Comparison between no antialiasing (left) and various SSAA levels (right): + +![Image](/img/Tutorials/3d/img/antialiasing_ssaa_2.25x.webp) + +![Image](/img/Tutorials/3d/img/antialiasing_ssaa_4x.webp) + +:::warning + +Supersampling also has high video RAM requirements, since it needs to render +in the target resolution then *downscale* to the window size. For example, +displaying a project in 3840×2160 (4K resolution) with 4× SSAA will require +rendering the scene in 7680×4320 (8K resolution), which is 4 times more +pixels. + +If you are using a high window size such as 4K, you may find that increasing +the resolution scale past a certain value will cause a heavy slowdown (or +even a crash) due to running out of VRAM. + +::: + +## Screen-space roughness limiter + +*This is only available in the Forward+ and Mobile renderers, not the Compatibility +renderer.* + +This is not an edge antialiasing method, but it is a way of reducing specular +aliasing in 3D. + +The screen-space roughness limiter works best on detailed geometry. While it has +an effect on roughness map rendering itself, its impact is limited there. + +The screen-space roughness limiter is enabled by default; it doesn't require +any manual setup. It has a small performance impact, so consider disabling it +if your project isn't affected by specular aliasing much. You can disable it +with the **Rendering > Quality > Screen Space Filters > Screen Space Roughness Limiter** +project setting. + +## Texture roughness limiter on import + +Like the screen-space roughness limiter, this is not an edge antialiasing +method, but it is a way of reducing specular aliasing in 3D. + +Roughness limiting on import works by specifying a normal map to use as a guide +for limiting roughness. This is done by selecting the roughness map in the +FileSystem dock, then going to the Import dock and setting **Roughness > Mode** +to the color channel the roughness map is stored in (typically **Green**), then +setting the path to the material's normal map. Remember to click **Reimport** +at the bottom of the Import dock after setting the path to the normal map. + +Since this processing occurs purely on import, it has no performance cost +whatsoever. However, its visual impact is limited. Limiting roughness on import +only helps reduce specular aliasing within textures, not the aliasing that +occurs on geometry edges on detailed meshes. + +## Which antialiasing technique should I use? + +**There is no "one size fits all" antialiasing technique.** Since antialiasing is +often demanding on the GPU or can introduce unwanted blurriness, you'll want to +add a setting to allow players to disable antialiasing. + +For projects with a photorealistic art direction, TAA is generally the most +suitable option. While TAA can introduce ghosting artifacts, there is no other +technique that combats specular aliasing as well as TAA does. The screen-space +roughness limiter helps a little, but is far less effective against specular +aliasing overall. If you have spare GPU power, you can use FSR2 at native +resolution for a better-looking form of temporal antialiasing compared to +standard TAA. + +For projects with a low amount of reflective surfaces (such as a cartoon +artstyle), MSAA can work well. MSAA is also a good option if avoiding blurriness +and temporal artifacts is important, such as in competitive games. + +When targeting low-end platforms such as mobile or integrated graphics, FXAA is +usually the only viable option. 2× MSAA may be usable in some circumstances, +but higher MSAA levels are unlikely to run smoothly on mobile GPUs. + +Redot allows using multiple antialiasing techniques at the same time. This is +usually unnecessary, but it can provide better visuals on high-end GPUs or for +[non-real-time rendering](../animation/creating_movies.md). For example, to make +moving edges look better when TAA is enabled, you can also enable MSAA at the +same time. + +### Antialiasing comparison + +| Feature | MSAA | TAA | FSR2 | FXAA | SSAA | SSRL | +| --- | --- | --- | --- | --- | --- | --- | +| Edge antialiasing | 🟢 Yes \| | 🟢 Yes \| | Yes \| 🟢 | Yes \| 🟢 | es \| 🔴 N | | +| Specular antialiasing | 🟡 Some \| | 🟢 Yes \| | Yes \| 🟡 | Some \| 🟢 | es \| 🟢 Y | s \| | +| Transparency antialiasing | 🟡 Some [1]_ \| | 🟢 Yes [2]_ \| | Yes [2]_ \| 🟢 | Yes \| 🟢 | es \| 🔴 N | | +| Added blur | 🟢 None \| | 🟡 Some \| | Some \| 🟡 | Some \| 🟡 | ome [3]_ \| 🟢 N | ne \| | +| Ghosting artifacts | 🟢 None \| | 🔴 Yes \| | Yes \| 🟢 | None \| 🟢 | one \| 🟢 N | ne \| | +| Performance cost | 🟡 Medium \| | 🟡 Medium \| | High \| 🟢 | Low \| 🔴 | ery High \| 🟢 L | w \| | +| Forward+ | ✔️ Yes | ✔️ Yes | ✔️ Yes | ✔️ Yes | ✔️ Yes | ✔️ Yes | +| Mobile | ✔️ Yes | ❌ No \| | ❌ No \| | ️ Yes \| | ️ Yes \| | ️ Yes \| | +| Compatibility | ✔️ Yes | ❌ No \| | ❌ No \| | No \| ✔ | Yes \| ❌ | No \| | + diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/3d_rendering_limitations.md b/Redot-Documentation/docs/26.1/Tutorials/3d/3d_rendering_limitations.md new file mode 100644 index 0000000..03eb8e0 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/3d_rendering_limitations.md @@ -0,0 +1,147 @@ + +# 3D rendering limitations + +## Introduction + +Due to their focus on performance, real-time rendering engines have many +limitations. Redot's renderer is no exception. To work effectively with those +limitations, you need to understand them. + +## Texture size limits + +On desktops and laptops, textures larger than 8192×8192 may not be supported on +older devices. You can check your target GPU's limitations on +[GPUinfo.org](https://www.gpuinfo.org/). + +Mobile GPUs are typically limited to 4096×4096 textures. Also, some mobile GPUs +don't support repeating non-power-of-two-sized textures. Therefore, if you want +your texture to display correctly on all platforms, you should avoid using +textures larger than 4096×4096 and use a power of two size if the texture needs +to repeat. + +To limit the size of a specific texture that may be too large to render, you can +set the **Process > Size Limit** import option to a value greater than ``0``. +This will reduce the texture's dimensions on import (preserving aspect ratio) +without affecting the source file. + +## Color banding + +When using the Forward+ or Mobile rendering methods, Redot's 3D engine +renders internally in HDR. However, the rendering output will be tonemapped to a +low dynamic range so it can be displayed on the screen. This can result in +visible banding, especially when using untextured materials. For performance +reasons, color precision is also lower when using the Mobile rendering method +compared to Forward+. + +When using the Compatibility rendering method, HDR is not used and the color +precision is the lowest of all rendering methods. This also applies to 2D +rendering, where banding may be visible when using smooth gradient textures. + +There are two main ways to alleviate banding: + +- If using the Forward+ or Forward Mobile rendering methods, enable + [Use Debanding](class_ProjectSettings_property_rendering/anti_aliasing/quality/use_debanding) + in **Project Settings > Rendering > Anti Aliasing**. This applies a fullscreen debanding + shader as a post-processing effect and is very cheap. +- Alternatively, bake some noise into your textures. This is mainly effective in + 2D, e.g. for vignetting effects. In 3D, you can also use a `custom debanding + shader to + be applied on your *materials*. This technique works even if your project is + rendered with low color precision, which means it will work when using the + Mobile and Compatibility rendering methods. + +
+ Color banding comparison (contrast increased for more visibility) +
+ Color banding comparison (contrast increased for more visibility) +
+
+ +:::info + +See [Banding in Games: A Noisy Rant (PDF)](https://loopit.dk/banding_in_games.pdf) +for more details about banding and ways to combat it. + +::: + +## Depth buffer precision + +To sort objects in 3D space, rendering engines rely on a *depth buffer* (also +called *Z-buffer*). This buffer has a finite precision: 24-bit on desktop +platforms, sometimes 16-bit on mobile platforms (for performance reasons). If +two different objects end up on the same buffer value, then Z-fighting will +occur. This will materialize as textures flickering back and forth as the camera +moves or rotates. + +To make the depth buffer more precise over the rendered area, you should +*increase* the Camera node's **Near** property. However, be careful: if you set +it too high, players will be able to see through nearby geometry. You should +also *decrease* the Camera node's **Far** property to the lowest permissible value +for your use case, though keep in mind it won't impact precision as much as the +**Near** property. + +If you only need high precision when the player can see far away, you could +change it dynamically based on the game conditions. For instance, if the player +enters an airplane, the **Near** property can be temporarily increased to avoid +Z-fighting in the distance. It can then be decreased once the player leaves the +airplane. + +Depending on the scene and viewing conditions, you may also be able to move the +Z-fighting objects further apart without the difference being visible to the +player. + +
+ Z-fighting comparison (before and after tweaking the scene by offsetting the Label3D away from the floor) +
+ Z-fighting comparison (before and after tweaking the scene by offsetting the Label3D away from the floor) +
+
+ +## Transparency sorting + +In Redot, transparent materials are drawn after opaque materials. Transparent +objects are sorted back to front before being drawn based on the Node3D's +position, not the vertex position in world space. Due to this, overlapping +objects may often be sorted out of order. To fix improperly sorted objects, +tweak the material's +[Render Priority](class_Material_property_render_priority) +property or the node's +[Sorting Offset](class_VisualInstance3D_property_sorting_offset). +Render Priority will force specific materials to appear in front of or behind +other transparent materials, while Sorting Offset will move the object +forward or backward for the purpose of sorting. Even then, these may not +always be sufficient. + +Some rendering engines feature *order-independent transparency* techniques to +alleviate this, but this is costly on the GPU. Redot currently doesn't provide +this feature. There are still several ways to avoid this problem: + +- Only make materials transparent if you actually need it. If a material only + has a small transparent part, consider splitting it into a separate material. + This will allow the opaque part to cast shadows and will also improve performance. + +- If your texture mostly has fully opaque and fully transparent areas, you can + use alpha testing instead of alpha blending. This transparency mode is faster + to render and doesn't suffer from transparency issues. Enable **Transparency > + Transparency** to **Alpha Scissor** in StandardMaterial3D, and adjust + **Transparency > Alpha Scissor Threshold** accordingly if needed. Note that + MSAA will not antialias the texture's edges unless alpha antialiasing is + enabled in the material's properties. However, FXAA, TAA and supersampling + will be able to antialias the texture's edges regardless of whether alpha + antialiasing is enabled on the material. + +- If you need to render semi-transparent areas of the texture, alpha scissor + isn't suitable. Instead, setting the StandardMaterial3D's + **Transparency > Transparency** property to **Depth Pre-Pass** can sometimes + work (at a performance cost). You can also try the **Alpha Hash** mode. + +- If you want a material to fade with distance, use the StandardMaterial3D + distance fade mode **Pixel Dither** or **Object Dither** instead of + **Pixel Alpha**. This will make the material opaque, which also speeds up rendering. + +
+ Transparency sorting comparison (alpha-blended materials on the left, alpha scissor materials on the right) +
+ Transparency sorting comparison (alpha-blended materials on the left, alpha scissor materials on the right) +
+
diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/3d_text.md b/Redot-Documentation/docs/26.1/Tutorials/3d/3d_text.md new file mode 100644 index 0000000..399279e --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/3d_text.md @@ -0,0 +1,164 @@ + +# 3D text + +## Introduction + +In a project, there may be times when text needs to be created as part of a 3D +scene and not just in the HUD. Redot provides 2 methods to do this: the +Label3D node and the TextMesh *resource* for a MeshInstance3D node. + +Additionally, Redot makes it possible to position Control nodes according to a +3D point's position on the camera. This can be used as an alternative to "true" +3D text in situations where Label3D and TextMesh aren't flexible enough. + +:::info + +You can see 3D text in action using the +[3D Labels and Texts demo project](https://github.com/redot-engine/redot-demo-projects/tree/master/3d/labels_and_texts). + +This page does **not** cover how to display a GUI scene within a 3D +environment. For information on how to achieve that, see the +[GUI in 3D](https://github.com/redot-engine/redot-demo-projects/tree/master/viewport/gui_in_3d) +demo project. + +::: + +## Label3D + +![Image](/img/Tutorials/3d/img/label_3d.png) + +Label3D behaves like a Label node, but in 3D space. Unlike the Label node, this +Label3D node does **not** inherit properties of a GUI theme. However, its look +remains customizable and uses the same font subresource as Control nodes +(including support for :abbr:`MSDF (Multi-channel Signed Distance Font)` font +rendering). + +### Advantages + +- Label3D is faster to generate than TextMesh. While both use a caching + mechanism to only render new glyphs once, Label3D will still be faster to + (re)generate, especially for long text. This can avoid stuttering during + gameplay on low-end CPUs or mobile. +- Label3D can use bitmap fonts and dynamic fonts (with and without + :abbr:`MSDF (Multi-channel Signed Distance Font)` or mipmaps). This makes it + more flexible on that aspect compared to TextMesh, especially for rendering + fonts with self-intersecting outlines or colored fonts (emoji). + +:::info + +See [doc_gui_using_fonts](../ui/gui_using_fonts.md) for guidelines on configuring font imports. + +::: + +### Limitations + +By default, Label3D has limited interaction with a 3D environment. It can be +occluded by geometry and lit by light sources if the **Shaded** flag is enabled. +However, it will not cast shadows even if **Cast Shadow** is set to **On** in +the Label3D's GeometryInstance3D properties. This is because the node internally +generates a quad mesh (one glyph per quad) with transparent textures and has the +same limitations as Sprite3D. Transparency sorting issues can also become apparent +when several Label3Ds overlap, especially if they have outlines. + +This can be mitigated by setting the Label3D's transparency mode to **Alpha +Cut**, at the cost of less smooth text rendering. The **Opaque Pre-Pass** +transparency mode can preserve text smoothness while allowing the Label3D to +cast shadows, but some transparency sorting issues will remain. + +See [Transparency sorting](doc_3d_rendering_limitations_transparency_sorting) +section in the 3D rendering limitations page for more information. + +Text rendering quality can also suffer when the Label3D is viewed at a distance. To improve +text rendering quality, [enable mipmaps on the font](doc_using_fonts_mipmaps) or +[switch the font to use MSDF rendering](doc_using_fonts_msdf). + +## TextMesh + +![Image](/img/Tutorials/3d/img/text_mesh.png) + +The TextMesh resource has similarities to Label3D. They both display text in a +3D scene, and will use the same font subresource. However, instead of generating +transparent quads, TextMesh generates 3D geometry that represents the glyphs' +contours and has the properties of a mesh. As a result, a TextMesh is shaded by +default and automatically casts shadows onto the environment. A TextMesh can +also have a material applied to it (including custom shaders). + +Here is an example of a texture and how it's applied to the mesh. You can use +the texture below as a reference for the generated mesh's UV map: + +![Image](/img/Tutorials/3d/img/text_mesh_texture.png) + +![Image](/img/Tutorials/3d/img/text_mesh_textured.png) + +### Advantages + +TextMesh has a few advantages over Label3D: + +- TextMesh can use a texture to modify text color on a per-side basis. +- TextMesh geometry can have actual depth to it, giving glyphs a 3D look. +- TextMesh can use custom shaders, unlike Label3D. + +### Limitations + +There are some limitations to TextMesh: + +- No built-in outline support, unlike Label3D. This can be simulated using custom + shaders though. +- Only dynamic fonts are supported (``.ttf``, ``.otf``, ``.woff``, ``.woff2``). + Bitmap fonts in the ``.fnt`` or ``.font`` formats are **not** supported. +- Fonts with self-intersecting outlines will not render correctly. + If you notice rendering issues on fonts downloaded from websites such as + Google Fonts, try downloading the font from the font author's official + website instead. +- Antialiasing the text rendering requires a full-scene antialiasing method to + be enabled such as MSAA, FXAA and temporal antialiasing (TAA). If no + antialiasing method is enabled, text will appear grainy, especially at a + distance. See [doc_3d_antialiasing](3d_antialiasing.md) for more information. + +## Projected Label node (or any other Control) + +There is a last solution that is more complex to set up, but provides the most +flexibility: projecting a 2D node onto 3D space. This can be achieved using the +return value of [unproject_position](class_Camera3D_method_unproject_position) +method on a Camera3D node in a script's ``_process()`` function. This return value +should then be used to set the ``position`` property of a Control node. + +See the [3D waypoints](https://github.com/redot-engine/redot-demo-projects/tree/master/3d/waypoints) +demo for an example of this. + +### Advantages + +- Any Control node can be used, including Label, RichTextLabel or even nodes such + as Button. This allows for powerful formatting and GUI interaction. +- The script-based approach allows for complete freedom in positioning. + For example, this makes it considerably easier to pin Controls to the screen's + edges when they go off-screen (for in-game 3D markers). +- Control theming is obeyed. This allows for easier customization that globally + applies to the project. + +### Limitations + +- Projected Controls cannot be occluded by 3D geometry in any way. You can use a + RayCast to fully hide the control if its target position is occluded by a + collider, but this doesn't allow for partially hiding the control behind a + wall. +- Changing text size depending on distance by adjusting the Control's ``scale`` + property is possible, but it needs to be done manually. Label3D and TextMesh + automatically take care of this, at the cost of less flexibility (can't set a + minimum/maximum text size in pixels). +- Handling resolution and aspect ratio changes must be taken into account in the + script, which can be challenging. + +## Should I use Label3D, TextMesh or a projected Control? + +In most scenarios, Label3D is recommended as it's easier to set up and provides +higher rendering quality (especially if 3D antialiasing is disabled). + +For advanced use cases, TextMesh is more flexible as it allows styling the text +with custom shaders. Custom shaders allow for modifying the final geometry, such +as curving the text along a surface. Since the text is actual 3D geometry, the +text can optionally have depth to it and can also contribute to global +illumination. + +If you need features such as BBCode or Control theming support, then using a projected +RichTextLabel node is the only way to go. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/csg_tools.md b/Redot-Documentation/docs/26.1/Tutorials/3d/csg_tools.md new file mode 100644 index 0000000..324c7e8 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/csg_tools.md @@ -0,0 +1,368 @@ +:::warning +This page is marked as outdated and may not reflect current Redot behavior. +::: + +# Prototyping levels with CSG + +CSG stands for **Constructive Solid Geometry**, and is a tool to combine basic +shapes or custom meshes to create more complex shapes. In 3D modeling software, +CSG is mostly known as "Boolean Operators". + +Level prototyping is one of the main uses of CSG in Redot. This technique allows +users to create the most common shapes by combining primitives. +Interior environments can be created by using inverted primitives. + +:::note +The CSG nodes in Redot are mainly intended for prototyping. There is +no built-in support for UV mapping or editing 3D polygons (though +extruded 2D polygons can be used with the CSGPolygon3D node). In +addition CSG can't reliably create meshes made up of multiple nodes +without holes. + +If you're looking for an easy to use level design tool for a project, +you may want to use [FuncRedot](https://github.com/func-godot/func_godot_plugin) +or [Cyclops Level Builder](https://github.com/blackears/cyclopsLevelBuilder) +instead. + +::: + + + +:::info + +You can check how to use CSG nodes to build various shapes (such as stairs or roads) using the +[Constructive Solid Geometry demo project](https://github.com/redot-engine/redot-demo-projects/tree/master/3d/csg). + +::: + +## Introduction to CSG nodes + +Like other features of Redot, CSG is supported in the form of nodes. These are +the CSG nodes: + +- [CSGBox3D](class_CSGBox3D) +- [CSGCylinder3D](class_CSGCylinder3D) (also supports cone) +- [CSGSphere3D](class_CSGSphere3D) +- [CSGTorus3D](class_CSGTorus3D) +- [CSGPolygon3D](class_CSGPolygon3D) +- [CSGMesh3D](class_CSGMesh3D) +- [CSGCombiner3D](class_CSGCombiner3D) + +![Image](/img/Tutorials/3d/img/csg_nodes.png) + +![Image](/img/Tutorials/3d/img/csg_mesh.png) + +### CSG tools features + +Every CSG node supports 3 kinds of boolean operations: + +- **Union:** Geometry of both primitives is merged, intersecting geometry + is removed. +- **Intersection:** Only intersecting geometry remains, the rest is removed. +- **Subtraction:** The second shape is subtracted from the first, leaving a dent + with its shape. + +![Image](/img/Tutorials/3d/img/csg_operation_menu.png) + +![Image](/img/Tutorials/3d/img/csg_operation.png) + +### CSGPolygon + +The [CSGPolygon3D](class_CSGPolygon3D) node extrude along a Polygon drawn in +2D (in X, Y coordinates) in the following ways: + +- **Depth:** Extruded back a given amount. +- **Spin:** Extruded while spinning around its origin. +- **Path:** Extruded along a Path node. This operation is commonly called + lofting. + +![Image](/img/Tutorials/3d/img/csg_poly_mode.png) + +![Image](/img/Tutorials/3d/img/csg_poly.png) + +:::note +The **Path** mode must be provided with a :ref:`Path3D ` +node to work. In the Path node, draw the path and the polygon in +CSGPolygon3D will extrude along the given path. + +::: + +### Custom meshes + +Custom meshes can be used for [CSGMesh3D](class_CSGMesh3D) as long as the +mesh is *manifold*. The mesh can be modeled in other software and imported into +Redot. Multiple materials are supported. + +For a mesh to be used as a CSG mesh, it is required to: + +- be closed +- have each edge connect to only two faces +- have volume + +And it is recommended to avoid: + +- negative volume +- self-intersection +- interior faces + +Redot uses the [manifold](https://github.com/elalish/manifold) library to +implement CSG meshes. The technical definition of "manifold" used by Redot is +the following, adapted from that library's [definition of "manifold"](https://github.com/elalish/manifold/wiki/Manifold-Library#manifoldness-definition): + + Every edge of every triangle must contain the same two vertices (by index) as + exactly one other triangle edge, and the start and end vertices must switch + places between these two edges. The triangle vertices must appear in clockwise + order when viewed from the outside of the Redot Engine manifold mesh. + +![Image](/img/Tutorials/3d/img/csg_custom_mesh.png) + +### CSGCombiner3D + +The [CSGCombiner3D](class_CSGCombiner3D) node is an empty shape used for +organization. It will only combine children nodes. + +### Processing order + +Every CSG node will first process its children nodes and their operations: +union, intersection, or subtraction, in tree order, and apply them to itself one +after the other. + +:::note +In the interest of performance, make sure CSG geometry remains +relatively simple, as complex meshes can take a while to process. +If adding objects together (such as table and room objects), create +them as separate CSG trees. Forcing too many objects in a single tree +will eventually start affecting performance. +Only use binary operations where you actually need them. + +::: + +## Prototyping a level + +We will prototype a room to practice the use of CSG tools. + +:::tip +Working in **Orthogonal** projection gives a better view when combining +the CSG shapes. + +::: + +Our level will contain these objects: + +- a room, +- a bed, +- a lamp, +- a desk, +- a bookshelf. + +Create a scene with a Node3D node as root node. + +:::tip +The default lighting of the environment doesn't provide clear shading +at some angles. Change the display mode using **Display Overdraw** in +the 3D viewport menu, or add a DirectionalLight node to help you see +clearly. + +::: + +![Image](/img/Tutorials/3d/img/csg_overdraw.png) + +Create a CSGBox3D and name it ``room``, enable **Invert Faces** and change the +dimensions of your room. + +![Image](/img/Tutorials/3d/img/csg_room.png) + +![Image](/img/Tutorials/3d/img/csg_room_invert.png) + +Next, create a CSGCombiner3D and name it ``desk``. + +A desk has one surface and 4 legs: + +- Create 1 CSGBox3D children node in **Union** mode for the surface + and adjust the dimensions. +- Create 4 CSGBox3D children nodes in **Union** mode for the legs + and adjust the dimensions. + +Adjust their placement to resemble a desk. + +![Image](/img/Tutorials/3d/img/csg_desk.png) + +:::note +CSG nodes inside a CSGCombiner3D will only process their operation +within the combiner. Therefore, CSGCombiner3Ds are used to organize +CSG nodes. + +::: + +Create a CSGCombiner3D and name it ``bed``. + +Our bed consists of 3 parts: the bed, the mattress and a pillow. Create a CSGBox3D +and adjust its dimension for the bed. Create another CSGBox3D and adjust its +dimension for the mattress. + +![Image](/img/Tutorials/3d/img/csg_bed_mat.png) + +We will create another CSGCombiner3D named ``pillow`` as the child of ``bed``. +The scene tree should look like this: + +![Image](/img/Tutorials/3d/img/csg_bed_tree.png) + +We will combine 3 CSGSphere3D nodes in **Union** mode to form a pillow. Scale the +Y axis of the spheres and enable **Smooth Faces**. + +![Image](/img/Tutorials/3d/img/csg_pillow_smooth.png) + +Select the ``pillow`` node and switch the mode to **Subtraction**; the combined +spheres will cut a hole into the mattress. + +![Image](/img/Tutorials/3d/img/csg_pillow_hole.png) + +Try to re-parent the ``pillow`` node to the root ``Node3D`` node; the hole will +disappear. + +:::note +This is to illustrate the effect of CSG processing order. +Since the root node is not a CSG node, the CSGCombiner3D nodes are +the end of the operations; this shows the use of CSGCombiner3D to +organize the CSG scene. + +::: + +Undo the re-parent after observing the effect. The bed you've built should look +like this: + +![Image](/img/Tutorials/3d/img/csg_bed.png) + +Create a CSGCombiner3D and name it ``lamp``. + +A lamp consists of 3 parts: the stand, the pole and the lampshade. +Create a CSGCylinder3D, enable the **Cone** option and make it the stand. Create +another CSGCylinder3D and adjust the dimensions to use it as a pole. + +![Image](/img/Tutorials/3d/img/csg_lamp_pole_stand.png) + +We will use a CSGPolygon3D for the lampshade. Use the **Spin** mode for the +CSGPolygon3D and draw a [trapezoid](https://en.wikipedia.org/wiki/Trapezoid) +while in **Front View** (numeric keypad 1); this shape will extrude around the +origin and form the lampshade. + +![Image](/img/Tutorials/3d/img/csg_lamp_spin.png) + +![Image](/img/Tutorials/3d/img/csg_lamp_polygon.png) + +![Image](/img/Tutorials/3d/img/csg_lamp_extrude.png) + +Adjust the placement of the 3 parts to make it look like a lamp. + +![Image](/img/Tutorials/3d/img/csg_lamp.png) + +Create a CSGCombiner3D and name it ``bookshelf``. + +We will use 3 CSGBox3D nodes for the bookshelf. Create a CSGBox3D and adjust its +dimensions; this will be the size of the bookshelf. + +![Image](/img/Tutorials/3d/img/csg_shelf_big.png) + +Duplicate the CSGBox3D and shorten the dimensions of each axis and change the mode +to **Subtraction**. + +![Image](/img/Tutorials/3d/img/csg_shelf_subtract.png) + +![Image](/img/Tutorials/3d/img/csg_shelf_subtract_menu.png) + +You've almost built a shelf. Create one more CSGBox3D for dividing the shelf into +two levels. + +![Image](/img/Tutorials/3d/img/csg_shelf.png) + +Position your furniture in your room as you like and your scene should look +this: + +![Image](/img/Tutorials/3d/img/csg_room_result.png) + +You've successfully prototyped a room level with the CSG tools in Redot. +CSG tools can be used for designing all kinds of levels, such as a maze +or a city; explore its limitations when designing your game. + +## Using prototype textures + +Redot's [doc_standard_material_3d](standard_material_3d.md) supports *triplanar mapping*, which can be +used to automatically apply a texture to arbitrary objects without distortion. +This is handy when using CSG as Redot doesn't support editing UV maps on CSG +nodes yet. Triplanar mapping is relatively slow, which usually restricts its +usage to organic surfaces like terrain. Still, when prototyping, it can be used +to quickly apply textures to CSG-based levels. + +:::note +If you need some textures for prototyping, Kenney made a +[set of CC0-licensed prototype textures](https://kenney.nl/assets/prototype-textures). + +::: + +There are two ways to apply a material to a CSG node: + +- Applying it to a CSGCombiner3D node as a material override + (**Geometry > Material Override** in the Inspector). This will affect its + children automatically, but will make it impossible to change the material in + individual children. +- Applying a material to individual nodes (**Material** in the Inspector). This + way, each CSG node can have its own appearance. Subtractive CSG nodes will + apply their material to the nodes they're "digging" into. + +To apply triplanar mapping to a CSG node, select it, go to the Inspector, click +the **[empty]** text next to **Material Override** (or **Material** for +individual CSG nodes). Choose **New StandardMaterial3D**. Click the newly created +material's icon to edit it. Unfold the **Albedo** section and load a texture +into the **Texture** property. Now, unfold the **Uv1** section and check +**Triplanar**. You can change the texture offset and scale on each axis by +playing with the **Scale** and **Offset** properties just above. Higher values +in the **Scale** property will cause the texture to repeat more often. + +:::tip +You can copy a StandardMaterial3D to reuse it across CSG nodes. To do so, +click the dropdown arrow next to a material property in the Inspector +and choose **Copy**. To paste it, select the node you'd like to apply +the material onto, click the dropdown arrow next to its material +property then choose **Paste**. + +::: + +## Converting to MeshInstance3D + +Since Redot 4.4, you can convert a CSG node and its children to a [class_MeshInstance3D](class_MeshInstance3D) node. + +This has several benefits: + +- Bake lightmaps, since UV2 can be generated on a MeshInstance3D. +- Bake occlusion culling, since the occlusion culling bake process only takes MeshInstance3D into account. +- Faster loading times, since the CSG mesh no longer needs to be rebuilt when the scene loads. +- Better performance when updating the node's transform if using the mesh within another CSG node. + +To convert a CSG node to a MeshInstance3D node, select it, then choose +**CSG > Bake Mesh Instance** in the toolbar. The MeshInstance3D node +will be created as a sibling. Note that the CSG node that was used for baking is **not** hidden +automatically, so remember to hide it to prevent its geometry from overlapping with the newly created +MeshInstance3D. + +You can also create a trimesh collision shape using **CSG > Bake Collision Shape**. +The generated [class_CollisionShape3D](class_CollisionShape3D) node must be a child of a [class_StaticBody3D](class_StaticBody3D) +or [class_AnimatableBody3D](class_AnimatableBody3D) node to be effective. + +:::tip + +Remember to keep the original CSG node in the scene tree, so that you can +perform changes to the geometry later if needed. To make changes to the +geometry, remove the MeshInstance3D node and make the root CSG node visible +again. + +::: + +## Exporting as glTF + +It can be useful to block out a level using CSG, then export it as a 3d model, to +import into 3D modeling software. You can do this by selecting **Scene > Export As... > +glTF 2.0 Scene**. + +![Image](/img/Tutorials/3d/img/export_as_gltf.webp) \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/environment_and_post_processing.md b/Redot-Documentation/docs/26.1/Tutorials/3d/environment_and_post_processing.md new file mode 100644 index 0000000..7a0feff --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/environment_and_post_processing.md @@ -0,0 +1,900 @@ + +# Environment and post-processing + +Redot 4 provides a redesigned Environment resource, as well as a new +post-processing system with many available effects right out of the box. + +:::note + +As of Redot 4, Environment *performance/quality* settings are defined in the +project settings instead of in the Environment resource. This makes global +adjustments easier, as you no longer have to tweak Environment resources +individually to suit various hardware configurations. + +Note that most Environment performance/quality settings are only visible +after enabling the **Advanced** toggle in the Project Settings. + +::: + +## Environment + +The [class_Environment](class_Environment) resource stores all the information required for +controlling the 2D and 3D rendering environment. This includes the sky, ambient +lighting, tone mapping, effects, and adjustments. By itself, it does nothing, +but you can enable it by using it in one of the following locations, in order +of priority: + +### Camera3D node (high priority) + +An Environment can be set to a Camera3D node. It will have priority over any +other setting. + +![Image](/img/Tutorials/3d/img/environment_camera.webp) + +This is mostly useful when you want to override an existing environment, +but in general it's a better idea to use the option below. + +### WorldEnvironment node (medium priority, recommended) + +The WorldEnvironment node can be added to any scene, but only one can exist per +active scene tree. Adding more than one will result in a warning. + +![Image](/img/Tutorials/3d/img/environment_world.webp) + +Any Environment added has higher priority than the default Environment +(explained below). This means it can be overridden on a per-scene basis, +which makes it quite useful. + +### Preview environment and sun (low priority) + +:::note + +Since Redot 4, the preview environment and sun system replace the +``default_env.tres`` file that was used in Redot 3 projects. + +::: + +If no WorldEnvironment node or DirectionalLight3D node is present in the current +scene, the editor will display a preview environment and sun instead. This can +be disabled using the buttons at the top of the 3D editor: + +![Image](/img/Tutorials/3d/img/environment_preview_sun_sky_toggle.webp) + +Clicking on the 3 vertical dots on the right will display a dialog which allows +you to customize the appearance of the preview environment: + +![Image](/img/Tutorials/3d/img/environment_preview_sun_sky_dialog.webp) + +**The preview sun and sky is only visible in the editor, not in the running +project.** Using the buttons at the bottom of the dialog, you can add the +preview sun and sky into the scene as nodes. + +:::tip + +If you hold `Shift` while clicking **Add Sun to Scene** or **Add +Environment to Scene** in the preview environment editor, this will add both +a preview sun and environment to the current scene (as if you clicked both +buttons separately). Use this to speed up project setup and prototyping. + +::: + +## Camera attributes + +:::note + +In Redot 4, exposure and depth of field information was split from the +Environment resource into a separate CameraAttributes resource. This allows +adjusting those properties independently of other Environment settings more +easily. + +::: + +The [class_CameraAttributes](class_CameraAttributes) resource stores exposure and depth of field +information. It also allows enabling automatic exposure adjustments depending on +scene brightness. + +There are two kinds of CameraAttribute resources available: + +- **CameraAttributesPractical:** Features are exposed using arbitrary units, + which are easier to reason about for most game use cases. +- **CameraAttributesPhysical:** Features are exposed using real world units, + similar to a digital camera. For example, field of view is set using a focal + length in millimeters instead of a value in degrees. Recommended when physical + accuracy is important, such as for photorealistic rendering. + +Both CameraAttribute resource types allow you to use the same features, but they +are configured differently. If you don't know which one to choose, use +**CameraAttributesPractical**. + +:::note + +Using a [class_CameraAttributesPhysical](class_CameraAttributesPhysical) on a Camera3D node will lock +out FOV and aspect adjustments in that Camera3D, as field of view is +adjusted in the CameraAttributesPhysical resource instead. If used in a +WorldEnvironment, the CameraAttributesPhysical will not override any +Camera3D in the scene. + +::: + +A CameraAttributes resource can be added to a Camera3D or a WorldEnvironment +node. When the current camera has a CameraAttributes set, it will *override* the +one set in WorldEnvironment (if any). + +In most situations, setting the CameraAttributes resource on the Camera3D node +instead of the WorldEnvironment is recommended. Unlike WorldEnvironment, +assigning the CameraAttributes resource to the Camera3D node prevents depth of +field from displaying in the 3D editor viewport, unless the camera is being +previewed. + +## Environment options + +The following is a detailed description of all environment options and how +they are intended to be used. + +### Background + +The Background section contains settings on how to fill the background (parts of +the screen where objects were not drawn). The background not only serves the +purpose of displaying an image or color. By default, it also affects how objects +are affected by ambient and reflected light. This is called image-based lighting +(IBL). + +As a result, the background sky may greatly impact your scene's overall +appearance, even if the sky is never directly visible on screen. This should be +taken into account when tweaking lighting in your scene. + +![Image](/img/Tutorials/3d/img/environment_background1.webp) + +There are several background modes available: + +- **Clear Color** uses the default clear color defined in the project settings. + The background will be a constant color. +- **Custom Color** is like Clear Color, but with a custom color value. +- **Sky** lets you define a background sky material (see below). By default, + objects in the scene will reflect this sky material and absorb ambient light + from it. +- **Canvas** displays the 2D scene as a background to the 3D scene. This can be used + to make environment effects visible on 2D rendering, such as + [glow in 2D](doc_environment_and_post_processing_using_glow_in_2d). +- **Keep** does not draw any sky, keeping what was present on previous frames + instead. This improves performance in purely indoor scenes, but creates a + "hall of mirrors" visual glitch if the sky is visible at any time. + +### Sky materials + +When using the **Sky** background mode (or the ambient/reflected light mode is +set to **Sky**), a Sky subresource becomes available to edit in the Environment +resource. Editing this subresource allows you to create a SkyMaterial resource +within the Sky. + +There are 3 built-in sky materials to choose from: + +- **PanoramaSkyMaterial:** Use a 360 degree panorama sky image (2:1 aspect ratio + recommended). To benefit from high dynamic range, the panorama image must be + in an HDR-compatible format such as ``.hdr`` or ``.exr`` rather than a + standard dynamic range format like ``.png`` or ``.jpg``. +- **ProceduralSkyMaterial:** Use a procedurally generated sky with adjustable + ground, sun, sky and horizon colors. This is the type of sky used in the + editor preview. The sun's position is automatically derived from the first 4 + DirectionalLight3D nodes present in the scene. There can be up to 4 suns at a + given time. +- **PhysicalSkyMaterial:** Use a physically-based procedural sky with adjustable + scattering parameters. The sun's position is automatically derived from the + first DirectionalLight3D node present in the scene. PhysicalSkyMaterial is + slightly more expensive to render compared to ProceduralSkyMaterial. There can + be up to 1 sun at a given time. + +Panorama sky images are sometimes called HDRIs (High Dynamic Range Images). +You can find freely licensed HDRIs on [Poly Haven](https://polyhaven.com/hdris). + +:::note + +HDR PanoramaSkyMaterial textures with very bright spots (such as real life +photos with the sun visible) may result in visible sparkles on ambient and +specular reflections. This is caused by the texture's peak exposure being +too high. + +To resolve this, select the panorama texture in the FileSystem dock, go to +the Import dock, enable **HDR Clamp Exposure** then click **Reimport**. + +::: + +If you need a custom sky material (e.g. for procedural clouds), you can +create a custom [sky shader](../shaders/shader_reference/sky_shader.md). + +### Ambient light + +Ambient light (as defined here) is a type of light that affects every piece of +geometry with the same intensity. It is global and independent of lights that +might be added to the scene. Ambient light is one of the two components of +image-based lighting. Unlike reflected light, ambient light does not vary +depending on the camera's position and viewing angle. + +There are several types of ambient light to choose from: + +- **Background:** Source ambient light from the background, such as the sky, + custom color or clear color (default). Ambient light intensity will vary + depending on the sky image's contents, which can result in more visually + appealing ambient lighting. A sky must be set as background for this mode to + be visible. +- **Disabled:** Do not use any ambient light. Useful for purely indoor scenes. +- **Color:** Use a constant color for ambient light, ignoring the background + sky. Ambient light intensity will be the same on all sides, which may result + in the scene's lighting looking more flat. Useful for indoor scenes where + pitch black shadows may be too dark, or to maximize performance on low-end + devices. +- **Sky:** Source ambient light from a specified sky, even if the background is + set to a mode other than **Sky**. If the background mode is already **Sky**, + this mode behaves identically to **Background**. + +![Image](/img/Tutorials/3d/img/environment_ambient.webp) + +When the ambient light mode is set to Sky or Background (and background is set +to Sky), it's possible to blend between the ambient color and sky using the +**Sky Contribution** property. This value is set to ``1.0`` by default, which +means that only the ambient sky is used. The ambient color is ignored unless +**Sky Contribution** is decreased below ``1.0``. + +Here is a comparison of how different ambient light affects a scene: + +![Image](/img/Tutorials/3d/img/environment_ambient2.webp) + +Finally, there is an **Energy** setting which is a multiplier. It's useful when +working with HDR. + +In general, you should only rely on ambient light alone for simple scenes or +large exteriors. You may also do so to boost performance. Ambient light is fast +to render, but it doesn't provide the best lighting quality. It's better to +generate ambient light from [ReflectionProbe](global_illumination/reflection_probes.md), +[VoxelGI](global_illumination/using_voxel_gi.md) or [SDFGI](global_illumination/using_sdfgi.md), as these +will simulate how indirect light propagates more accurately. Below is a comparison, +in terms of quality, between using a flat ambient color and a VoxelGI: + +![Image](/img/Tutorials/3d/img/environment_ambient_comparison.webp) + +Using one of the methods described above will replace constant ambient +lighting with ambient lighting from the probes. + +### Reflected light + +Reflected light (also called specular light) is the other of the two components +of image-based lighting. + +Reflected light can be set to one of 3 modes: + +- **Background:** Reflect from the background, such as the sky, custom color or + clear color (default). +- **Disabled:** Do not reflect any light from the environment. Useful for purely + indoor scenes, or to maximize performance on low-end devices. +- **Sky:** Reflect from the background sky, even if the background is set to a + mode other than **Sky**. If the background mode is already **Sky**, this mode + behaves identically to **Background**. + +### Fog + +:::note + +This section refers to non-volumetric fog only. +It is possible to use both non-volumetric fog and [doc_volumetric_fog](volumetric_fog.md) +at the same time. + +::: + +Fog, as in real life, makes distant objects fade away into a uniform color. +There are two kinds of fog in Redot: + +- **Depth Fog:** This one is applied based on the distance from the camera. +- **Height Fog:** This one is applied to any objects below (or above) a certain + height, regardless of the distance from the camera. + +![Image](/img/Tutorials/3d/img/environment_fog_depth_height.webp) + +Both of these fog types can have their curve tweaked, making their transition more or less sharp. + +Two properties can be tweaked to make the fog effect more interesting: + +The first is **Sun Amount**, which makes use of the Sun Color property of the fog. +When looking towards a directional light (usually a sun), the color of the fog +will be changed, simulating the sunlight passing through the fog. + +The second is **Transmit Enabled** which simulates more realistic light transmittance. +In practice, it makes light stand out more across the fog. + +![Image](/img/Tutorials/3d/img/environment_fog_transmission.webp) + +:::note + +Fog can cause banding to appear on the viewport, especially at +higher density levels. See [doc_3d_rendering_limitations_color_banding](doc_3d_rendering_limitations_color_banding) +for guidance on reducing banding. + +::: + +### Volumetric Fog + +Volumetric fog provides a realistic fog effect to the scene, with fog color +being affected by the lights that traverse the fog. + +:::info + +See [doc_volumetric_fog](volumetric_fog.md) for documentation on setting up volumetric fog. + +::: + +### Tonemap + +Tonemap selects the tonemapping curve that will be applied to the scene, from a +list of standard curves used in the film and game industries. Tonemapping operators +other than Linear are used to make light and dark areas more homogeneous, +while also avoiding clipping of bright highlights. + +The tone mapping options are: + +- **Mode:** The tone mapping mode to use. + + - **Linear:** The default tonemapping mode. This is the fastest and simplest + tonemapping operator, but it causes bright lighting to look blown out, with + noticeable clipping in the output colors. + - **Reinhardt:** Performs a variation on rendered pixels' colors by this + formula: ``color = color / (1 + color)``. This avoids clipping bright + highlights, but the resulting image can look a bit dull. + - **Filmic:** This avoids clipping bright highlights, with a resulting image + that usually looks more vivid than Reinhardt. + - **ACES:** Academy Color Encoding System tonemapper. + ACES is slightly more expensive than other options, but it handles + bright lighting in a more realistic fashion by desaturating it as it becomes brighter. + ACES typically has a more contrasted output compared to Reinhardt and Filmic. + ACES is the recommended option when aiming for photorealistic visuals. + This tonemapping mode was called "ACES Fitted" in Redot 3.x. + +- **Exposure:** Tone mapping exposure which simulates amount of light received + over time (default: ``1.0``). Higher values result in an overall brighter appearance. + If the scene appears too dark as a result of a tonemapping operator or whitepoint + change, try increasing this value slightly. + +- **White:** Tone mapping whitepoint, which simulates where in the scale white is + located (default: ``1.0``). For photorealistic lighting, recommended values are + between ``6.0`` and ``8.0``. Higher values result in less blown out highlights, + but make the scene appear slightly darker as a whole. + +## Mid- and post-processing effects + +The Environment resource supports many popular mid- and post-processing effects. + +:::note + +Screen-space effects such as :abbr:`SSR (Screen-Space Reflections)`, +:abbr:`SSAO (Screen-Space Ambient Occlusion)`, +:abbr:`SSIL (Screen-Space Indirect Lighting)` and glow do not operate on +geometry that is located outside the camera view or is occluded by other +opaque geometry. Consider this when tweaking their settings to avoid +distracting changes during gameplay. + +::: + +### Screen-Space Reflections (SSR) + +*This feature is only available when using the Forward+ renderer, not +Mobile or Compatibility.* + +While Redot supports several sources of reflection data such as +[doc_reflection_probes](global_illumination/reflection_probes.md), they may not provide enough detail for all +situations. Scenarios where screen-space reflections make the most sense are +when objects are in contact with each other (object over floor, over a table, +floating on water, etc). + +![Image](/img/Tutorials/3d/img/environment_ssr.webp) + +On top of providing more detail, screen-space reflections also work in real-time +(while other types of reflections are usually precomputed). This can be used to +make characters, cars, etc. reflect on surrounding surfaces when moving around. + +Screen-space reflections can be used at the same time as other reflection +sources to benefit from detailed reflections when possible, while having a +fallback when screen-space reflections cannot be used (for example, to reflect +off-screen objects). + +A few user-controlled parameters are available to better tweak the technique: + +- **Max Steps:** Determines the length of the reflection. The bigger this + number, the more costly it is to compute. +- **Fade In:** Allows adjusting the fade-in curve, which is useful to make the + contact area softer. +- **Fade Out:** Allows adjusting the fade-out curve, so the step limit fades out + softly. +- **Depth Tolerance:** Can be used to allow screen-space rays to pass behind + objects. The rays will treat each object as if it has this depth in + determining if it can pass behind the object. Higher values will make + screen-space reflections exhibit fewer "breakups", at the cost of some objects + creating physically incorrect reflections. + +Keep in mind that screen-space-reflections only work for reflecting opaque +geometry. Transparent materials won't be reflected, as they don't write to the depth buffer. +This also applies to shaders that use ``hint_screen_texture`` or ``hint_depth_texture`` +uniforms. + +### Screen-Space Ambient Occlusion (SSAO) + +*This feature is only available when using the Forward+ renderer, not +Mobile or Compatibility.* + +As mentioned in the **Ambient** section, areas where light from light nodes +does not reach (either because it's outside the radius or shadowed) are lit +with ambient light. Redot can simulate this using VoxelGI, ReflectionProbe, +the Sky, or a constant ambient color. The problem, however, is that all the +methods proposed previously act more on a larger scale (large regions) than at the +smaller geometry level. + +Constant ambient color and Sky are the same everywhere, while GI and +Reflection probes have more local detail, but not enough to simulate situations +where light is not able to fill inside hollow or concave features. + +This can be simulated with Screen Space Ambient Occlusion. As you can see in the +image below, its purpose is to make sure concave areas are darker, simulating +a narrower path for the light to enter: + +![Image](/img/Tutorials/3d/img/environment_ssao.webp) + +It is a common mistake to enable this effect, turn on a light, and not be able to +appreciate it. This is because :abbr:`SSAO (Screen-Space Ambient Occlusion)` +only acts on *ambient* light. It does not affect direct light. + +This is why, in the image above, the effect is less noticeable under the direct +light (on the left). If you want to force +:abbr:`SSAO (Screen-Space Ambient Occlusion)` to work with direct light too, +use the **Light Affect** parameter. Even though this is not physically correct, +some artists like how it looks. + +:abbr:`SSAO (Screen-Space Ambient Occlusion)` looks best when combined with a +real source of indirect light, like VoxelGI: + +![Image](/img/Tutorials/3d/img/environment_ssao2.webp) + +Tweaking :abbr:`SSAO (Screen-Space Ambient Occlusion)` is possible with several +parameters: + +![Image](/img/Tutorials/3d/img/environment_ssao_parameters.webp) + +- **Radius:** The distance at which objects can occlude each other when + calculating screen-space ambient occlusion. Higher values will result in + occlusion over a greater distance at the cost of performance and quality. +- **Intensity:** The primary screen-space ambient occlusion intensity. Acts as a + multiplier for the screen-space ambient occlusion effect. A higher value + results in darker occlusion. + Since :abbr:`SSAO (Screen-Space Ambient Occlusion)` is a screen-space effect, + it's recommended to remain conservative with this value. + :abbr:`SSAO (Screen-Space Ambient Occlusion)` that is too strong can be + distracting during gameplay. +- **Power:** The distribution of occlusion. A higher value results in darker + occlusion, similar to **Intensity**, but with a sharper falloff. +- **Detail:** Sets the strength of the additional level of detail for the + screen-space ambient occlusion effect. A high value makes the detail pass more + prominent, but it may contribute to aliasing in your final image. +- **Horizon:** The threshold for considering whether a given point on a surface + is occluded or not represented as an angle from the horizon mapped into the + 0.0-1.0 range. A value of 1.0 results in no occlusion. +- **Sharpness:** The amount that the screen-space ambient occlusion effect is + allowed to blur over the edges of objects. Setting too high will result in + aliasing around the edges of objects. Setting too low will make object edges + appear blurry. +- **Light Affect:** The screen-space ambient occlusion intensity in direct + light. In real life, ambient occlusion only applies to indirect light, which + means its effects can't be seen in direct light. Values higher than 0 will + make the :abbr:`SSAO (Screen-Space Ambient Occlusion)` effect visible in + direct light. Values above ``0.0`` are not physically accurate, but some + artists prefer this effect. + +### Screen-Space Indirect Lighting (SSIL) + +*This feature is only available when using the Forward+ renderer, not +Mobile or Compatibility.* + +:abbr:`SSIL (Screen-Space Indirect Lighting)` provides indirect lighting for +small details or dynamic geometry that other global illumination techniques +cannot cover. This applies to bounced diffuse lighting, but also emissive +materials. When :abbr:`SSIL (Screen-Space Indirect Lighting)` is enabled on its +own, the effect may not be that noticeable, which is intended. + +Instead, :abbr:`SSIL (Screen-Space Indirect Lighting)` is meant to be used as a +*complement* to other global illumination techniques such as VoxelGI, SDFGI and +LightmapGI. :abbr:`SSIL (Screen-Space Indirect Lighting)` also provides +a subtle ambient occlusion effect, similar to SSAO, but with less detail. + +This feature only provides indirect lighting. It is not a full global illumination +solution. This makes it different from screen-space global illumination (SSGI) +offered by other 3D engines. :abbr:`SSIL (Screen-Space Indirect Lighting)` +can be combined with :abbr:`SSR (Screen-Space Reflections)` and/or +:abbr:`SSAO (Screen-Space Ambient Occlusion)` for greater visual quality +(at the cost of performance). + +Tweaking :abbr:`SSIL (Screen-Space Indirect Lighting)` is possible with several parameters: + +- **Radius:** The distance that bounced lighting can travel when using the + screen space indirect lighting effect. A larger value will result in light + bouncing further in a scene, but may result in under-sampling artifacts which + look like long spikes surrounding light sources. +- **Intensity:** The brightness multiplier for the screen-space indirect + lighting effect. A higher value will result in brighter light. +- **Sharpness:** The amount that the screen-space indirect lighting effect is + allowed to blur over the edges of objects. Setting too high will result in + aliasing around the edges of objects. Setting too low will make object edges + appear blurry. +- **Normal Rejection:** Amount of normal rejection used when calculating + screen-space indirect lighting. Normal rejection uses the normal of a given + sample point to reject samples that are facing away from the current pixel. + Normal rejection is necessary to avoid light leaking when only one side of an + object is illuminated. However, normal rejection can be disabled if light + leaking is desirable, such as when the scene mostly contains emissive objects + that emit light from faces that cannot be seen from the camera. + +![Image](/img/Tutorials/3d/img/environment_ssil.webp) + +### Signed Distance Field Global Illumination (SDFGI) + +*This feature is only available when using the Forward+ renderer, not +Mobile or Compatibility.* + +Signed distance field global illumination (SDFGI) is a form of real-time global +illumination. It is not a screen-space effect, which means it can provide global +illumination for off-screen elements (unlike :abbr:`SSIL (Screen-Space Indirect Lighting)`). + +:::info + +See [doc_using_sdfgi](global_illumination/using_sdfgi.md) for instructions on setting up this global +illumination technique. + +::: + +![Image](/img/Tutorials/3d/img/environment_sdfgi.webp) + +### Glow + +:::note + +When using the Compatibility rendering method, glow uses a different +implementation with some properties being unavailable and hidden from the +inspector: **Levels**, **Normalized**, **Strength**, **Blend Mode**, +**Mix**, **Map**, and **Map Strength**. + +This implementation is optimized to run on low-end devices and is less +flexible as a result. + +::: + +In photography and film, when light amount exceeds the maximum *luminance* +(brightness) supported by the media, it generally bleeds outwards to darker +regions of the image. This is simulated in Redot with the **Glow** effect. + +![Image](/img/Tutorials/3d/img/environment_glow1.webp) + +By default, even if the effect is enabled, it will be weak or invisible. One of +two conditions need to happen for it to actually show: + +- 1) The light in a pixel surpasses the **HDR Threshold** (where 0 is all light + surpasses it, and 1.0 is light over the tonemapper **White** value). + Normally, this value is expected to be at 1.0, but it can be lowered to + allow more light to bleed. There is also an extra parameter, **HDR Scale**, + that allows scaling (making brighter or darker) the light surpassing the + threshold. + +![Image](/img/Tutorials/3d/img/environment_glow_threshold.webp) + +- 2) The **Bloom** property has a value greater than ``0.0``. As it increases, + it sends the whole screen to the glow processor at higher amounts. + +![Image](/img/Tutorials/3d/img/environment_glow_bloom.webp) + +Both will cause the light to start bleeding out of the brighter areas. + +Once glow is visible, it can be controlled with a few extra parameters: + +- **Intensity** is an overall scale for the effect, it can be made stronger or + weaker (``0.0`` removes it). +- **Strength** is how strong the gaussian filter kernel is processed. Greater + values make the filter saturate and expand outwards. In general, changing this + is not needed, as the size can be adjusted more efficiently with the **Levels**. + +The **Blend Mode** of the effect can also be changed: + +- **Additive** is the strongest one, as it only adds the glow effect over the + image with no blending involved. In general, it's too strong to be used, but + can look good with low-intensity **Bloom** (produces a dream-like effect). +- **Screen** ensures glow never brightens more than itself and it works great as + an all around. +- **Softlight** is the default and weakest one, producing only a subtle color + disturbance around the objects. This mode works best on dark scenes. +- **Replace** can be used to + [blur the whole screen](doc_environment_and_post_processing_using_glow_to_blur_the_screen) + or debug the effect. It only shows the glow effect without the image below. +- **Mix** mixes the glow effect with the main image. This can be used for + greater artistic control. The mix factor is controlled by the **Mix** property + which appears above the blend mode (only when the blend mode is set to Mix). + High mix factor values will appear to darken the image unless **Bloom** is + increased. + +To change the glow effect size and shape, Redot provides **Levels**. Smaller +levels are strong glows that appear around objects, while large levels are hazy +glows covering the whole screen: + +![Image](/img/Tutorials/3d/img/environment_glow_layers.webp) + +The real strength of this system, though, is to combine levels to create more +interesting glow patterns: + +![Image](/img/Tutorials/3d/img/environment_glow_layers2.webp) + +Finally, the glow effect can be controlled using a *glow map*, which is a +texture that determines how bright glow should be on each part of the screen. +This texture can optionally be colored to tint the glow effect to the glow map's +color. The texture is stretched to fit the viewport, so using an aspect ratio +that matches your viewport's most common aspect ratio (such as 16:9) is recommended +to avoid visible distortion. + +There are 2 main use cases for a glow map texture: + +- Create a "lens dirt" effect using a dirt pattern texture. +- Make glow less strong on specific parts of the screen by using a gradient texture. + +![Image](/img/Tutorials/3d/img/environment_glow_map.webp) + +### Using glow in 2D + +There are 2 ways to use glow in 2D: + +- Since Redot 4.2, you can enable HDR for 2D rendering when using the Forward+ + and Mobile rendering methods. This has a performance cost, but it allows for a + greater dynamic range. This also allows you to control which objects glow + using their individual **Modulate** or **Self Modulate** properties (use the + RAW mode in the color picker). Enabling HDR can also reduce banding in the 2D + rendering output. + + - To enable HDR in 2D, open the Project Settings, enable + [Rendering > Viewport > HDR 2D](class_ProjectSettings_property_rendering/viewport/hdr_2d) + then restart the editor. + +- If you want to maximize performance, you can leave HDR disabled for 2D + rendering. However, you will have less control on which objects glow. + + - Enable glow, set the environment background mode to **Canvas** then decrease + **Glow HDR Threshold** so that pixels that are not overbright will still + glow. To prevent UI elements from glowing, make them children of a + [class_CanvasLayer](class_CanvasLayer) node. You can control which layers are affected by + glow using the **Background > Canvas Max Layer** property of the Environment + resource. + +
+ Example of using glow in a 2D scene +
+ Example of using glow in a 2D scene. HDR 2D is enabled, while coins and the +bullet have their **Modulate** property increased to overbright values using the +RAW mode in the color picker. +
+
+ +:::warning + +The 2D renderer renders in linear color space if the +[Rendering > Viewport > HDR 2D](class_ProjectSettings_property_rendering/viewport/hdr_2d) +project setting is enabled, so the ``source_color`` hint must also be used +for uniform samplers that are used as color input in ``canvas_item`` shaders. +If this is not done, the texture will appear washed out. + +If 2D HDR is disabled, ``source_color`` will keep working correctly in +``canvas_item`` shaders, so it's recommend to use it when relevant either +way. + +::: + +### Using glow to blur the screen + +Glow can be used to blur the whole viewport, which is useful for background blur +when a menu is open. Only 3D rendering will be affected unless the environment's +background mode is set to **Canvas**. To prevent UI elements from being blurred +when using the Canvas background mode, make them children of a [class_CanvasLayer](class_CanvasLayer) +node. You can control which layers are affected by this blurring effect using the +**Background > Canvas Max Layer** property of the Environment resource. + +To use glow as a blurring solution: + +- Enable **Normalized** and adjust levels according to preference. Increasing + higher level indices will result in a more blurred image. It's recommended to + leave a single glow level at ``1.0`` and leave all other glow levels at + ``0.0``, but this is not required. Note that the final appearance will vary + depending on viewport resolution. +- Set **Intensity** to ``1.0`` and **Bloom** to ``1.0``. +- Set the blend mode to **Replace** and **HDR Luminance Cap** to ``1.0``. + +
+ Example of using glow to blur the 2D rendering in the menu's background +
+ Example of using glow to blur the 2D rendering in the menu's background +
+
+ +### Adjustments + +At the end of processing, Redot offers the possibility to do some standard +image adjustments. + +![Image](/img/Tutorials/3d/img/environment_adjustments.webp) + +**Basic BCS adjustments** + +The first adjustment is being able to change the typical **Brightness**, **Contrast**, +and **Saturation** properties: + +![Image](/img/Tutorials/3d/img/environment_adjustments_bcs.webp) + +**Color correction using a 1D gradient** + +The second adjustment is by supplying a color correction gradient. This can be +done by assigning a GradientTexture1D resource to the **Color Correction** +property, or by loading a texture containing a horizontal gradient. The leftmost +part of the gradient represents black in the source image, whereas the rightmost +part of the gradient represents white in the source image. + +A linear black-to-white gradient like the following one will produce no effect: + +![Image](/img/Tutorials/3d/img/environment_adjustments_default_gradient.webp) + +But creating custom ones will allow to map each channel to a different color: + +![Image](/img/Tutorials/3d/img/environment_adjustments_custom_gradient.webp) + +**Color correction using a 3D LUT** + +A 3D look-up-texture (LUT) can also be used for color correction. This is a +special texture used to modify each color channel separately from one another +(red, green, blue). This image can be of any resolution, but since color +correction is low-frequency data, sticking to low resolutions is recommended for +performance reasons. A LUT texture's resolution is typically 17×17×17, 33×33×33, +51×51×51 or 65×65×65 (the odd size allows for better interpolation). + +For this to work, the look-up texture's import mode must be set to Texture3D +in the Import dock (instead of being imported as a regular Texture2D): + +![Image](/img/Tutorials/3d/img/environment_adjustments_3d_lut_import.webp) + +Make sure to configure the number of horizontal and vertical slices to import as +well. If you don't do this, the LUT texture will not affect the viewport +correctly when used. You can preview how the 3D texture was imported by +double-clicking it, in the FileSystem dock, then going to the inspector to flip +through the texture's layers. + +You can use this neutral 33×33×33 LUT template as a base (right-click and choose +**Save as…**): + +![Image](/img/Tutorials/3d/img/environment_adjustments_3d_lut_template.webp) + +With the above LUT template, after changing its import mode to **Texture3D**, +set its number of **Horizontal** slices to ``33`` in the Import dock then click +**Reimport**. If you load this LUT into the **Color Correction** property, you +won't see any visible difference for now since this texture is designed to be a +neutral starting point. + +This LUT template can be modified in an image editor to provide a different +mood to the image. A common workflow is to place the LUT image next to a +screenshot of the project's 3D viewport, then use an image editor to modify both +the LUT image and the screenshot at the same time. The LUT can then be saved and +applied to the game engine to perform the same color correction in real-time. + +For example, modifying the LUT template in an image editor to give it a +"sepia" look results in the image on the right: + +![Image](/img/Tutorials/3d/img/environment_adjustments_3d_lut_comparison.webp) + +:::note + +Adjustments and color correction are applied *after* tonemapping. +This means the tonemapping properties defined above still have an effect +when adjustments are enabled. + +::: + +## Camera attribute options + +### Depth of Field / Far Blur + +This effect simulates focal distance on cameras. It blurs objects behind +a given range. It has an initial **Distance** with a **Transition** region +(in world units): + +![Image](/img/Tutorials/3d/img/environment_dof_far.webp) + +The **Amount** parameter controls the amount of blur. For larger blurs, tweaking +the depth of field quality in the advanced project settings may be needed to +avoid artifacts. + +### Depth of Field / Near Blur + +This effect simulates focal distance on cameras. It blurs objects close +to the camera (acts in the opposite direction as far blur). +It has an initial **Distance** with a **Transition** region (in world units): + +![Image](/img/Tutorials/3d/img/environment_dof_near.webp) + +The **Amount** parameter controls the amount of blur. For larger blurs, tweaking +the **Quality** may be needed in order to avoid artifacts. + +It is common to use both blurs together to focus the viewer's attention on a +given object, or create a so-called +["tilt shift" effect](https://en.wikipedia.org/wiki/Miniature_faking). + +![Image](/img/Tutorials/3d/img/environment_mixed_blur.webp) + +:::note + +When using CameraAttributesPhysical instead of CameraAttributesPractical, +depth of field is automatically computed from the camera attributes' focus +distance, focal length, and aperture. + +::: + +### Exposure + +This multiplies the overall scene brightness visible from the camera. Higher +values result in a visually brighter scene. + +### Auto Exposure + +*This feature is only available when using the Forward+ renderer, not +Mobile or Compatibility.* + +Even though, in most cases, lighting and texturing are heavily artist controlled, +Redot supports a basic high dynamic range implementation with the auto exposure +mechanism. This is generally used to add realism when combining interior areas +with low light and bright outdoor areas. Auto exposure simulates the camera +(or eye) in an effort to adapt between light and dark locations and their +different amounts of light. + +:::note + +Auto exposure needs to evaluate the scene's brightness every frame, which +has a moderate performance cost. Therefore, it's recommended to leave Auto +Exposure disabled if it doesn't make much of a difference in your scene. + +::: + +![Image](/img/Tutorials/3d/img/environment_hdr_autoexp.webp) + +The simplest way to use auto exposure is to make sure outdoor lights (or other +strong lights) have energy beyond 1.0. This is done by tweaking their **Energy** +multiplier (on the Light itself). To make it consistent, the **Sky** usually +needs to use the energy multiplier too, to match with the directional light. +Normally, values between 3.0 and 6.0 are enough to simulate indoor-outdoor conditions. + +By combining Auto Exposure with [doc_environment_and_post_processing_glow](doc_environment_and_post_processing_glow) +post-processing, pixels that go over the tonemap **White** will bleed to the +glow buffer, creating the typical bloom effect in photography. + +![Image](/img/Tutorials/3d/img/environment_hdr_bloom.webp) + +The user-controllable values in the Auto Exposure section come with sensible +defaults, but you can still tweak them: + +![Image](/img/Tutorials/3d/img/environment_hdr.webp) + +- **Scale:** Value to scale the lighting. Higher values produce brighter + images, and lower values produce darker ones. +- **Min Sensitivity / Min Exposure Value:** Minimum luminance that auto exposure + will aim to adjust for (in ISO when using CameraAttributesPractical, or in + EV100 when using CameraAttributesPhysical). Luminance is the average of the + light in all the pixels of the screen. +- **Max Sensitivity / Max Exposure Value:** Maximum luminance that auto exposure + will aim to adjust for (in ISO when using CameraAttributesPractical, or in + EV100 when using CameraAttributesPhysical). +- **Speed:** Speed at which luminance corrects itself. The higher the value, the + faster luminance correction happens. High values may be more suited to + fast-paced games, but can be distracting in some scenarios. + +When using CameraAttributesPractical, exposure is set using *sensitivity* +defined in ISO instead of an exposure value in EV100. Typical ISO values are +between 50 and 3200, with higher values resulting in higher final exposure. In +real life, daytime photography generally uses ISO values between 100 and 800. + +:::info + +See [doc_physical_light_and_camera_units](physical_light_and_camera_units.md) if you wish to use real world +units to configure your camera's exposure, field of view and depth of field. + +::: diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/faking_global_illumination.md b/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/faking_global_illumination.md new file mode 100644 index 0000000..7143e70 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/faking_global_illumination.md @@ -0,0 +1,99 @@ + +# Faking global illumination + +## Why fake global illumination? + +Redot provides several global illumination (GI) techniques, all with their advantages +and drawbacks. Nonetheless, it remains possible to avoid using any GI technique +and use a handmade approach instead. There are a few reasons for using a +"handmade" approach to global illumination instead of VoxelGI, SDFGI or +baked lightmaps: + +- You need to have good rendering performance, but can't afford going through + a potentially cumbersome lightmap baking process. +- You need an approach to GI that is fully real-time *and* works in procedurally + generated levels. +- You need an approach to GI that is fully real-time *and* does not suffer from + significant light leaks. + +The approaches described below only cover indirect diffuse lighting, not +specular lighting. For specular lighting, consider using ReflectionProbes which +are usually cheap enough to be used in conjunction with this fake GI approach. + +:::info + +Not sure if faking global illumination with lights is suited to your needs? +See [doc_introduction_to_global_illumination_comparison](doc_introduction_to_global_illumination_comparison) for a +comparison of GI techniques available in Redot 4. + +::: + +## Faking DirectionalLight3D global illumination + +While the sky provides its own directional lighting, the scene's main DirectionalLight3D +node typically emits a large amount of light. When using a GI technique, this light +would be reflected on solid surfaces and would bounce back on most outdoors shaded surfaces. + +We can fake this by adding a second DirectionalLight3D node with the following changes: + +- Rotate the light by 180 degrees. This allows it to represent lighting bounced + by the main DirectionalLight3D node. +- Set **Shadows** to **Off**. This reduces the secondary light's performance burden + while also allowing shaded areas to receive *some* lighting (which is what we want here). +- Set **Energy** to 10-40% of the original value. There is no "perfect" value, + so experiment with various energy values depending on the light and your typical + material colors. +- Set **Specular** to ``0.0``. Indirect lighting shouldn't emit visible specular + lobes, so we need to disable specular lighting entirely for the secondary light. + +:::note + +This approach works best in scenes that are mostly outdoors. When going indoors, +the secondary DirectionalLight3D's light will still be visible as this light +has shadows disabled. + +This can be worked around by smoothly decreasing the secondary DirectionalLight3D's +energy when entering an indoor area (and doing the opposite when leaving the indoor area). +For instance, this can be achieved using an Area3D node and AnimationPlayer. + +::: + +## Faking positional light global illumination + +It's possible to follow the same approach as DirectionalLight3D for positional +lights (OmniLight3D and SpotLight3D). However, this will require more manual +work as this operation needs to be repeated for every positional light node in +the scene to look good. + +In an ideal scenario, additional OmniLight3Ds should be added at every location +where a significant amount of light hits a bright enough surface. However, due +to time constraints, this isn't always easily feasible (especially when +performing procedural level generation). + +If you're in a hurry, you can place a secondary OmniLight3D node at the same position +as the main OmniLight3D node. +You can add this node as a child of the main OmniLight3D node to make it easy to +move and hide both nodes at the same time. + +In the secondary OmniLight3D node, perform the following changes: + +- Increase the light's **Range** by 25-50%. This allows the secondary light to lighten + what was previously not lit by the original light. +- Set **Shadows** to **Off**. This reduces the secondary light's performance burden + while also allowing shaded areas to receive *some* lighting (which is what we want here). +- Set **Energy** to 10-40% of the original value. There is no "perfect" value, + so experiment with various energy values depending on the light and its surroundings. +- Set **Specular** to 0. Indirect lighting shouldn't emit visible specular lobes, + so we need to disable specular lighting entirely for the secondary light. + +For SpotLight3D, the same trick can be used. In this case, the secondary OmniLight3D +should be placed in a way that reflects where *most* light will be bounced. +This is usually close to the SpotLight3D's primary impact location. + +In the example below, a SpotLight3D node is used to light up the room's floor. +However, since there is no indirect lighting, the rest of the room remains +entirely dark. In real life, the room's walls and ceiling would be lit up by +light bouncing around. Using an OmniLight3D node positioned between the +SpotLight3D's origin and the floor allows simulating this effect: + +![Image](/img/Tutorials/3d/global_illumination/img/faking_global_illumination_comparison.webp) \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/index.md b/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/index.md new file mode 100644 index 0000000..375c097 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/index.md @@ -0,0 +1,13 @@ +# Global illumination + +This section contains tutorials and documentation about global illumination in Redot Engine. + +## Articles + +- [Faking global illumination](faking_global_illumination) +- [Introduction to global illumination](introduction_to_global_illumination) +- [Reflection probes](reflection_probes) +- [Using Lightmap global illumination](using_lightmap_gi) +- [Signed distance field global illumination (SDFGI)](using_sdfgi) +- [Using Voxel global illumination](using_voxel_gi) + diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/introduction_to_global_illumination.md b/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/introduction_to_global_illumination.md new file mode 100644 index 0000000..2c3d63f --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/introduction_to_global_illumination.md @@ -0,0 +1,373 @@ + +# Introduction to global illumination + +## What is global illumination? + +*Global illumination* is a catch-all term used to describe a system of lighting +that uses both direct light (light that comes directly from a light source) and +indirect light (light that bounces from a surface). In a 3D rendering engine, +global illumination is one of the most important elements to achieving +realistic lighting. Global illumination aims to mimic how light behaves +in real life, such as light bouncing on surfaces and light being emitted +from emissive materials. + +In the example below, the entire scene is illuminated by an emissive material +(the white square at the top). The white wall and ceiling on the back is tinted +red and green close to the walls, as the light bouncing on the colored walls is +being reflected back onto the rest of the scene. + +![Image](/img/Tutorials/3d/global_illumination/img/global_illumination_example.webp) + +Global illumination is composed of several key concepts: + +### Indirect diffuse lighting + +This is the lighting that does not change depending on the camera's angle. +There are two main sources of indirect diffuse lighting: + +- Light *bouncing* on surfaces. This bounced lighting is multiplied with the + material's albedo color. The bounced lighting can then be reflected by other + surfaces, with decreasing impact due to light attenuation. In real life, + light bounces an infinite number of times. However, for performance + reasons, this can't be simulated in a game engine. Instead, the number of + bounces is typically limited to 1 or 2 (or up to 16 when baking lightmaps). A + greater number of bounces will lead to more realistic light falloff in shaded + areas, at the cost of lower performance or greater bake times. +- Emissive materials can also emit light that can be bounced on surfaces. + This acts as a form of *area lighting*. Instead of having an infinitely + small point emit light using an OmniLight3D or SpotLight3D node, + an area of a determined size will emit light using its own surface. + +Direct diffuse lighting is already handled by the light nodes themselves, which +means that global illumination algorithms only try to represent indirect +lighting. + +Different global illumination techniques offer varying levels of accuracy +to represent indirect diffuse lighting. See the comparison table at the bottom +of this page for more information. + +To provide more accurate ambient occlusion for small objects, screen-space ambient occlusion +(SSAO) can be enabled in the [environment ](doc_environment_and_post_processing) +settings. SSAO has a significant performance cost, so make sure to disable +it when targeting low-end hardware. + +:::note + +Indirect diffuse lighting may be a source of color banding in scenes with no +detailed textures. This results in light gradients not being smooth, but +having a visible "stepping" effect instead. See the +[doc_3d_rendering_limitations_color_banding](doc_3d_rendering_limitations_color_banding) section in the 3D rendering +limitations documentation for ways to reduce this effect. + +::: + +### Specular lighting + +Specular lighting is also referred to as *reflections*. +This is the lighting that changes in intensity depending on the camera's angle. +This specular lighting can be *direct* or *indirect*. + +Most global illumination techniques offer a way to render specular lighting. +However, the degree of accuracy at which specular lighting is rendered varies +greatly from technique to technique. See the comparison table at the bottom +of this page for more information. + +To provide more accurate reflections for small objects, screen-space reflections (SSR) +can be enabled in the [environment ](doc_environment_and_post_processing) settings. +SSR has a significant performance cost (even more so than SSAO), so make sure to disable +it when targeting low-end hardware. + +## Which global illumination technique should I use? + +When determining a global illumination (GI) technique to use, +there are several criteria to keep in mind: + +- **Performance.** Real-time GI techniques are usually more expensive + compared to semi-real-time or baked techniques. Note that most of the cost in + GI rendering is spent on the GPU, rather than the CPU. +- **Visuals.** On top of not performing the best, real-time GI techniques + generally don't provide the best visual output. This is especially the case in + a mostly static scene where the dynamic nature of real-time GI is not easily + noticeable. If maximizing visual quality is your goal, baked techniques will + often look better and will result in fewer light leaks. +- **Real-time ability.** Some GI techniques are fully real-time, + whereas others are only semi-real-time or aren't real-time at all. + Semi-real-time techniques have restrictions that fully real-time techniques don't. + For instance, dynamic objects may not contribute emissive lighting to the scene. + Non-real-time techniques do not support *any* form of dynamic GI, + so it must be faked using other techniques if needed (such as placing positional lights + near emissive surfaces). + Real-time ability also affects the GI technique's viability in procedurally + generated levels. +- **User work needed.** Some GI techniques are fully automatic, whereas others + require careful planning and manual work on the user's side. Depending on your + time budget, some GI techniques may be preferable to others. + +Here's a comparison of all the global illumination techniques available in Redot: + +### Performance + +In order of performance from fastest to slowest: + +- **ReflectionProbe:** + + - ReflectionProbes with their update mode set to **Always** are much more + expensive than probes with their update mode set to **Once** (the default). + Suited for integrated graphics when using the **Once** update mode. + *Available in all renderers.* + +- **LightmapGI:** + + - Lights can be baked with indirect lighting only, or fully baked on a + per-light basis to further improve performance. Hybrid setups can be used + (such as having a real-time directional light and fully baked positional lights). + Directional information can be enabled before baking to improve visuals at + a small performance cost (and at the cost of larger file sizes). + Suited for integrated graphics. + *Available in all renderers. However, baking lightmaps requires hardware + with RenderingDevice support.* + +- **VoxelGI:** + + - The bake's number of subdivisions can be adjusted to balance between performance and quality. + The VoxelGI rendering quality can be adjusted in the Project Settings. + The rendering can optionally be performed at half resolution + (and then linearly scaled) to improve performance significantly. + **Not available** *when using the Mobile or Compatibility renderers.* + +- **Screen-space indirect lighting (SSIL):** + + - The SSIL quality and number of blur passes can be adjusted in the Project Settings. + By default, SSIL rendering is performed at half resolution (and then linearly scaled) + to ensure a reasonable performance level. + **Not available** *when using the Mobile or Compatibility renderers.* + +- **SDFGI:** + + - The number of cascades can be adjusted to balance performance and quality. + The number of rays thrown per frame can be adjusted in the Project Settings. + The rendering can optionally be performed at half resolution + (and then linearly scaled) to improve performance significantly. + **Not available** *when using the Mobile or Compatibility renderers.* + +### Visuals + +For comparison, here's a 3D scene with no global illumination options used: + +![Image](/img/Tutorials/3d/global_illumination/img/gi_none.webp) + + A 3D scene without any form of global illumination (only constant environment lighting). The box and sphere near the camera are both dynamic objects. + +Here's how Redot's various global illumination techniques compare: + +- **VoxelGI:** |average| Good reflections and indirect lighting, but beware of leaks. + + - Due to its voxel-based nature, VoxelGI will exhibit light leaks if walls and floors are too thin. + It's recommended to make sure all solid surfaces are at least as thick as one voxel. + + Streaking artifacts may also be visible on sloped surfaces. In this case, + tweaking the bias properties or rotating the VoxelGI node can help combat + this. + + .. figure:: img/gi_voxel_gi.webp + :alt: VoxelGI in action. + + VoxelGI in action. + +- **SDFGI:** |average| Good reflections and indirect lighting, but beware of leaks and visible cascade shifts. + + - GI level of detail varies depending on the distance + between the camera and surface. + + Leaks can be reduced significantly by enabling the **Use Occlusion** + property. This has a small performance cost, but it often results in fewer + leaks compared to VoxelGI. + + Cascade shifts may be visible when the camera moves fast. This can be made + less noticeable by adjusting the cascade sizes or using fog. + + .. figure:: img/gi_sdfgi.webp + :alt: SDFGI in action. + + SDFGI in action. + +- **Screen-space indirect lighting (SSIL):** |average| Good *secondary* source of indirect lighting, but no reflections. + + - SSIL is designed to be used as a complement to another GI technique such as + VoxelGI, SDFGI or LightmapGI. SSIL works best for small-scale details, as it + cannot provide accurate indirect lighting for large structures on its own. + SSIL can provide real-time indirect lighting in situations where other GI + techniques fail to capture small-scale details or dynamic objects. Its + screen-space nature will result in some artifacts, especially when objects + enter and leave the screen. SSIL works using the last frame's color (before + post-processing) which means that emissive decals and custom shaders are + included (as long as they're present on screen). + + .. figure:: img/gi_ssil_only.webp + :alt: SSIL in action (without any other GI technique). Notice the emissive lighting around the yellow box. + + SSIL in action (without any other GI technique). Notice the emissive lighting around the yellow box. + +- **LightmapGI:** |good| Excellent indirect lighting, decent reflections (optional). + + - This is the only technique where the number of light bounces + can be pushed above 2 (up to 16). When directional information + is enabled, spherical harmonics (SH) are used + to provide blurry reflections. + + .. figure:: img/gi_lightmap_gi_indirect_only.webp + :alt: LightmapGI in action. Only indirect lighting is baked here, but direct light can also be baked. + + LightmapGI in action. Only indirect lighting is baked here, but direct light can also be baked. + +- **ReflectionProbe:** |average| Good reflections, but poor indirect lighting. + + - Indirect lighting can be disabled, set to a constant color spread throughout + the probe, or automatically read from the probe's environment (and applied + as a cubemap). This essentially acts as local ambient lighting. Reflections + and indirect lighting are blended with other nearby probes. + + .. figure:: img/gi_none_reflection_probe.webp + :alt: ReflectionProbe in action (without any other GI technique). Notice the reflective sphere. + + ReflectionProbe in action (without any other GI technique). Notice the reflective sphere. + +### Real-time ability + +- **VoxelGI:** |good| Fully real-time. + + - Indirect lighting and reflections are fully real-time. Dynamic objects can + receive GI *and* contribute to it with their emissive surfaces. Custom + shaders can also emit their own light, which will be emitted accurately. + + Viable for procedurally generated levels *if they are generated in advance* + (and not during gameplay). Baking requires several seconds or more to complete, + but it can be done from both the editor and an exported project. + +- **SDFGI:** |average| Semi-real-time. + + - Cascades are generated in real-time, making SDFGI + viable for procedurally generated levels (including when structures are generated + during gameplay). + + Dynamic objects can *receive* GI, but not *contribute* to it. Emissive lighting + will only update when an object enters a cascade, so it may still work for + slow-moving objects. + +- **Screen-space indirect lighting (SSIL):** |good| Fully real-time. + + - SSIL works with both static and dynamic lights. It also works with both + static and dynamic occluders (including emissive materials). + +- **LightmapGI:** |bad| Baked, and therefore not real-time. + + - Both indirect lighting and SH reflections are baked and can't be changed at + runtime. Real-time GI must be + [simulated via other means ](doc_faking_global_illumination), + such as real-time positional lights. Dynamic objects receive indirect lighting + via light probes, which can be placed automatically or manually by the user + (LightmapProbe node). Not viable for procedurally generated levels, + as baking lightmaps is only possible from the editor. + +- **ReflectionProbe:** |average| Optionally real-time. + + - By default, reflections update when the probe is moved. + They update as often as possible if the update mode + is set to **Always** (which is expensive). + + - Indirect lighting must be configured manually by the user, but can be changed + at runtime without causing an expensive computation to happen behind the scenes. + This makes ReflectionProbes viable for procedurally generated levels. + +### User work needed + +- **VoxelGI:** One or more VoxelGI nodes need to be created and baked. + + - Adjusting extents correctly is required to get good results. Additionally + rotating the node and baking again can help combat leaks or streaking + artifacts in certain situations. Bake times are fast – usually below + 10 seconds for a scene of medium complexity. + +- **SDFGI:** Very little. + + - SDFGI is fully automatic; it only needs to be enabled in the Environment resource. + The only manual work required is to set MeshInstances' bake mode property correctly. + No node needs to be created, and no baking is required. + +- **Screen-space indirect lighting (SSIL):** Very little. + + - SSIL is fully automatic; it only needs to be enabled in the Environment resource. + No node needs to be created, and no baking is required. + +- **LightmapGI:** Requires UV2 setup and baking. + + - Static meshes must be reimported with UV2 and lightmap generation enabled. + On a dedicated GPU, bake times are relatively fast thanks to the GPU-based + lightmap baking – usually below 1 minute for a scene of medium complexity. + +- **ReflectionProbe:** Placed manually by the user. + +### Summary + +If you are unsure about which GI technique to use: + +- For desktop games, it's a good idea to start with [SDFGI ](doc_using_sdfgi) + first as it requires the least amount of setup. Move to other GI techniques + later if needed. To improve performance on low-end GPUs and integrated + graphics, consider adding an option to disable SDFGI or [VoxelGI ](doc_using_voxel_gi) in your game's settings. SDFGI can be disabled in the + Environment resource, and VoxelGI can be disabled by hiding the VoxelGI + node(s). To further improve visuals on high-end setups, add an option to + enable SSIL in your game's settings. +- For mobile games, [LightmapGI ](doc_using_lightmap_gi) and + [ReflectionProbes ](doc_reflection_probes) are the only supported options. + See also [doc_introduction_to_global_illumination_alternatives](doc_introduction_to_global_illumination_alternatives). + +:::info + +You can compare global illumination techniques in action using the +[Global Illumination demo project ](https://github.com/redot-engine/redot-demo-projects/tree/master/3d/global_illumination). + +::: + +### Which global illumination mode should I use on meshes and lights? + +Regardless of which global illumination technique you use, there is no +universally "better" global illumination mode. Still, here are some +recommendations for meshes: + +- For static level geometry, use the **Static** global illumination mode *(default)*. +- For small dynamic geometry and players/enemies, use the **Disabled** global + illumination mode. Small dynamic geometry will not be able to contribute a significant + amount of indirect lighting, due to the geometry being smaller than a voxel. + If you need indirect lighting for small dynamic objects, it can be simulated + using an OmniLight3D or SpotLight3D node parented to the object. +- For *large* dynamic level geometry (such as a moving train), use the + **Dynamic** global illumination mode. Note that this only has an effect with + VoxelGI, as SDFGI and LightmapGI do not support global illumination with + dynamic objects. + +Here are some recommendations for light bake modes: + +- For static level lighting, use the **Static** bake mode. + The **Static** mode is also suitable for dynamic lights that don't change + much during gameplay, such as a flickering torch. +- For short-lived dynamic effects (such as a weapon), use the **Disabled** + bake mode to improve performance. +- For long-lived dynamic effects (such as a rotating alarm light), use the + **Dynamic** bake mode to improve quality *(default)*. Note that this only has + an effect with VoxelGI and SDFGI, as LightmapGI does not support global + illumination with dynamic lights. + +## Alternatives to GI techniques + +If none of the GI techniques mentioned above fits, it's still possible to +[simulate GI by placing additional lights manually ](doc_faking_global_illumination). +This requires more manual work, but it can offer good performance *and* good +visuals if done right. This approach is still used in many modern games to this +day. + +When targeting low-end hardware in situations where using LightmapGI is not +viable (such as procedurally generated levels), relying on environment lighting +alone or a constant ambient light factor may be a necessity. This may result in +flatter visuals, but adjusting the ambient light color and sky contribution +still makes it possible to achieve acceptable results in most cases. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/reflection_probes.md b/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/reflection_probes.md new file mode 100644 index 0000000..bd7a2a3 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/reflection_probes.md @@ -0,0 +1,185 @@ + +# Reflection probes + +:::note + +Reflection probes are only supported in the Forward+ and Mobile renderers, +not the Compatibility renderer. + +::: + +As stated in the [doc_standard_material_3d](../standard_material_3d.md), objects can show reflected and/or +diffuse light. Reflection probes are used as a source of reflected *and* ambient +light for objects inside their area of influence. They can be used to provide +more accurate reflections than [VoxelGI ](using_voxel_gi.md) and +[SDFGI ](using_sdfgi.md) while being fairly cheap on system resources. + +Since reflection probes can also store ambient light, they can be used as a +low-end alternative to VoxelGI and SDFGI when [baked lightmaps ](using_lightmap_gi.md) aren't viable (e.g. in procedurally generated levels). + +Reflection probes can also be used at the same time as screen-space reflections +to provide reflections for off-screen objects. In this case, Redot will blend +together the screen-space reflections and reflections from reflection probes. + +:::info + +Not sure if ReflectionProbe is suited to your needs? +See [doc_introduction_to_global_illumination_comparison](doc_introduction_to_global_illumination_comparison) +for a comparison of GI techniques available in Redot 4. + +::: + +## Visual comparison + +![Image](/img/Tutorials/3d/global_illumination/img/gi_none.webp) + + Reflection probe disabled. Environment sky is used as a fallback. + +![Image](/img/Tutorials/3d/global_illumination/img/gi_none_reflection_probe.webp) + + Reflection probe enabled. + +![Image](/img/Tutorials/3d/global_illumination/img/gi_lightmap_gi_indirect_only_reflection_probe.webp) + + Reflection probe enabled with LightmapGI used at the same time. The lightmap appears in the reflection. + +By combining reflection probes with screen-space reflections, you can get the +best of both worlds: high-quality reflections for general room structure (that +remain present when off-screen), while also having real-time reflections for +small details. + +![Image](/img/Tutorials/3d/global_illumination/img/reflection_probes_reflection_probe.webp) + + Reflections in a room using ReflectionProbe only. Notice how small details + don't have any reflections. + +![Image](/img/Tutorials/3d/global_illumination/img/reflection_probes_ssr.webp) + + Reflections in a room using screen-space reflections only. Notice how the + reflection on the sides of the room's walls is partly missing due to being + off-screen. + +![Image](/img/Tutorials/3d/global_illumination/img/reflection_probes_reflection_probe_ssr.webp) + + Reflections in a room using ReflectionProbe and screen-space reflections together. + The screen-space reflections are blended with the reflection probe, + acting as a fallback in situations where the reflection probe fails to display + any reflection. + +## Setting up a ReflectionProbe + +- Add a [class_ReflectionProbe](class_ReflectionProbe) node. +- Configure the ReflectionProbe's extents in the inspector to fit your scene. To + get reasonably accurate reflections, you should generally have one + ReflectionProbe node per room (sometimes more for large rooms). + +:::tip + +Remember that ReflectionProbe extents don't have to be square, and you can +even rotate the ReflectionProbe node to fit rooms that aren't aligned with +the X/Z grid. Use this to your advantage to better cover rooms without +having to place too many ReflectionProbe nodes. + +::: + +## ReflectionProbe properties + +- **Update Mode:** Controls when the reflection probe updates. + **Once** only renders the scene once every time the ReflectionProbe is moved. + This makes it much faster to render compared to the **Always** update mode, + which forces the probe to re-render everything around it every frame. + Leave this property on **Once** (default) unless you need the reflection probe + to update every frame. +- **Intensity:** The brightness of the reflections and ambient lighting. This + usually doesn't need to be changed from its default value of ``1.0``, but you + can decrease it ``1.0`` if you find that reflections look too strong. +- **Max Distance:** Controls the maximum distance used by the ReflectionProbe's + internal camera. The distance is always at least equal to the **Extents**, but + this can be increased to make objects located outside the extents visible in + reflections. *This property does not affect the maximum distance at which the + ReflectionProbe itself is visible.* +- **Extents:** The area that will be affected by the ReflectionProbe's lighting + and reflections. +- **Origin Offset:** The origin to use for the internal camera used for + reflection probe rendering. This must always be constrained within the + **Extents**. If needed, adjust this to prevent the reflection from being + obstructed by a solid object located exactly at the center of the + ReflectionProbe. +- **Box Projection:** Controls whether parallax correction should be used when + rendering the reflection probe. This adjusts the reflection's appearance + depending on the camera's position (relative to the reflection probe). This + has a small performance cost, but the quality increase is often worth it in + box-shaped rooms. Note that this effect doesn't work quite as well in rooms + with less regular shapes (such as ellipse-shaped rooms). +- **Interior:** If enabled, ambient lighting will not be sourced from the + environment sky, and the background sky won't be rendered onto the reflection + probe. +- **Enable Shadows:** Controls whether real-time light shadows should be + rendered within the reflection probe. Enable this to improve reflection + quality at the cost of performance. This should be left disabled for + reflection probes with the **Always** mode, as it's very expensive to render + reflections with shadows every frame. Fully [baked light ](using_lightmap_gi.md) + shadows are not affected by this setting and will be rendered in the + reflection probe regardless. +- **Cull Mask:** Controls which objects are visible in the reflection. This can + be used to improve performance by excluding small objects from the reflection. + This can also be used to prevent an object from having self-reflection + artifacts in situations where **Origin Offset** can't be used. +- **Mesh LOD Threshold:** The automatic level of detail threshold to use for + rendering meshes within the reflection. This only affects meshes that have + automatic LODs generated for them. Higher values can improve performance by + using less detailed geometry, especially for objects that are far away from + the reflection's origin. The visual difference of using less detailed objects + is usually not very noticeable during gameplay, especially in rough + reflections. + +The Ambient category features several properties to adjust ambient lighting +rendered by the ReflectionProbe: + +- **Mode:** If set to **Disabled**, no ambient light is added by the probe. If + set to **Environment**, the ambient light color is automatically sampled from + the environment sky (if **Interior** is disabled) and the reflection's average + color. If set to **Constant Color**, the color specified in the **Color** + property is used instead. The **Constant Color** mode can be used as an + approximation of area lighting. +- **Color:** The color to use when the ambient light mode is set to **Constant Mode**. +- **Color Energy:** The multiplier to use for the ambient light custom + **Color**. This only has an effect when the ambient light mode is **Custom + Color**. + +## ReflectionProbe blending + +To make transitions between reflection sources smoother, Redot supports automatic +probe blending: + +- Up to 4 ReflectionProbes can be blended together at a given location. + A ReflectionProbe will also fade out smoothly back to environment lighting + when it isn't touching any other ReflectionProbe node. +- SDFGI and VoxelGI will blend in smoothly with ReflectionProbes if used. + This allows placing ReflectionProbes strategically to get more accurate (or fully real-time) + reflections where needed, while still having rough reflections available in the + VoxelGI or SDFGI's area of influence. + +To make several ReflectionProbes blend with each other, you need to have part of +each ReflectionProbe overlap each other's area. The extents should only overlap +as little possible with other reflection probes to improve rendering performance +(typically a few units in 3D space). + +## Limitations + +When using the Forward+ renderer, Redot uses a *clustering* approach for +reflection probe rendering. As many reflection probes as desired can be added (as long as +performance allows). However, there's still a default limit of 512 *clustered +elements* that can be present in the current camera view. A clustered element is +an omni light, a spot light, a [decal ](../using_decals.md) or a +[reflection probe ](reflection_probes.md). This limit can be increased by adjusting +[Max Clustered Elements](class_ProjectSettings_property_rendering/limits/cluster_builder/max_clustered_elements) +in **Project Settings > Rendering > Limits > Cluster Builder**. + +When using the Mobile renderer, only 8 reflection probes can be applied on each +individual Mesh *resource*. If there are more reflection probes affecting a single mesh, +not all of them will be rendered on the mesh. + +Similarly, when using the Compatibility renderer, up to 2 reflection probes can +be applied per mesh. If more than 2 reflection probes affect a single mesh, +additional probes will not be rendered. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/using_lightmap_gi.md b/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/using_lightmap_gi.md new file mode 100644 index 0000000..f0eff4c --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/using_lightmap_gi.md @@ -0,0 +1,626 @@ + +# Using Lightmap global illumination + +Baked lightmaps are a workflow for adding indirect (or fully baked) +lighting to a scene. Unlike the [VoxelGI ](using_voxel_gi.md) and +[SDFGI ](using_sdfgi.md) approaches, baked lightmaps work fine on low-end PCs +and mobile devices, as they consume almost no resources at runtime. Also unlike +VoxelGI and SDFGI, baked lightmaps can optionally be used to store direct +lighting, which provides even further performance gains. + +Unlike VoxelGI and SDFGI, baked lightmaps are completely static. Once baked, they +can't be modified at all. They also don't provide the scene with reflections, so +using [doc_reflection_probes](reflection_probes.md) together with it on interiors (or using a Sky +on exteriors) is a requirement to get good quality. + +As they are baked, they have fewer problems than VoxelGI and SDFGI regarding +light bleeding, and indirect light will often look better. The downside is that +baking lightmaps takes longer compared to baking VoxelGI. While baking VoxelGI +can be done in a matter of seconds, baking lightmaps can take several minutes if +not more. This can slow down iteration speed significantly, so it is recommended +to bake lightmaps only when you actually need to see changes in lighting. Since +Redot 4.0, lightmaps are baked on the GPU, making light baking faster if you +have a mid-range or high-end dedicated GPU. + +Baking lightmaps will also reserve baked materials' UV2 slot, which means you can +no longer use it for other purposes in materials (either in the built-in +[doc_standard_material_3d](../standard_material_3d.md) or in custom shaders). + +Despite their lack of flexibility, baked lightmaps typically offer both the best +quality *and* performance at the same time in (mostly) static scenes. This makes +lightmaps still popular in game development, despite lightmaps being the +oldest technique for global illumination in video games. + +:::info + +Not sure if LightmapGI is suited to your needs? +See [doc_introduction_to_global_illumination_comparison](doc_introduction_to_global_illumination_comparison) +for a comparison of GI techniques available in Redot 4. + +::: + +## Visual comparison + +![Image](/img/Tutorials/3d/global_illumination/img/gi_none.webp) + + LightmapGI disabled. + +![Image](/img/Tutorials/3d/global_illumination/img/gi_lightmap_gi_indirect_only.webp) + + LightmapGI enabled (with indirect light baked only). Direct light is still + real-time, allowing for subtle changes during gameplay. + +![Image](/img/Tutorials/3d/global_illumination/img/gi_lightmap_gi_direct_and_indirect.webp) + + LightmapGI enabled (with direct and indirect light baked). Best performance, + but lower quality visuals. Notice the blurrier sun shadow in the top-right + corner. + +Here are some comparisons of how LightmapGI vs. VoxelGI look. Notice that +lightmaps are more accurate, but also suffer from the fact +that lighting is on an unwrapped texture, so transitions and resolution may not +be that good. VoxelGI looks less accurate (as it's an approximation), but +smoother overall. + +![Image](/img/Tutorials/3d/global_illumination/img/lightmap_gi_comparison.png) + +SDFGI is also less accurate compared to LightmapGI. However, SDFGI can support +large open worlds without any need for baking. + +## Setting up + +:::warning + +Baking lightmaps in the Android and web editors is not supported due to +graphics API limitations on those devices. On Android and web platforms, +only *rendering* lightmaps that were baked on a desktop PC is supported. + +::: + +:::note + +The LightmapGI node only bakes nodes that are on the same level as the +LightmapGI node (siblings), or nodes that are children of the +LightmapGI node. This allows you to use several LightmapGI nodes to bake +different parts of the scene, independently from each other. + +::: + +First of all, before the lightmapper can do anything, the objects to be baked need +a UV2 layer and a texture size. A UV2 layer is a set of secondary texture coordinates +that ensures any face in the object has its own place in the UV map. Faces must +not share pixels in the texture. + +There are a few ways to ensure your object has a unique UV2 layer and texture size: + +### Unwrap on scene import (recommended) + +In most scenarios, this is the best approach to use. The only downside is that, +on large models, unwrapping can take a while on import. Nonetheless, Redot will +cache the UV2 across reimports, so it will only be regenerated when needed. + +Select the imported scene in the filesystem dock, then go to the **Import** dock. +There, the following option can be modified: + +![Image](/img/Tutorials/3d/global_illumination/img/lightmap_gi_import.webp) + +The **Meshes > Light Baking** option must be set to **Static Lightmaps (VoxelGI/SDFGI/LightmapGI)**: + +![Image](/img/Tutorials/3d/global_illumination/img/lightmap_gi_mesh_import_meshes.webp) + +When unwrapping on import, you can adjust the texture size using the **Meshes > Lightmap +Texel Size** option. *Lower* values will result in more detailed lightmaps, +possibly resulting in higher visual quality at the cost of longer bake times and +larger lightmap file sizes. The default value of ``0.2`` is suited for +small/medium-sized scenes, but you may want to increase it to ``0.5`` or even +more for larger scenes. This is especially the case if you're baking indirect +lighting only, as indirect light is low-frequency data (which means it doesn't +need high-resolution textures to be accurately represented). + +The effect of setting this option is that all meshes within the scene will have +their UV2 maps properly generated. + +:::warning + +When reusing a mesh within a scene, keep in mind that UVs will be generated +for the first instance found. If the mesh is re-used with different scales +(and the scales are wildly different, more than half or twice), this will +result in inefficient lightmaps. To avoid this, adjust the **Lightmap +Scale** property in the GeometryInstance3D section of a MeshInstance3D node. +This lets you *increase* the level of lightmap detail for specific +MeshInstance3D nodes (but not decrease it). + +Also, the ``*.unwrap_cache`` files should *not* be ignored in version control +as these files guarantee that UV2 reimports are consistent across platforms +and engine versions. + +::: + +### Unwrap from within Redot + +:::warning + +If this Mesh menu operation is used on an imported 3D scene, the generated +UV2 will be lost when the scene is reloaded. + +::: + +Redot has an option to unwrap meshes and visualize the UV channels. After +selecting a MeshInstance3D node, it can be found in the **Mesh** menu at the top +of the 3D editor viewport: + +![Image](/img/Tutorials/3d/global_illumination/img/lightmap_gi_mesh_menu.webp) + +This will generate a second set of UV2 coordinates which can be used for baking. +It will also set the texture size automatically. + +### Unwrap from your 3D modeling software + +The last option is to do it from your favorite 3D app. This approach is +generally **not recommended**, but it's explained so that you know it exists. +The main advantage is that, on complex objects that you may want to re-import a +lot, the texture generation process can be quite costly within Redot, so having +it unwrapped before import can be faster. + +Simply do an unwrap on the second UV2 layer. + +![Image](/img/Tutorials/3d/global_illumination/img/lightmap_gi_blender.webp) + +Then import the 3D scene normally. Remember you will need to set the texture +size on the mesh after import. + +![Image](/img/Tutorials/3d/global_illumination/img/lightmap_gi_lmsize.webp) + +If you use external meshes on import, the size will be kept. Be wary that most +unwrappers in 3D modeling software are not quality-oriented, as they are meant +to work quickly. You will mostly need to use seams or other techniques to create +better unwrapping. + +### Generating UV2 for primitive meshes + +:::note + +This option is only available for primitive meshes such as [class_BoxMesh](class_BoxMesh), +[class_CylinderMesh](class_CylinderMesh), [class_PlaneMesh](class_PlaneMesh), etc. + +::: + +Enabling UV2 on primitive meshes allows you to make them receive and contribute +to baked lighting. This can be used in certain lighting setups. For instance, +you could hide a torus that has an emissive material after baking lightmaps to +create an area light that follows the shape of a torus. + +By default, primitive meshes do not have UV2 generated to save resources (as +these meshes may be created during gameplay). You can edit a primitive mesh in +the inspector and enable **Add UV2** to make the engine procedurally generate +UV2 for a primitive mesh. The default **UV2 Padding** value is tuned to avoid +most lightmap bleeding, without wasting too much space on the edges. If you +notice lightmap bleeding on a specific primitive mesh only, you may have to +increase **UV2 Padding**. + +**Lightmap Size Hint** represents the size taken by a single mesh on the +lightmap texture, which varies depending on the mesh's size properties and the +**UV2 Padding** value. **Lightmap Size Hint** should not be manually changed, as +any modifications will be lost when the scene is reloaded. + +### Generating UV2 for CSG nodes + +Since Redot 4.4, you can +[convert a CSG node and its children to a MeshInstance3D ](doc_csg_tools_converting_to_mesh_instance_3d). +This can be used to bake lightmaps on a CSG node by following these steps: + +- Select the root CSG node and choose **CSG > Bake Mesh Instance** at the top of the 3D editor viewport. +- Hide the root CSG node that was just baked (it is not hidden automatically). +- Select the newly created MeshInstance3D node and choose **Mesh > Unwrap UV2 for Lightmap/AO**. +- Bake lightmaps. + +:::tip + +Remember to keep the original CSG node in the scene tree, so that you can +perform changes to the geometry later if needed. To make changes to the +geometry, remove the MeshInstance3D node and make the root CSG node visible +again. + +::: + +### Checking UV2 + +In the **Mesh** menu mentioned before, the UV2 texture coordinates can be visualized. +If something is failing, double-check that the meshes have these UV2 coordinates: + +![Image](/img/Tutorials/3d/global_illumination/img/lightmap_gi_uvchannel.webp) + +## Setting up the scene + +Before anything is done, a **LightmapGI** node needs to be added to a scene. +This will enable light baking on all nodes (and sub-nodes) in that scene, even +on instanced scenes. + +![Image](/img/Tutorials/3d/global_illumination/img/lightmap_gi_scene.webp) + +A sub-scene can be instanced several times, as this is supported by the baker. +Each instance will be assigned a lightmap of its own. To avoid issues with +inconsistent lightmap texel scaling, make sure to respect the rule about mesh +scaling mentioned before. + +### Setting up meshes + +For a **MeshInstance3D** node to take part in the baking process, it needs to have +its bake mode set to **Static**. Meshes that have their bake mode set to **Disabled** +or **Dynamic** will be ignored by the lightmapper. + +![Image](/img/Tutorials/3d/global_illumination/img/lightmap_gi_use.webp) + +When auto-generating lightmaps on scene import, this is enabled automatically. + +### Setting up lights + +Lights are baked with indirect light only by default. This means that shadowmapping +and lighting are still dynamic and affect moving objects, but light bounces from +that light will be baked. + +Lights can be disabled (no bake) or be fully baked (direct and indirect). This +can be controlled from the **Bake Mode** menu in lights: + +![Image](/img/Tutorials/3d/global_illumination/img/lightmap_gi_bake_mode.webp) + +The modes are: + +### Disabled + +The light is ignored when baking lightmaps. This is the mode to use for dynamic +lighting effects such as explosions and weapon effects. + +:::warning + +Hiding a light has no effect on the resulting lightmap bake. This means +you must use the Disabled bake mode instead of hiding the Light node by +disabling its **Visible** property. + +::: + +### Dynamic + +This is the default mode, and is a compromise between performance and real-time +friendliness. Only indirect lighting will be baked. Direct light and shadows are +still real-time, as they would be without LightmapGI. + +This mode allows performing *subtle* changes to a light's color, energy and +position while still looking fairly correct. For example, you can use this +to create flickering static torches that have their indirect light baked. + +Depending on the value of **Shadowmask Mode**, it is possible to still get +distant baked shadows for DirectionalLight3D. This allows shadows up close to be +real-time and show dynamic objects, while allowing static objects in the +distance to still cast shadows. + +### Static + +Both indirect and direct lighting will be baked. Since static surfaces can skip +lighting and shadow computations entirely, this mode provides the best +performance along with smooth shadows that never fade based on distance. The +real-time light will not affect baked surfaces anymore, but it will still affect +dynamic objects. When using the **All** bake mode on a light, dynamic objects +will not cast real-time shadows onto baked surfaces, so you need to use a +different approach such as blob shadows instead. Blob shadows can be implemented +with a Decal node. + +The light will not be adjustable at all during gameplay. Moving the light or +changing its color (or energy) will not have any effect on static surfaces. + +Since bake modes can be adjusted on a per-light basis, it is possible to create +hybrid baked light setups. One popular option is to use a real-time +DirectionalLight with its bake mode set to **Dynamic**, and use the **Static** +bake mode for OmniLights and SpotLights. This provides good performance while +still allowing dynamic objects to cast real-time shadows in outdoor areas. + +Fully baked lights can also make use of light nodes' **Size** (omni/spot) or +**Angular Distance** (directional) properties. This allows for shadows with +realistic penumbra that increases in size as the distance between the caster and +the shadow increases. This also has a lower performance cost compared to +real-time PCSS shadows, as only dynamic objects have real-time shadows rendered +on them. + +![Image](/img/Tutorials/3d/global_illumination/img/lightmap_gi_omnilight_size.png) + +## Baking + +To begin the bake process, click the **Bake Lightmaps** button at the top of the +3D editor viewport when selecting the LightmapGI node: + +![Image](/img/Tutorials/3d/global_illumination/img/lightmap_gi_bake.webp) + +This can take from seconds to minutes (or hours) depending on scene size, bake +method and quality selected. + +:::warning + +Baking lightmaps is a process that can require a lot of video memory, +especially if the resulting texture is large. Due to internal limitations, +the engine may also crash if the generated texture size is too large (even +on systems with a lot of video memory). + +To avoid crashes, make sure the lightmap texel size in the Import dock is +set to a high enough value. + +::: + +### Tweaks + +- **Quality:** Four bake quality modes are provided: Low, Medium, High, and + Ultra. Higher quality takes more time, but result in a better-looking lightmap + with less noise. The difference is especially noticeable with emissive + materials or areas that get little to no direct lighting. Each bake quality + mode can be further adjusted in the Project Settings. +- **Bounces:** The number of bounces to use for indirect lighting. The default + value (``3``) is a good compromise between bake times and quality. Higher + values will make light bounce around more times before it stops, which makes + indirect lighting look smoother (but also possibly brighter depending on + materials and geometry). +- **Bounce Indirect Energy:** The global multiplier to use when baking lights' + indirect energy. This multiplies each light's own **Indirect Energy** value. + Values different from ``1.0`` are not physically accurate, but can be used for + artistic effect. +- **Directional:** If enabled, stores directional information for lightmaps. + This improves normal mapped materials' appearance for baked surfaces, + especially with fully baked lights (since they also have direct light baked). + The downside is that directional lightmaps are slightly more expensive to render. + They also require more time to bake and result in larger file sizes. +- **Shadowmask Mode:** If set to a mode other than **None**, the first DirectionalLight3D + in the scene with the **Dynamic** global illumination mode will have its static shadows + baked to a separate texture called a *shadowmask*. This can be used to allow distant + static objects to cast shadows onto other static objects regardless of the distance + from the camera. See the [section on shadowmasking ](doc_using_lightmap_gi_shadowmask) + for further details. +- **Interior:** If enabled, environment lighting will not be sourced. Use this + for purely indoor scenes to avoid light leaks. +- **Use Texture for Bounces:** If enabled, a texture with the lighting + information will be generated to speed up the generation of indirect lighting + at the cost of some accuracy. The geometry might exhibit extra light leak + artifacts when using low resolution lightmaps or UVs that stretch the lightmap + significantly across surfaces. Leave this enabled if unsure. +- **Use Denoiser:** If enabled, uses a denoising algorithm to make the lightmap + significantly less noisy. This increases bake times and can occasionally + introduce artifacts, but the result is often worth it. See + [doc_using_lightmap_gi_denoising](doc_using_lightmap_gi_denoising) for more information. +- **Denoiser Strength:** The strength of denoising step applied to the generated + lightmaps. Higher values are more effective at removing noise, but can reduce + shadow detail for static shadows. Only effective if denoising is enabled and + the denoising method is :abbr:`JNLM (Non-Local Means with Joint Filtering)` + (:abbr:`OIDN (Open Image Denoise)` does not have a denoiser strength setting). +- **Bias:** The offset value to use for shadows in 3D units. You generally don't + need to change this value, except if you run into issues with light bleeding or + dark spots in your lightmap after baking. This setting does not affect real-time + shadows casted on baked surfaces (for lights with **Dynamic** bake mode). +- **Max Texture Size:** The maximum texture size for the generated texture + atlas. Higher values will result in fewer slices being generated, but may not + work on all hardware as a result of hardware limitations on texture sizes. + Leave this at its default value of ``16384`` if unsure. +- **Environment > Mode:** Controls how environment lighting is sourced when + baking lightmaps. The default value of **Scene** is suited for levels with + visible exterior parts. For purely indoor scenes, set this to **Disabled** to + avoid light leaks and speed up baking. This can also be set to **Custom Sky** + or **Custom Color** to use environment lighting that differs from the actual + scene's environment sky. +- **Gen Probes > Subdiv:** See [doc_using_lightmap_gi_dynamic_objects](doc_using_lightmap_gi_dynamic_objects). +- **Data > Light Data:** See [doc_using_lightmap_gi_data](doc_using_lightmap_gi_data). + +## Using shadowmasking for distant directional shadows + +When using a DirectionalLight3D, the maximum distance at which it can draw +real-time shadows is limited by its **Shadow Max Distance** property. This can +be an issue in large scenes, as distant objects won't appear to have any shadows +from the DirectionalLight3D. While this can be resolved by using the **Static** +global illumination mode on the DirectionalLight3D, this has several downsides: + +- Since both direct and indirect light are baked, there is no way for dynamic + objects to cast shadows onto static surfaces in a realistic manner. Redot skips + shadow sampling entirely in this case to avoid "double lighting" artifacts. +- Static shadows up close lack in detail, as they only rely on the lightmap texture + and not on real-time shadow cascades. + +We can avoid these downsides while still benefiting from distant shadows by +using *shadowmasking*. While dynamic objects won't receive shadows from the +shadowmask, it still greatly improves visuals since most scenes are primarily +comprised of static objects. + +Since the lightmap texture alone doesn't contain shadow information, we can bake +this shadow information to a separate texture called a *shadowmask*. + +Shadowmasking only affects the first DirectionalLight3D in the scene (determined +by tree order) that has the **Dynamic** global illumination mode. It is not +possible to use shadowmasking with the **Static** global illumination mode, as +this mode skips shadow sampling on static objects entirely. This is because the +Static global illumination mode bakes both direct and indirect light. + +Three shadowmasking modes are available: + +- **None (default):** Don't bake a shadowmask texture. Directional shadows will + not be visible outside the range specified by the DirectionalLight3D's + **Shadow Max Distance** property. +- **Replace:** Bakes a shadowmask texture, and uses it to draw directional + shadows when outside the range specified by the DirectionalLight3D's **Shadow + Max Distance** property. Shadows within this range remain fully real-time. + This option generally makes the most sense for most scenes, as it can deal + well with static objects that exhibit subtle motion (e.g. foliage shadows). +- **Overlay:** Bakes a shadowmask texture, and uses it to draw directional + shadows regardless of the distance from the camera. Shadows within the range + of the DirectionalLight3D's **Shadow Max Distance** property will be overlaid + with real-time shadows. This can make the transition between real-time and + baked shadows less jarring, at the cost of a "smearing" effect present on + static object shadows depending on lightmap texel density. Also, this mode + can't deal as well with static objects that exhibit subtle motion (such as + foliage), as the baked shadows can't be animated over time. Still, for scenes + where the camera moves quickly, this may be a better choice than **Replace**. + +Here's a visual comparison of the shadowmask modes with a scene where the +**Shadow Max Distance** was set very low for comparison purposes. The blue boxes +are dynamic objects, while the rest of the scene is a static object. There is +only a single DirectionalLight3D in the scene with the Dynamic global +illumination mode: + +![Image](/img/Tutorials/3d/global_illumination/img/lightmap_gi_shadowmask.webp) + + Comparison between shadowmask modes + +:::note + +It is possible to switch between the **Replace** and **Overlay** shadowmask +modes without having to bake lightmaps again. + +::: + +## Balancing bake times with quality + +Since high-quality bakes can take very long (up to dozens of minutes for large +complex scenes), it is recommended to use lower quality settings at first. Then, +once you are confident with your scene's lighting setup, raise the quality +settings and perform a "final" bake before exporting your project. + +Reducing the lightmap resolution by increasing **Lightmap Texel Size** on the +imported 3D scenes will also speed up baking significantly. However, this will +require you to reimport all lightmapped 3D scenes before you can bake lightmaps +again. + +## Denoising + +Since baking lightmaps relies on raytracing, there will always be visible noise +in the "raw" baked lightmap. Noise is especially visible in areas that are +difficult to reach by bounced light, such as indoor areas with small openings +where the sunlight can enter. Noise can be reduced by increasing bake quality, +but doing so will increase bake times significantly. + +![Image](/img/Tutorials/3d/global_illumination/img/lightmap_gi_denoiser_comparison.webp) + + Comparison between denoising disabled and enabled (with the default JNLM denoiser). + +To combat noise without increasing bake times too much, a denoiser can be used. +A denoiser is an algorithm that runs on the final baked lightmap, detects patterns of +noise and softens them while attempting to best preserve detail. +Redot offers two denoising algorithms: + +### JNLM (Non-Local Means with Joint Filtering) + +JNLM is the default denoising method and is included in Redot. It uses a simple +but efficient denoising algorithm known as *non-local means*. JNLM runs on the +GPU using a compute shader, and is compatible with any GPU that can run Redot +4's Vulkan-based rendering methods. No additional setup is required. + +JNLM's denoising can be adjusted using the **Denoiser Strength** property that +is visible when **Use Denoiser** enabled. Higher values can be more effective at +removing noise, at the cost of suppressing shadow detail for static shadows. + +![Image](/img/Tutorials/3d/global_illumination/img/lightmap_gi_denoiser_jnlm_strength.webp) + + Comparison between JNLM denoiser strength values. Higher values can reduce detail. + +### OIDN (Open Image Denoise) + +Unlike JNLM, OIDN uses a machine learning approach to denoising lightmaps. It +features a model specifically trained to remove noise from lightmaps while +preserving more shadow detail in most scenes compared to JNLM. + +OIDN can run on the GPU if hardware acceleration is configured. With a modern +high-end GPU, this can provide a speedup of over 50× over CPU-based denoising: + +- On AMD GPUs, HIP must be installed and configured. +- On NVIDIA GPUs, CUDA must be installed and configured. This may automatically + be done by the NVIDIA installer, but on Linux, CUDA libraries may not be + installed by default. Double-check that the CUDA packages from your Linux + distribution are installed. +- On Intel GPUs, SYCL must be installed and configured. + +If hardware acceleration is not available, OIDN will fall back to multithreaded +CPU-based denoising. To confirm whether GPU-based denoising is working, use a +GPU utilization monitor while baking lightmaps and look at the GPU utilization +percentage and VRAM utilization while the denoising step is shown in the Redot +editor. The ``nvidia-smi`` command line tool can be useful for this. + +OIDN is not included with Redot due to its relatively large download size. You +can download precompiled OIDN binary packages from its +[website ](https://www.openimagedenoise.org/downloads.html). +Extract the package to a location on your PC, then specify the path to the +``oidnDenoise`` executable in the Editor Settings (**FileSystem > Tools > OIDN > +OIDN Denoise Path**). This executable is located within the ``bin`` folder of +the binary package you extracted. + +After specifying the path to the OIDN denoising executable, change the denoising +method in the project settings by setting **Rendering > Lightmapping > +Denoiser** to **OIDN**. This will affect all lightmap bakes on this project +after the setting is changed. + +:::note + +The denoising method is configured in the project settings instead of the +editor settings. This is done so that different team members working on the +same project are assured to be using the same denoising method for +consistent results. + +::: + +![Image](/img/Tutorials/3d/global_illumination/img/lightmap_gi_denoiser_jnlm_vs_oidn.webp) + + Comparison between JNLM and OIDN denoisers. + Notice how OIDN better preserves detail and reduces seams across different objects. + +## Dynamic objects + +Unlike VoxelGI and SDFGI, dynamic objects receive indirect lighting differently +compared to static objects. This is because lightmapping is only performed on +static objects. + +To display indirect lighting on dynamic objects, a 3D probe system is used, with +light probes being spread throughout the scene. When baking lightmaps, the +lightmapper will calculate the amount of *indirect* light received by the probe. +Direct light is not stored within light probes, even for lights that have their +bake mode set to **Static** (as dynamic objects continue to be lit in +real-time). + +There are 2 ways to add light probes to a scene: + +- **Automatic:** Set **Gen Probes > Subdiv** to a value other than **Disabled**, + then bake lightmaps. The default is ``8``, but you can choose a greater value + to improve precision at the cost of longer bake times and larger output file + size. +- **Manual:** In addition or as an alternative to generating probes + automatically, you can add light probes manually by adding [class_LightmapProbe](class_LightmapProbe) + nodes to the scene. This can be used to improve lighting detail in areas frequently + travelled by dynamic objects. After placing LightmapProbe nodes in the scene, + you must bake lightmaps again for them to be effective. + +:::note + +After baking lightmaps, you will notice white spheres in the 3D scene that +represent how baked lighting will affect dynamic objects. These spheres do +**not** appear in the running project. + +If you want to hide these spheres in the editor, toggle **View > Gizmos > +LightmapGI** at the top of the 3D editor (a "closed eye" icon indicates the +gizmo is hidden). + +::: + +## Lightmap data + +The **Data > Light Data** property in the LightmapGI node contains the lightmap +data after baking. Textures are saved to disk, but this also contains the +capture data for dynamic objects, which can be heavy. If you are using a scene +in ``.tscn`` format, you should save this resource to an external binary +``.lmbake`` file to avoid bloating the ``.tscn`` scene with binary data encoded +in Base64. + +:::tip + +The generated EXR file can be viewed and even edited using an image editor +to perform post-processing if needed. However, keep in mind that changes to +the EXR file will be lost when baking lightmaps again. + +::: + +## Reducing LightmapGI artifacts + +If you notice LightmapGI nodes popping in and out of existence as the camera +moves, this is most likely because the engine is rendering too many LightmapGI +instances at once. Redot is limited to rendering 8 LightmapGI nodes at once, +which means up to 8 instances can be in the camera view before some of them will +start flickering. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/using_sdfgi.md b/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/using_sdfgi.md new file mode 100644 index 0000000..0284eb4 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/using_sdfgi.md @@ -0,0 +1,231 @@ + +# Signed distance field global illumination (SDFGI) + +Signed distance field global illumination (SDFGI) is a novel technique available +in Redot 4.0. It provides semi-real-time global illumination that scales to any +world size and works with procedurally generated levels. + +SDFGI supports dynamic lights, but *not* dynamic occluders or dynamic emissive surfaces. +Therefore, SDFGI provides better real-time ability than +[baked lightmaps ](using_lightmap_gi.md), but worse real-time ability than +[VoxelGI ](using_voxel_gi.md). + +From a performance standpoint, SDFGI is one of the most demanding global illumination +techniques in Redot. Like with VoxelGI, there are still many settings available to tweak +its performance requirements at the cost of quality. + +:::important + +SDFGI is only supported when using the Forward+ renderer, not the Mobile or +Compatibility renderers. + +::: + +:::info + +Not sure if SDFGI is suited to your needs? +See [doc_introduction_to_global_illumination_comparison](doc_introduction_to_global_illumination_comparison) +for a comparison of GI techniques available in Redot 4. + +::: + +## Visual comparison + +![Image](/img/Tutorials/3d/global_illumination/img/gi_none.webp) + + SDFGI disabled. + +![Image](/img/Tutorials/3d/global_illumination/img/gi_sdfgi.webp) + + SDFGI enabled. + +## Setting up SDFGI + +In Redot, SDFGI is the global illumination technique with the fewest required +steps to enable: + +1. Make sure your MeshInstance nodes have their **Global Illumination > Mode** + property set to **Static** in the inspector. + + - For imported 3D scenes, the bake mode can be configured in the Import dock + after selecting the 3D scene file in the FileSystem dock. + +2. Add a WorldEnvironment node and create an Environment resource for it. +3. Edit the Environment resource, scroll down to the **SDFGI** section and unfold it. +4. Enable **SDFGI > Enabled**. SDFGI will automatically follow the camera when it + moves, so you do not need to configure extents (unlike VoxelGI). + +## Environment SDFGI properties + +In the Environment resource, there are several properties available to adjust +SDFGI appearance and quality: + +- **Use Occlusion:** If enabled, SDFGI will throw additional rays to find and + reduce light leaks. This has a performance cost, so only enable this property + if you actually need it. +- **Read Sky Light:** If enabled, the environment lighting is represented in the + global illumination. This should be enabled in outdoor scenes and disabled in + fully indoor scenes. +- **Bounce Feedback:** By default, indirect lighting only bounces once when + using SDFGI. Setting this value above ``0.0`` will cause SDFGI to bounce more + than once, which provides more realistic indirect lighting at a small + performance cost. Sensible values are usually between ``0.3`` and ``1.0`` + depending on the scene. Note that in some scenes, values above ``0.5`` can + cause infinite feedback loops to happen, causing the scene to become extremely + bright in a few seconds' time. + If your indirect lighting looks "splotchy", consider increasing this value above + ``0.0`` to get more uniform-looking lighting. If your lighting ends up looking + too bright as a result, decrease **Energy** to compensate. +- **Cascades:** Higher values result in more detailed GI information + (and/or greater maximum distance), but are significantly more expensive on the + CPU and GPU. The performance cost of having more cascades especially increases + when the camera moves fast, so consider decreasing this to ``4`` or lower + if your camera moves fast. +- **Min Cell Size:** The minimum SDFGI cell size to use for the nearest, most detailed + cascade. Lower values result in more accurate indirect lighting and reflection + at the cost of lower performance. + Adjusting this setting also affects **Cascade 0 Distance** and **Max Distance** automatically. +- **Cascade 0 Distance:** The distance at which the nearest, most detailed + cascade ends. Greater values make the nearest cascade transition less noticeable, + at the cost of reducing the level of detail in the nearest cascade. + Adjusting this setting also affects **Min Cell Size** and **Max Distance** automatically. +- **Max Distance:** Controls how far away the signed distance field will be computed + (for the least detailed cascade). SDFGI will not have any effect past this distance. + This value should always be set below the Camera's Far value, as there is no benefit + in computing SDFGI past the viewing distance. + Adjusting this setting also affects **Min Cell Size** and **Cascade 0 Distance** automatically. +- **Y Scale:** Controls how far apart SDFGI probes are spread *vertically*. + By default, vertical spread is the same as horizontal. However, since most + game scenes aren't highly vertical, setting the Y Scale to + ``75%`` or even ``50%`` can provide better quality and reduce light leaks + without impacting performance. +- **Energy:** The brightness multiplier for SDFGI's indirect lighting. +- **Normal Bias:** The normal bias to use for SDFGI's probe ray bounces. + Unlike **Probe Bias**, this only increases the value in relation to the + mesh's normals. This makes the bias adjustment more nuanced and avoids + increasing the bias too much for no reason. Increase this + value if you notice striping artifacts in indirect lighting or reflections. +- **Probe Bias:** The bias to use for SDFGI's probe ray bounces. Increase this + value if you notice striping artifacts in indirect lighting or reflections. + +## SDFGI interaction with lights and objects + +The amount of indirect energy emitted by a light is governed by its color, +energy *and* indirect energy properties. To make a specific light emit more +or less indirect energy without affecting the amount of direct light emitted +by the light, adjust the **Indirect Energy** property in the Light3D inspector. + +To ensure correct visuals when using SDFGI, you must configure your meshes +and lights' global illumination properties according to their *purpose* in the +scene (static or dynamic). + +There are 3 global illumination modes available for meshes: + +- **Disabled:** The mesh won't be taken into account in SDFGI generation. + The mesh will receive indirect lighting from the scene, but it will not + contribute indirect lighting to the scene. +- **Static (default):** The mesh will be taken into account in SDFGI generation. + The mesh will both receive *and* contribute indirect lighting to the scene. If + the mesh is changed in any way after SDFGI is generated, the camera must move + away from the object then move back close to it for SDFGI to regenerate. + Alternatively, SDFGI can be toggled off and back on. If neither is done, + indirect lighting will look incorrect. +- **Dynamic (not supported with SDFGI):** The mesh won't be taken into account in SDFGI generation. + The mesh will receive indirect lighting from the scene, but it will not + contribute indirect lighting to the scene. + *This acts identical to the **Disabled** bake mode when using SDFGI.* + +Additionally, there are 3 bake modes available for lights +(DirectionalLight3D, OmniLight3D and SpotLight3D): + +- **Disabled:** The light won't be taken into account for SDFGI baking. + The light won't contribute indirect lighting to the scene. +- **Static:** The light will be taken into account for SDFGI baking. The light + will contribute indirect lighting to the scene. If the light is changed in any + way after baking, indirect lighting will look incorrect until the camera moves + away from the light and back (which causes SDFGI to be baked again). will look + incorrect. If in doubt, use this mode for level lighting. +- **Dynamic (default):** The light won't be taken into account for SDFGI baking, + but it will still contribute indirect lighting to the scene in real-time. + This option is slower compared to **Static**. Only use the **Dynamic** global + illumination mode on lights that will change significantly during gameplay. + +:::note + +The amount of indirect energy emitted by a light depends on its color, +energy *and* indirect energy properties. To make a specific light emit more +or less indirect energy without affecting the amount of direct light emitted +by the light, adjust the **Indirect Energy** property in the Light3D inspector. + +::: + +:::info + +See [doc_introduction_to_global_illumination_gi_mode_recommendations](doc_introduction_to_global_illumination_gi_mode_recommendations) +for general usage recommendations. + +::: + +## Adjusting SDFGI performance and quality + +Since SDFGI is relatively demanding, it will perform best on systems with recent +dedicated GPUs. On older dedicated GPUs and integrated graphics, +tweaking the settings is necessary to achieve reasonable performance. + +In the Project Settings' **Rendering > Global Illumination** section, +SDFGI quality can also be adjusted in several ways: + +- **Sdfgi > Probe Ray Count:** Higher values result in better quality, + at the cost of higher GPU usage. If this value is set too low, + this can cause surfaces to have visible "splotches" of indirect lighting on + them due to the number of rays thrown being very low. +- **Sdfgi > Frames To Converge:** Higher values result in better quality, but GI will take + more time to fully converge. The effect of this setting is especially noticeable when first + loading a scene, or when lights with a bake mode other than **Disabled** are moving fast. + If this value is set too low, this can cause surfaces to have visible "splotches" + of indirect lighting on them due to the number of rays thrown being very low. + If your scene's lighting doesn't have fast-moving lights that contribute to GI, + consider setting this to ``30`` to improve quality without impacting performance. +- **Sdfgi > Frames To Update Light:** Lower values result in moving lights being + reflected faster, at the cost of higher GPU usage. If your scene's lighting + doesn't have fast-moving lights that contribute to GI, consider setting this + to ``16`` to improve performance. +- **Gi > Use Half Resolution:** If enabled, both SDFGI and VoxelGI will have + their GI buffer rendering at halved resolution. For instance, when rendering + in 3840×2160, the GI buffer will be computed at a 1920×1080 resolution. + Enabling this option saves a lot of GPU time, but it can introduce visible + aliasing around thin details. + +SDFGI rendering performance also depends on the number of cascades and +the cell size chosen in the Environment resource (see above). + +## SDFGI caveats + +SDFGI has some downsides due to its cascaded nature. When the camera moves, +cascade shifts may be visible in indirect lighting. This can be alleviated +by adjusting the cascade size, but also by adding fog (which will make distant +cascade shifts less noticeable). + +Additionally, performance will suffer if the camera moves too fast. +This can be fixed in two ways: + +- Ensuring the camera doesn't move too fast in any given situation. +- Temporarily disabling SDFGI in the Environment resource if the camera needs + to be moved at a high speed, then enabling SDFGI once the camera speed slows down. + +When SDFGI is enabled, it will also take some time for global illumination +to be fully converged (25 frames by default). This can create a noticeable transition +effect while GI is still converging. To hide this, you can use a ColorRect node +that spans the whole viewport and fade it out when switching scenes using an +AnimationPlayer node. + +The signed distance field is only updated when the camera moves in and out of a +cascade. This means that if geometry is modified in the distance, the global +illumination appearance will be correct once the camera gets closer. However, if +a nearby object with a bake mode set to **Static** or **Dynamic** is moved (such +as a door), the global illumination will appear incorrect until the camera moves +away from the object. + +SDFGI's sharp reflections are only visible on opaque materials. Transparent +materials will only use rough reflections, even if the material's roughness is +lower than 0.2. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/using_voxel_gi.md b/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/using_voxel_gi.md new file mode 100644 index 0000000..60ce9e2 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/global_illumination/using_voxel_gi.md @@ -0,0 +1,206 @@ + +# Using Voxel global illumination + +VoxelGI is a form of fully real-time global illumination, intended to be used +for small/medium-scale 3D scenes. VoxelGI is fairly demanding on the GPU, so +it's best used when targeting dedicated graphics cards. + +:::important + +VoxelGI is only supported when using the Forward+ renderer, not the Mobile or +Compatibility renderers. + +::: + +:::info + +Not sure if VoxelGI is suited to your needs? +See [doc_introduction_to_global_illumination_comparison](doc_introduction_to_global_illumination_comparison) +for a comparison of GI techniques available in Redot 4. + +::: + +## Visual comparison + +![Image](/img/Tutorials/3d/global_illumination/img/gi_none.webp) + + VoxelGI disabled. + +![Image](/img/Tutorials/3d/global_illumination/img/gi_voxel_gi.webp) + + VoxelGI enabled. + +## Setting up VoxelGI + +1. Make sure your static level geometry is imported with the Light Baking option + set to **Static** or **Static Lightmaps** in the Import dock. + For manually added MeshInstance3D nodes, make sure the **Global Illumination > Mode** + property is set to **Static** in the inspector. +2. Create a VoxelGI node in the Scene tree dock. +3. Move the VoxelGI node to the center of the area you want it to cover by + dragging the manipulation gizmo in the 3D viewport. Then adjust the VoxelGI's + extents by dragging the red points in the 3D viewport (or enter values in the + inspector). Make sure the VoxelGI's extents aren't unnecessarily large, or + quality will suffer. +4. Select the VoxelGI node and click **Bake** at the top of the 3D editor viewport. + This will take at least a few seconds to complete (depending on the number of VoxelGI + subdivisions and scene complexity). + +If at least one mesh contained within the VoxelGI's extents has its global +illumination mode set to **Static**, you should see indirect lighting appear +within the scene. + +:::note + +To avoid bloating text-based scene files with large amounts of binary data, +make sure the VoxelGIData resource is *always* saved to an external binary file. +This file must be saved with a ``.res`` (binary resource) extension instead of +``.tres`` (text-based resource). +Using an external binary resource for VoxelGIData will keep your text-based +scene small while ensuring it loads and saves quickly. + +::: + +## VoxelGI node properties + +The following properties can be adjusted in the VoxelGI node inspector before +baking: + +- **Subdiv:** Higher values result in more precise indirect lighting, at the cost + of lower performance, longer bake times and increased storage requirements. +- **Extents:** Represents the size of the box in which indirect lighting should + be baked. Extents are centered around the VoxelGI node's origin. + +The following properties can be adjusted in the VoxelGIData *resource* that is +contained within a VoxelGI node after it has been baked: + +- **Dynamic Range:** The maximum brightness that can be represented in indirect lighting. + Higher values make it possible to represent brighter indirect light, + at the cost of lower precision (which can result in visible banding). + If in doubt, leave this unchanged. +- **Energy:** The indirect lighting's overall energy. This also effects the energy + of direct lighting emitted by meshes with emissive materials. +- **Bias:** Optional bias added to lookups into the voxel buffer at runtime. + This helps avoid self-occlusion artifacts. +- **Normal Bias:** Similar to **Bias**, but offsets the lookup into the voxel buffer + by the surface normal. This also helps avoid self-occlusion artifacts. Higher + values reduce self-reflections visible in non-rough materials, at the cost of + more visible light leaking and flatter-looking indirect lighting. To + prioritize hiding self-reflections over lighting quality, set **Bias** to + ``0.0`` and **Normal Bias** to a value between ``1.0`` and ``2.0``. +- **Propagation:** The energy factor to use for bounced indirect lighting. + Higher values will result in brighter, more diffuse lighting + (which may end up looking too flat). When **Use Two Bounces** is enabled, + you may want to decrease **Propagation** to compensate for the overall brighter + indirect lighting. +- **Use Two Bounces:** If enabled, lighting will bounce twice instead of just once. + This results in more realistic-looking indirect lighting, and makes indirect lighting + visible in reflections as well. Enabling this generally has no noticeable performance cost. +- **Interior:** If enabled, environment sky lighting will not be taken into account by VoxelGI. + This should be enabled in indoor scenes to avoid light leaking from the environment. + +## VoxelGI interaction with lights and objects + +To ensure correct visuals when using VoxelGI, you must configure your meshes +and lights' global illumination properties according to their *purpose* in the +scene (static or dynamic). + +There are 3 global illumination modes available for meshes: + +- **Disabled:** The mesh won't be taken into account for VoxelGI baking. + The mesh will *receive* indirect lighting from the scene, but it will not + *contribute* indirect lighting to the scene. +- **Static (default):** The mesh will be taken into account for VoxelGI baking. The mesh will + both receive *and* contribute indirect lighting to the scene. If the mesh + is changed in any way after baking, the VoxelGI node must be baked again. + Otherwise, indirect lighting will look incorrect. +- **Dynamic:** The mesh won't be taken into account for VoxelGI baking, but it will + still receive *and* contribute indirect lighting to the scene in real-time. + This option is much slower compared to **Static**. Only use the **Dynamic** + global illumination mode on large meshes that will change significantly during gameplay. + +Additionally, there are 3 bake modes available for lights +(DirectionalLight3D, OmniLight3D and SpotLight3D): + +- **Disabled:** The light won't be taken into account for VoxelGI baking. + The light won't contribute indirect lighting to the scene. +- **Static:** The light will be taken into account for VoxelGI baking. + The light will contribute indirect lighting to the scene. If the light + is changed in any way after baking, the VoxelGI node must be baked again or + indirect lighting will look incorrect. If in doubt, use this mode for level lighting. +- **Dynamic (default):** The light won't be taken into account for VoxelGI baking, + but it will still contribute indirect lighting to the scene in real-time. + This option is slower compared to **Static**. Only use the **Dynamic** global + illumination mode on lights that will change significantly during gameplay. + +:::note + +The amount of indirect energy emitted by a light depends on its color, +energy *and* indirect energy properties. To make a specific light emit more +or less indirect energy without affecting the amount of direct light emitted +by the light, adjust the **Indirect Energy** property in the Light3D inspector. + +::: + +:::info + +See [doc_introduction_to_global_illumination_gi_mode_recommendations](doc_introduction_to_global_illumination_gi_mode_recommendations) +for general usage recommendations. + +::: + +## Adjusting VoxelGI performance and quality + +Since VoxelGI is relatively demanding, it will perform best on systems with recent +dedicated GPUs. On older dedicated GPUs and integrated graphics, +tweaking the settings is necessary to achieve reasonable performance. + +In the Project Settings' **Rendering > Global Illumination** section, +VoxelGI quality can also be adjusted in two ways: + +- **Voxel Gi > Quality:** If set to **Low** + instead of **High**, voxel cone tracing will only use 4 taps instead of 6. + This speeds up rendering at the cost of less pronounced ambient occlusion. +- **Gi > Use Half Resolution:** If enabled, both VoxelGI and SDFGI will have + their GI buffer rendering at halved resolution. For instance, when rendering + in 3840×2160, the GI buffer will be computed at a 1920×1080 resolution. + Enabling this option saves a lot of GPU time, but it can introduce visible + aliasing around thin details. + +Note that the **Advanced** toggle must be enabled in the project settings dialog +for the above settings to be visible. + +Additionally, VoxelGI can be disabled entirely by hiding the VoxelGI node. +This can be used for comparison purposes or to improve performance on low-end systems. + +## Reducing VoxelGI light leaks and artifacts + +After baking VoxelGI, you may notice indirect light is leaking at some spots +in your level geometry. This can be remedied in several ways: + +- For both light leaking and artifacts, try moving or rotating the VoxelGI node + then bake it again. +- To combat light leaking in general, ensure your level geometry is fully sealed. + This is best done in the 3D modeling software used to design the level, + but primitive MeshInstance3D nodes with their global illumination mode set to + **Static** can also be used. +- To combat light leaking with thin geometry, it's recommended to make the geometry + in question thicker. If this is not possible, then add a primitive MeshInstance3D + node with its global illumination mode set to **Static**. Bake VoxelGI again, + then hide the primitive MeshInstance3D node (it will still be taken into account by VoxelGI). + For optimal results, the MeshInstance3D should have a material whose color + matches the original thin geometry. +- To combat artifacts that can appear on reflective surfaces, try increasing + **Bias** and/or **Normal Bias** in the VoxelGIData resource as described above. + Do not increase these values too high, or light leaking will become more pronounced. + +If you notice VoxelGI nodes popping in and out of existence as the camera moves, +this is most likely because the engine is rendering too many VoxelGI instances +at once. Redot is limited to rendering 8 VoxelGI nodes at once, which means up +to 8 instances can be in the camera view before some of them will start +flickering. + +Additionally, for performance reasons, Redot can only blend between 2 VoxelGI +nodes at a given pixel on the screen. If you have more than 2 VoxelGI nodes +overlapping, global illumination may appear to flicker as the camera moves or +rotates. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/high_dynamic_range.md b/Redot-Documentation/docs/26.1/Tutorials/3d/high_dynamic_range.md new file mode 100644 index 0000000..7a7ee0a --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/high_dynamic_range.md @@ -0,0 +1,114 @@ +:::warning +This page is marked as outdated and may not reflect current Redot behavior. +::: + +# High dynamic range lighting + +## Introduction + +Normally, an artist does all the 3D modeling, then all the texturing, looks at +their awesome looking model in the 3D modeling software and says "looks +fantastic, ready for integration!" then goes into the game, lighting is setup +and the game runs. + +So at what point does all this "HDR" business come into play? To understand +the answer, we need to look at how displays behave. + +Your display outputs linear light ratios from some maximum to some minimum +intensity. Modern game engines perform complex math on linear light values in +their respective scenes. So what's the problem? + +The display has a limited range of intensity, depending on the display type. +The game engine renders to an unlimited range of intensity values, however. +While "maximum intensity" means something to an sRGB display, it has no bearing +in the game engine; there is only a potentially infinitely wide range +of intensity values generated per frame of rendering. + +This means that some transformation of the scene light intensity, also known +as *scene-referred* light ratios, need to be transformed and mapped to fit +within the particular output range of the chosen display. This can be most +easily understood if we consider virtually photographing our game engine scene +through a virtual camera. Here, our virtual camera would apply a particular +camera rendering transform to the scene data, and the output would be ready +for display on a particular display type. + +:::note + +Redot does not support high dynamic range *output* yet. It can only perform +lighting in HDR and tonemap the result to a low dynamic range image. + +For advanced users, it is still possible to get a non-tonemapped image +of the viewport with full HDR data, which can then be saved to an OpenEXR file. + +::: + +## Computer displays + +Almost all displays require a nonlinear encoding for the code values sent +to them. The display in turn, using its unique transfer characteristic, +"decodes" the code value into linear light ratios of output, and projects +the ratios out of the uniquely colored lights at each reddish, greenish, +and blueish emission site. + +For a majority of computer displays, the specifications of the display are +outlined in accordance with IEC 61966-2-1, also known as the +1996 sRGB specification. This specification outlines how an sRGB display +is to behave, including the color of the lights in the LED pixels as well as +the transfer characteristics of the input (OETF) and output (EOTF). + +Not all displays use the same OETF and EOTF as a computer display. +For example, television broadcast displays use the BT.1886 EOTF. +However, Redot currently only supports sRGB displays. + +The sRGB standard is based around the nonlinear relationship between the current +to light output of common desktop computing CRT displays. + +![Image](/img/Tutorials/3d/img/hdr_gamma.png) + +The mathematics of a scene-referred model require that we multiply the scene by +different values to adjust the intensities and exposure to different +light ranges. The transfer function of the display can't appropriately render +the wider dynamic range of the game engine's scene output using the simple +transfer function of the display. A more complex approach to encoding +is required. + +## Scene linear & asset pipelines + +Working in scene-linear sRGB is more complex than pressing a single switch. First, +imported image assets must be converted to linear light ratios on import. Even +when linearized, those assets may not be perfectly well-suited for use +as textures, depending on how they were generated. + +There are two ways to do this: + +### sRGB transfer function to display linear ratios on image import + +This is the easiest method of using sRGB assets, but it's not the most ideal. +One issue with this is loss of quality. Using 8 bits per channel to represent +linear light ratios is not sufficient to quantize the values correctly. +These textures may also be compressed later, which can exacerbate the problem. + +### Hardware sRGB transfer function to display linear conversion + +The GPU will do the conversion after reading the texel using floating-point. +This works fine on PC and consoles, but most mobile devices don't support it, +or they don't support it on compressed texture formats (iOS for example). + +### Scene linear to display-referred nonlinear + +After all the rendering is done, the scene linear render requires transforming +to a suitable output such as an sRGB display. To do this, enable sRGB conversion +in the current [Environment](class_Environment) (more on that below). + +Keep in mind that the **sRGB -> Display Linear** and **Display Linear -> sRGB** +conversions must always be **both** enabled. Failing to enable one of them will +result in horrible visuals suitable only for avant-garde experimental +indie games. + +## Parameters of HDR + +HDR settings can be found in the [Environment](class_Environment) +resource. Most of the time, these are found inside a +[WorldEnvironment](class_WorldEnvironment) +node or set in a Camera node. For more information, see +[doc_environment_and_post_processing](environment_and_post_processing.md). \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/index.md b/Redot-Documentation/docs/26.1/Tutorials/3d/index.md new file mode 100644 index 0000000..812d53c --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/index.md @@ -0,0 +1,34 @@ +# 3D + +This section contains tutorials and documentation about 3d in Redot Engine. + +## Articles + +- [3D antialiasing](3d_antialiasing) +- [3D rendering limitations](3d_rendering_limitations) +- [3D text](3d_text) +- [Prototyping levels with CSG](csg_tools) +- [Environment and post-processing](environment_and_post_processing) +- [High dynamic range lighting](high_dynamic_range) +- [Introduction to 3D](introduction_to_3d) +- [3D lights and shadows](lights_and_shadows) +- [Mesh level of detail (LOD)](mesh_lod) +- [Occlusion culling](occlusion_culling) +- [Physical light and camera units](physical_light_and_camera_units) +- [Resolution scaling](resolution_scaling) +- [Third-person camera with spring arm](spring_arm) +- [Standard Material 3D and ORM Material 3D](standard_material_3d) +- [Using decals](using_decals) +- [Using gridmaps](using_gridmaps) +- [Using MultiMeshInstance3D](using_multi_mesh_instance) +- [Axis order](using_transforms) +- [Variable rate shading](variable_rate_shading) +- [Visibility ranges (HLOD)](visibility_ranges) +- [Volumetric fog and fog volumes](volumetric_fog) + +## Subcategories + +- [Global illumination](./global_illumination/index) +- [Particles](./particles/index) +- [Procedural geometry](./procedural_geometry/index) + diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/introduction_to_3d.md b/Redot-Documentation/docs/26.1/Tutorials/3d/introduction_to_3d.md new file mode 100644 index 0000000..be3997b --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/introduction_to_3d.md @@ -0,0 +1,426 @@ + +# Introduction to 3D + +Creating a 3D game can be challenging. That extra Z coordinate makes +many of the common techniques that helped to make 2D games simpler no +longer work. To aid in this transition, it is worth mentioning that +Redot uses similar APIs for 2D and 3D. Most nodes are the same and +are present in both 2D and 3D versions. In fact, it is worth checking +the 3D platformer tutorial, or the 3D kinematic character tutorials, +which are almost identical to their 2D counterparts. + +
+ An example 3D game demo created using Redot +
+ Redot Third Person Shooter (TPS) Demo, available on the +[Github repository](https://github.com/redot-engine/tps-demo) or the +[Asset Library](doc_project_manager_downloading_demos). +
+
+ +In 3D, math is a little more complex than in 2D. For an introduction to the +relevant math written for game developers, not mathemeticians or engineers, +check out [doc_vector_math](../math/vector_math.md) and [doc_using_transforms](using_transforms.md). + +## 3D workspace + +Editing 3D scenes is done in the 3D workspace. This workspace can be selected +manually, but it will be automatically selected when a Node3D node is +selected. + +![Image](/img/Tutorials/3d/img/redot01.png) + +Similar to 2D, the tabs below the workspace selector are used to change between +currently opened scenes or create a new one using the plus (+) button. The left and +right docks should be familiar from [editor introduction](../editor/index.md). + +Below the scene selector, the main toolbar is visible, and beneath the main toolbar +is the 3D viewport. + +### Main toolbar + +Some buttons in the main toolbar are the same as those in the 2D workspace. A brief explanation +is given with the shortcut if the mouse cursor is hovered over a button for one second. +Some buttons may have additional functionality if another keypress is performed. A recap +of main functionality of each button with its default shortcut is provided below from +left to right: + +![Image](/img/Tutorials/3d/img/redot02.jpg) + +- **Select Mode** (`Q`): Allows selection of nodes in the viewport. Left clicking + on a node to select one. Left clicking and dragging a rectangle selects all + nodes within the rectangle's boundaries, once released. + Holding `Shift` while selecting adds more nodes to the selection. + Clicking on a selected node while holding `Shift` deselects the node. + In this mode, you can use the gizmos to perform movement or rotation. +- **Move Mode** (`W`): Enables move (or translate) mode for the selected nodes. + See [doc_introduction_to_3d_space_and_manipulation](doc_introduction_to_3d_space_and_manipulation) for more details. +- **Rotate Mode** (`E`): Enables rotation mode for the selected nodes. See + [doc_introduction_to_3d_space_and_manipulation](doc_introduction_to_3d_space_and_manipulation) for more details. +- **Scale Mode** (`R`): Enables scaling and displays scaling gizmos in different + axes for the selected nodes. See [doc_introduction_to_3d_space_and_manipulation](doc_introduction_to_3d_space_and_manipulation) + for more details. + +- **Show the list of selectable nodes at the clicked position**: As the description suggests, + this provides a list of selectable nodes at the clicked position as a context menu, + if there is more than one node in the clicked area. +- **Lock** (`Ctrl + L`) the selected nodes, preventing selection and movement in the viewport. + Clicking the button again (or using `Ctrl + Shift + L`) unlocks the selected nodes. + Locked nodes can only be selected in the scene tree. + They can easily be identified with a padlock next to their node names in the scene tree. + Clicking on this padlock also unlocks the nodes. +- **Group selected nodes** (`Ctrl + G`). This allows selection of the root node if + any of the children are selected. + Using `Ctrl + G` ungroups them. Additionally, clicking the ungroup button in + the scene tree performs the same action. +- **Use Local Space** (`T`): If enabled, gizmos of a node are drawn using the current node's + rotation angle instead of the [global viewport axes](doc_introduction_to_3d_coordinate_system). +- **Use Snap** (`Y`): If enabled, movement, and rotation snap to grid. Snapping can also + temporarily be activated using `Ctrl` while performing the action. + The settings for changing snap options are explained below. +- **Project Camera Override**: This action temporarily replaces the active camera in the level + (e.g., the camera following the player) with the camera in the editor's viewport, allowing you + to move freely and inspect the level's different parts, while game is running. +- **Toggle preview sunlight**: If no DirectionalLight3D exist in the scene, a preview + of sunlight can be used as a light source. See + [doc_introduction_to_3d_preview_environment_light](doc_introduction_to_3d_preview_environment_light) for more details. +- **Toggle preview environment**: If no WorldEnvironment exists in the scene, a preview of the + environment can be used as a placeholder. See + [doc_introduction_to_3d_preview_environment_light](doc_introduction_to_3d_preview_environment_light) for more details. +- **Edit Sun and Environment Settings (three dots)**: Opens the menu to configure preview + sunlight and environment settings. See [doc_introduction_to_3d_preview_environment_light](doc_introduction_to_3d_preview_environment_light) + for more details. + +- **Transform menu**: It has three options: + + - *Snap Object to Floor*: Snaps an object to a solid floor. + - *Transform Dialog*: Opens a dialog to adjust transform parameters (translate, rotate, scale, + and transform) manually. + - *Snap Settings*: Allows you to change transform, rotate snap (in degrees), and scale snap + (in percent) settings. + +- **View menu**: Controls the view options and enables additional viewports: + +![Image](/img/Tutorials/3d/img/redot03.png) + +In this menu, you can also show/hide grids, which are set to 1x1 meter by default, +and the origin, where the blue, green, and red axis lines intersect. +Moreover, specific types of gizmos can be toggled in this menu. + +![Image](/img/Tutorials/3d/img/redot04.png) + +An open eye means that the gizmo is visible, a closed eye means it is hidden. +A half-open eye means that it is also visible through opaque surfaces. + +Clicking on *Settings* in this view menu opens a window to change the +*Vertical Field of View (VFOV)* parameter +(in degrees), *Z-Near*, and *Z-Far* values. + +Next to the View menu, additional buttons may be visible. In the toolbar image +at the beginning of this chapter, an additional *Mesh* button appears because a +MeshInstance3D is selected. This menu provides some quick actions or tools to +work on a specific node or selection. + +### View menu of viewport + +Below the *Select* tool, in the 3D viewport, clicking on the three dots opens the +**View menu** for the viewport. +Hiding all shown gizmos in the editor's 3D view can also be performed through +this menu: + +![Image](/img/Tutorials/3d/img/tuto_3d6_1.webp) + +This menu also displays the current view type and enables quick adjustment of the +viewport's viewing angle. Additionally, it offers options to modify the appearance of +nodes within the viewport. + +### Coordinate system + +Redot uses the [metric](https://en.wikipedia.org/wiki/Metric_system) +system for everything in 3D, with 1 unit being equal to 1 meter. +Physics and other areas are tuned for this scale. Therefore, attempting to use a +different scale is usually a bad idea (unless you know what you are doing). + +When working with 3D assets, it's always best to work in the correct scale (set +the unit to metric in your 3D modeling software). Redot allows scaling +post-import and, while this works in most cases, in rare situations it may +introduce floating-point precision issues (and thus, glitches or artifacts) in +delicate areas such as rendering or physics. Make sure your artists always work +in the right scale! + +The Y coordinate is used for "up". As for the horizontal X/Z axes, Redot uses a +**right-handed** coordinate system. This means that for most objects that need +alignment (such as lights or cameras), the Z axis is used as a "pointing +towards" direction. This convention roughly means that: + +- **X** is sides +- **Y** is up/down +- **Z** is front/back + +See this chart for comparison with other 3D software: + +
+ 3D coordinate systems comparison chart +
+ Image by [Freya Holmér](https://twitter.com/FreyaHolmer) +
+
+ +### Space and manipulation gizmos + +Moving, rotating, and scaling objects in the 3D view is done through the +manipulator gizmos. +Each axis is represented by a color: Red, Green, Blue represent X, Y, Z +respectively. This convention applies to the grid and other gizmos too +(and also to the shader language, ordering of components for +Vector3, Color, etc.). + +![Image](/img/Tutorials/3d/img/tuto_3d5.webp) + +Some useful keybindings: + +- To snap placement or rotation, press `Ctrl` while moving, scaling, + or rotating. +- To center the view on the selected object, press `F`. + +In the viewport, the arrows can be clicked and held to move the object on an axis. +The arcs can be clicked and held to rotate the object. +To lock one axis and move the object freely in the other two axes, the colored rectangles +can be clicked, held, and dragged. + +If the transform mode is changed from *Select Mode* to *Scale Mode*, the arrows will be +replaced by cubes, which can be dragged to scale an object as if the object is being moved. + +### Navigating the 3D environment + +In 3D environments, it is often important to adjust the viewpoint or angle +from which you are viewing the scene. +In Redot, navigating the 3D environment in the viewport (or spatial editor) +can be done in multiple ways. + +The default 3D scene navigation controls are similar to Blender (aiming to +have some sort of consistency in the free software pipeline), but +options are included to customize mouse buttons and behavior to be +similar to other tools in the Editor Settings. To change the controls +to Maya or Modo controls, you can navigate to **Editor Settings > Editors > 3D**. +Then, under *Navigation*, search for *Navigation Scheme*. + +![Image](/img/Tutorials/3d/img/tuto_3d4.webp) + +Using the default settings, the following shortcuts control how one can +navigate in the viewport: + +Pressing the middle mouse button and dragging the mouse allows you to orbit around +the center of what is on the screen. + +It is also possible to left-click and hold the manipulator gizmo located +on the top right of the viewport to orbit around the center: + +![Image](/img/Tutorials/3d/img/tuto_3d_gizmo.webp) + +Left-clicking on one of the colored circles will set the view to the chosen +orthogonal and the viewport's view menu will be updated accordingly. + +![Image](/img/Tutorials/3d/img/tuto_3d_updated_view_menu.webp) + +If the *Perspective* view is enabled on the viewport (can be seen on the viewport's View menu, +not the View menu on the main toolbar), holding down the right mouse button on the viewport +or pressing `Shift + F` switches to "free-look" mode. +In this mode you can move the mouse to look around, use the `W` `A` +`S` `D` keys to fly around the view, `E` to go up, and `Q` to +go down. To disable this mode, release the right mouse button or press +`Shift + F` again. + +In the free-look mode, you can temporarily increase the flying +speed using `Shift` or decrease it using `Alt`. To change and keep the +speed modifier use `mouse wheel up` or `mouse wheel down`, to increase or +decrease it, respectively. + +In orthogonal mode, holding the right mouse button will pan the view instead. +Use `Keypad 5` to toggle between perspective and orthogonal view. + +### Using Blender-style transform shortcuts + +Since Redot 4.2, you can enable Blender-style shortcuts for translating, +rotating and scaling nodes. In Blender, these shortcuts are: + +- `G` for translating +- `R` for rotating +- `S` for scaling + +After pressing a shortcut key while focusing on the 3D editor viewport, +move the mouse or enter a number to move the selected node(s) by the +specified amount in 3D units. You can constrain movement to a specific +axis by specifying the axis as a letter, then the distance (if entering a +value with the keyboard). + +For instance, to move the selection upwards by 2.5 units, enter the +following sequence in order (Y+ is upwards in Redot): + +`G`-`Y`-`2`-`.`-`5`-`Enter` + +To use Blender-style transform shortcuts in Redot, go to the Editor Settings' +**Shortcuts** tab, then in the Spatial Editor section: + +- Bind **Begin Translate Transformation** to `G`. +- Bind **Begin Rotate Transformation** to `R`. +- Bind **Begin Scale Transformation** to `S`. +- Finally, unbind **Scale Mode** so that its shortcut won't conflict with + **Begin Rotate Transformation**. + +:::tip +More shortcuts can be found on the +[doc_default_key_mapping_shortcuts_spatial_editor](doc_default_key_mapping_shortcuts_spatial_editor) page. + +::: + +## Node3D node + +[Node2D](class_Node2D) is the base node for 2D. +[Control](class_Control) is the base node for everything GUI. +Following this reasoning, the 3D engine uses the [Node3D](class_Node3D) +node for everything 3D. + +![Image](/img/Tutorials/3d/img/tuto_3d1.webp) + +Node3Ds have a local transform, which is relative to the parent +node (as long as the parent node is also of **or inherits from** the type +Node3D). This transform can be accessed as a 3×4 +[Transform3D](class_Transform3D), or as 3 [Vector3](class_Vector3) +members representing location, Euler rotation (X, Y and Z angles) and +scale. + +![Image](/img/Tutorials/3d/img/tuto_3d2.webp) + +## 3D content + +Unlike 2D, where loading image content and drawing is straightforward, 3D is a +little more difficult. The content needs to be created with special 3D tools +(also called Digital Content Creation tools, or DCCs) and exported to an +exchange file format to be imported in Redot. This is required since 3D formats +are not as standardized as images. + +### Manually authored models (using 3D modeling software) + +It is possible to import 3D models in Redot created in external tools. +Depending on the format, you can import entire scenes (exactly as they look in +the 3D modeling software), including animation, skeletal rigs, blend shapes, or +as simple resources. + +:::info +See :ref:`doc_importing_3d_scenes` for more on importing. + +::: + +### Generated geometry + +It is possible to create custom geometry by using the +[ArrayMesh](class_ArrayMesh) resource directly. Simply create your arrays +and use the [ArrayMesh.add_surface_from_arrays()](class_ArrayMesh_method_add_surface_from_arrays) +function. A helper class is also available, [SurfaceTool](class_SurfaceTool), +which provides a more straightforward API and helpers for indexing, +generating normals, tangents, etc. + +In any case, this method is meant for generating static geometry (models +that will not be updated often), as creating vertex arrays and +submitting them to the 3D API has a significant performance cost. + +:::note +To learn about prototyping inside Redot or using external tools, see +[doc_csg_tools](csg_tools.md). + +::: + +### Immediate geometry + +If, instead, you need to generate simple geometry that will be updated often, +Redot provides a special [ImmediateMesh](class_ImmediateMesh) resource +that can be used in a [MeshInstance3D](class_MeshInstance3D) node. +This provides an OpenGL 1.x-style immediate-mode API to create points, lines, +triangles, etc. + +### 2D in 3D + +While Redot packs a powerful 2D engine, many types of games use 2D in a +3D environment. By using a fixed camera (either orthogonal or +perspective) that does not rotate, nodes such as +[Sprite3D](class_Sprite3D) and +[AnimatedSprite3D](class_AnimatedSprite3D) +can be used to create 2D games that take advantage of mixing with 3D +backgrounds, more realistic parallax, lighting/shadow effects, etc. + +The disadvantage is, of course, that added complexity and reduced +performance in comparison to plain 2D, as well as the lack of reference +of working in pixels. + +## Environment + +Besides editing a scene, it is often common to edit the environment. +Redot provides a [WorldEnvironment](class_WorldEnvironment) +node that allows changing the background color, mode (as in, put a +skybox), and applying several types of built-in post-processing effects. +Environments can also be overridden in the Camera. + +### Preview environment and light + +By default, any 3D scene that doesn't have a [WorldEnvironment](class_WorldEnvironment) +node, or a [DirectionalLight3D](class_DirectionalLight3D), will have +a preview turned on for what it's missing to light the scene. + +The preview light and environment will only be visible in the scene while +in the editor. If you run the scene or export the project they will not +affect the scene. + +The preview light and environment can be turned on or off from the top menu +by clicking on their respective icon. + +![Image](/img/Tutorials/3d/img/tuto_3d8.webp) + + +The three dots dropdown menu next to those icons can be used to adjust the properties +of the preview environment and light if they are enabled. + +![Image](/img/Tutorials/3d/img/tuto_3d9.webp) + +The same preview sun and environment is used for every scene in the same project, +So only make adjustments that would apply to all of the scenes you will need a preview +light and environment for. + +### Cameras + +No matter how many objects are placed in the 3D space, nothing will be +displayed unless a [Camera3D](class_Camera3D) is +also added to the scene. Cameras can work in either orthogonal or +perspective projections: + +![Image](/img/Tutorials/3d/img/tuto_3d10.webp) + +Cameras are associated with (and only display to) a parent or grandparent +viewport. Since the root of the scene tree is a viewport, cameras will +display on it by default, but if sub-viewports (either as render target +or picture-in-picture) are desired, they need their own children cameras +to display. + +![Image](/img/Tutorials/3d/img/tuto_3d11.png) + +When dealing with multiple cameras, the following rules are enforced for +each viewport: + +- If no cameras are present in the scene tree, the first one that + enters it will become the active camera. Further cameras entering the + scene will be ignored (unless they are set as *current*). +- If a camera has the "*current*" property set, it will be used + regardless of any other camera in the scene. If the property is set, + it will become active, replacing the previous camera. +- If an active camera leaves the scene tree, the first camera in + tree-order will take its place. + +### Lights + +The background environment emits some ambient light which appears on surfaces. +Still, without any light sources placed in the scene, the scene will appear +quite dark unless the background environment is very bright. + +Most outdoor scenes have a directional light (the sun or moon), while indoor +scenes typically have several positional lights (lamps, torches, …). +See [doc_lights_and_shadows](lights_and_shadows.md) for more information on setting up lights in Redot. diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/lights_and_shadows.md b/Redot-Documentation/docs/26.1/Tutorials/3d/lights_and_shadows.md new file mode 100644 index 0000000..1b1f339 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/lights_and_shadows.md @@ -0,0 +1,526 @@ + +# 3D lights and shadows + +## Introduction + +Light sources emit light that mixes with the materials and produces a visible +result. Light can come from several types of sources in a scene: + +- From the material itself, in the form of the emission color (though it does + not affect nearby objects unless baked or screen-space indirect lighting is enabled). +- Light nodes: DirectionalLight3D, OmniLight3D and SpotLight3D. +- Ambient light in the [Environment](class_Environment) or + [doc_reflection_probes](doc_reflection_probes). +- Global illumination ([LightmapGI](doc_using_lightmap_gi), + [VoxelGI](doc_using_voxel_gi) or [SDFGI](doc_using_sdfgi)). + +The emission color is a material property. You can read more about it +in the [doc_standard_material_3d](doc_standard_material_3d) tutorial. + +:::info + +You can compare various types of lights in action using the +[3D Lights and Shadows demo project](https://github.com/redot-engine/redot-demo-projects/tree/master/3d/lights_and_shadows). + +::: + +## Light nodes + +There are three types of light nodes: [class_DirectionalLight3D](class_DirectionalLight3D), +[class_OmniLight3D](class_OmniLight3D) and [class_SpotLight3D](class_SpotLight3D). Let's take a look at the common +parameters for lights: + +![Image](/img/Tutorials/3d/img/light_params.png) + +Each property has a specific function: + +- **Color:** Base color for emitted light. +- **Energy:** Energy multiplier. This is useful for saturating lights or working with [doc_high_dynamic_range](doc_high_dynamic_range). +- **Indirect Energy:** Secondary multiplier used with indirect light (light bounces). This works with [doc_using_lightmap_gi](doc_using_lightmap_gi), VoxelGI or SDFGI. +- **Volumetric Fog Energy:** Secondary multiplier used with volumetric fog. This only has an effect when volumetric fog is enabled. +- **Negative:** Light becomes subtractive instead of additive. It's sometimes useful to manually compensate some dark corners. +- **Specular:** Affects the intensity of the specular blob in objects affected by this light. At zero, this light becomes a pure diffuse light. +- **Bake Mode:** Sets the bake mode for the light. See [doc_using_lightmap_gi](doc_using_lightmap_gi). +- **Cull Mask:** Objects that are in the selected layers below will be affected by this light. + Note that objects disabled via this cull mask will still cast shadows. + If you don't want disabled objects to cast shadows, adjust the **Cast Shadow** + property on the GeometryInstance3D to the desired value. + +:::info + +See [doc_physical_light_and_camera_units](doc_physical_light_and_camera_units) if you wish to use real world +units to configure your lights' intensity and color temperature. + +::: + +## Light number limits + +When using the Forward+ renderer, Redot uses a *clustering* approach for +real-time lighting. As many lights as desired can be added (as long as +performance allows). However, there's still a default limit of 512 *clustered +elements* that can be present in the current camera view. A clustered element is +an omni light, a spot light, a [decal](doc_using_decals) or a +[reflection probe](doc_reflection_probes). This limit can be increased by adjusting +[Max Clustered Elements](class_ProjectSettings_property_rendering/limits/cluster_builder/max_clustered_elements) +in **Project Settings > Rendering > Limits > Cluster Builder**. + +When using the Mobile renderer, there is a limitation of 8 OmniLights + 8 SpotLights +per mesh resource. There is also a limit of 256 OmniLights + 256 SpotLights that +can be rendered in the current camera view. These limits currently cannot be changed. + +When using the Compatibility renderer, up to 8 OmniLights + 8 SpotLights can be +rendered per mesh resource. This limit can be increased in the advanced Project +Settings by adjusting +[Max Renderable Elements](class_ProjectSettings_property_rendering/limits/opengl/max_renderable_elements) +and/or [Max Lights per Object](class_ProjectSettings_property_rendering/limits/opengl/max_lights_per_object) +in **Rendering > Limits > OpenGL**, at the cost of performance and longer shader +compilation times. The limit can also be decreased to reduce shader compilation +times and improve performance slightly. + +With all rendering methods, up to 8 DirectionalLights can be visible at a time. +However, each additional DirectionalLight with shadows enabled will reduce the +effective shadow resolution of each DirectionalLight. This is because +directional shadow atlas is shared between all lights. + +If the rendering limit is exceeded, lights will start popping in and out during +camera movement, which can be distracting. Enabling **Distance Fade** on light +nodes can help reduce this issue while also improving performance. Splitting +your meshes into smaller portions can also help, especially for level geometry +(which also improves culling efficiency). + +If you need to render more lights than possible in a given renderer, +consider using [baked lightmaps](doc_using_lightmap_gi) with lights' bake +mode set to **Static**. This allows lights to be fully baked, which also makes +them much faster to render. You can also use emissive materials with any +[global illumination](doc_introduction_to_global_illumination) technique +as a replacement for light nodes that emit light over a large area. + +## Shadow mapping + +Lights can optionally cast shadows. This gives them greater realism (light does +not reach occluded areas), but it can incur a bigger performance cost. +There is a list of generic shadow parameters, each also has a specific function: + +- **Enabled:** Check to enable shadow mapping in this light. +- **Opacity:** Areas occluded are darkened by this opacity factor. Shadows are + fully opaque by default, but this can be changed to make shadows translucent + for a given light. +- **Bias:** When this parameter is too low, self-shadowing occurs. When too + high, shadows separate from the casters. Tweak to what works best for you. +- **Normal Bias:** When this parameter is too low, self-shadowing occurs. When too + high, shadows appear misaligned from the casters. Tweak to what works best for you. +- **Transmittance Bias:** When this parameter is too low, self-shadowing + occurs on materials that have transmittance enabled. When too high, shadows + will not affect materials that have transmittance enabled consistently. Tweak + to what works best for you. +- **Reverse Cull Face:** Some scenes work better when shadow mapping is rendered + with face-culling inverted. +- **Blur:** Multiplies the shadow blur radius for this light. This works with + both traditional shadow mapping and contact-hardening shadows (lights with + **Angular Distance** or **Size** greater than ``0.0``). Higher values result + in softer shadows, which will also appear to be more temporally stable for + moving objects. The downside of increasing shadow blur is that it will make + the grainy pattern used for filtering more noticeable. + See also [doc_lights_and_shadows_shadow_filter_mode](doc_lights_and_shadows_shadow_filter_mode). +- **Caster Mask:** Shadows are only cast by objects in these layers. Note that + this mask does not affect which objects shadows are cast *onto*. + +![Image](/img/Tutorials/3d/img/lights_and_shadows_blur.webp) + +### Tweaking shadow bias + +Below is an image of what tweaking bias looks like. Default values work for most +cases, but in general, it depends on the size and complexity of geometry. + +If the **Shadow Bias** or **Shadow Normal Bias** is set too low for a given light, +the shadow will be "smeared" onto the objects. This will cause the light's +intended appearance to darken, and is called *shadow acne*: + +![Image](/img/Tutorials/3d/img/lights_and_shadows_acne.webp) + +On the other hand, if the **Shadow Bias** or **Shadow Normal Bias** is set too +high for a given light, the shadow may appear to be disconnected from the +object. This is called *peter-panning*: + +![Image](/img/Tutorials/3d/img/lights_and_shadows_peter_panning.webp) + +In general, increasing **Shadow Normal Bias** is preferred over increasing +**Shadow Bias**. Increasing **Shadow Normal Bias** does not cause as much +peter-panning as increasing **Shadow Bias**, but it can still resolve +most shadow acne issues efficiently. The downside of increasing **Shadow Normal +Bias** is that it can make shadows appear thinner for certain objects. + +Any sort of bias issues can be fixed by +[increasing the shadow map resolution](doc_lights_and_shadows_balancing_performance_and_quality), +at the cost of decreased performance. + +:::note + +Tweaking shadow mapping settings is an art – there are no "one size fits +all" settings. To achieve the best visuals, you may need to use different +shadow bias values on a per-light basis. + +::: + +**Note on Appearance Changes**: When enabling shadows on a light, be aware that the light's +appearance might change compared to when it's rendered without shadows in the compatibility +renderer. Due to limitations with older mobile devices, shadows are implemented using a multi-pass +rendering approach so lights with shadows are rendered in sRGB space instead of linear space. +This change in rendering space can sometimes drastically alter the light's appearance. To achieve a similar +appearance to an unshadowed light, you may need to adjust the light's energy setting. + +## Directional light + +This is the most common type of light and represents a light source very far +away (such as the sun). It is also the cheapest light to compute and should be +used whenever possible (although it's not the cheapest shadow-map to compute, +but more on that later). + +Directional light models an infinite number of parallel light rays +covering the whole scene. The directional light node is represented by a big arrow which +indicates the direction of the light rays. However, the position of the node +does not affect the lighting at all and can be anywhere. + +![Image](/img/Tutorials/3d/img/light_directional.png) + +Every face whose front-side is hit by the light rays is lit, while the others +stay dark. Unlike most other light types, directional lights don't have specific +parameters. + +The directional light also offers a **Angular Distance** property, which +determines the light's angular size in degrees. Increasing this above ``0.0`` +will make shadows softer at greater distances from the caster, while also +affecting the sun's appearance in procedural sky materials. This is called a +*contact-hardening* shadow (also known as PCSS). + +For reference, the angular distance of the Sun viewed from the Earth is +approximately ``0.5``. This kind of shadow is expensive, so check the +recommendations in [doc_lights_and_shadows_pcss_recommendations](doc_lights_and_shadows_pcss_recommendations) if setting +this value above ``0.0`` on lights with shadows enabled. + +### Directional shadow mapping + +To compute shadow maps, the scene is rendered (only depth) from an orthogonal +point of view that covers the whole scene (or up to the max distance). There is, +however, a problem with this approach because objects closer to the camera +receive low-resolution shadows that may appear blocky. + +To fix this, a technique named *Parallel Split Shadow Maps* (PSSM) is used. +This splits the view frustum in 2 or 4 areas. Each area gets its own shadow map. +This allows small areas close to the viewer to have the same shadow resolution +as a huge, far-away area. When shadows are enabled for DirectionalLight3D, the +default shadow mode is PSSM with 4 splits. In scenarios where an object is large +enough to appear in all four splits, it results in increased draw calls. Specifically, +such an object will be rendered five times in total: once for each of the four shadow +splits and once for the final scene rendering. This can impact performance, understanding +this behavior is important for optimizing your scene and managing performance expectations. + +![Image](/img/Tutorials/3d/img/lights_and_shadows_pssm_explained.webp) + +With this, shadows become more detailed: + +![Image](/img/Tutorials/3d/img/lights_and_shadows_directional_mode.webp) + +To control PSSM, a number of parameters are exposed: + +![Image](/img/Tutorials/3d/img/lights_and_shadows_directional_shadow_params.webp) + +Each split distance is controlled relative to the camera far (or shadow +**Max Distance** if greater than ``0.0``). ``0.0`` is the eye position and +``1.0`` is where the shadow ends at a distance. Splits are in-between. +Default values generally work well, but tweaking the first split a bit is common +to give more detail to close objects (like a character in a third-person game). + +Always make sure to set a shadow **Max Distance** according to what the scene +needs. A lower maximum distance will result in better-looking shadows and better +performance, as fewer objects will need to be included in shadow rendering. You +can also adjust **Fade Start** to control how aggressive the shadow fade-out +should be at a distance. For scenes where the **Max Distance** fully covers the +scene at any given camera position, you can increase **Fade Start** to ``1.0`` +to prevent the shadow from fading at a distance. This should not be done in +scenes where **Max Distance** doesn't fully cover the scene, as the shadow will +appear to be suddenly cut off at a distance. + +Sometimes, the transition between a split and the next can look bad. To fix +this, the **Blend Splits** option can be turned on, which sacrifices detail and +performance in exchange for smoother transitions: + +![Image](/img/Tutorials/3d/img/blend_splits.png) + +The **Shadow > Normal Bias** parameter can be used to fix special cases of +self-shadowing when objects are perpendicular to the light. The only downside is +that it makes the shadow a bit thinner. Consider increasing **Shadow > Normal +Bias** before increasing **Shadow > Bias** in most situations. + +Lastly, **Pancake Size** is a property that can be adjusted to fix missing +shadows when using large objects with unsubdivided meshes. Only change this +value if you notice missing shadows that are not related to shadow biasing +issues. + +## Omni light + +Omni light is a point source that emits light spherically in all directions up to a given +radius. + +![Image](/img/Tutorials/3d/img/light_omni.png) + +In real life, light attenuation is an inverse function, which means omni lights don't have a radius. +This is a problem because it means computing several omni lights would become demanding. + +To solve this, a **Range** parameter is introduced together with an attenuation function. + +![Image](/img/Tutorials/3d/img/light_omni_params.png) + +These two parameters allow tweaking how this works visually in order to find aesthetically pleasing results. + +![Image](/img/Tutorials/3d/img/light_attenuation.png) + +A **Size** parameter is also available in OmniLight3D. Increasing this value +will make the light fade out slower and shadows appear blurrier when far away +from the caster. This can be used to simulate area lights to an extent. This is +called a *contact-hardening* shadow (also known as PCSS). This kind of shadow is +expensive, so check the recommendations in +[doc_lights_and_shadows_pcss_recommendations](doc_lights_and_shadows_pcss_recommendations) if setting this value above +``0.0`` on lights with shadows enabled. + +![Image](/img/Tutorials/3d/img/lights_and_shadows_pcss.webp) + +### Omni shadow mapping + +Omni light shadow mapping is relatively straightforward. The main issue that +needs to be considered is the algorithm used to render it. + +Omni Shadows can be rendered as either **Dual Paraboloid** or **Cube** mapped. +**Dual Parabolid** renders quickly, but can cause deformations, while **Cube** +is more correct, but slower. The default is **Cube**, but consider changing it +to **Dual Parabolid** for lights where it doesn't make much of a visual +difference. + +![Image](/img/Tutorials/3d/img/lights_and_shadows_dual_parabolid_vs_cubemap.webp) + +If the objects being rendered are mostly irregular and subdivided, Dual +Paraboloid is usually enough. In any case, as these shadows are cached in a +shadow atlas (more on that at the end), it may not make a difference in +performance for most scenes. + +Omni lights with shadows enabled can make use of projectors. The projector +texture will *multiply* the light's color by the color at a given point on the +texture. As a result, lights will usually appear to be darker once a projector +texture is assigned; you can increase **Energy** to compensate for this. + +Omni light projector textures require a special 360° panorama mapping, similar +to [class_PanoramaSkyMaterial](class_PanoramaSkyMaterial) textures. + +With the projector texture below, the following result is obtained: + +![Image](/img/Tutorials/3d/img/lights_and_shadows_omni_projector_example.webp) + +![Image](/img/Tutorials/3d/img/lights_and_shadows_omni_projector.webp) + +:::tip + +If you've acquired omni projectors in the form of cubemap images, you can use +[this web-based conversion tool](https://danilw.github.io/GLSL-howto/cubemap_to_panorama_js/cubemap_to_panorama.html) +to convert them to a single panorama image. + +::: + +## Spot light + +Spot lights are similar to omni lights, except they emit light only into a cone +(or "cutoff"). They are useful to simulate flashlights, +car lights, reflectors, spots, etc. This type of light is also attenuated towards the +opposite direction it points to. + +Spot lights share the same **Range**, **Attenuation** and **Size** as OmniLight3D, +and add two extra parameters: + +- **Angle:** The aperture angle of the light. +- **Angle Attenuation:** The cone attenuation, which helps soften the cone borders. + +### Spot shadow mapping + +Spots feature the same parameters as omni lights for shadow mapping. Rendering +spot shadow maps is significantly faster compared to omni lights, as only one +shadow texture needs to be rendered (instead of rendering 6 faces, or 2 in dual +parabolid mode). + +Spot lights with shadows enabled can make use of projectors. The projector +texture will *multiply* the light's color by the color at a given point on the +texture. As a result, lights will usually appear to be darker once a projector +texture is assigned; you can increase **Energy** to compensate for this. + +Unlike omni light projectors, a spot light projector texture doesn't need to +follow a special format to look correct. It will be mapped in a way similar to a +[decal](doc_using_decals). + +With the projector texture below, the following result is obtained: + +![Image](/img/Tutorials/3d/img/lights_and_shadows_spot_projector_example.webp) + +![Image](/img/Tutorials/3d/img/lights_and_shadows_spot_projector.webp) + +:::note + +Spot lights with wide angles will have lower-quality shadows than spot +lights with narrow angles, as the shadow map is spread over a larger +surface. At angles wider than 89 degrees, spot light shadows will stop +working entirely. If you need shadows for wider lights, use an omni light +instead. + +::: + +## Shadow atlas + +Unlike Directional lights, which have their own shadow texture, omni and spot +lights are assigned to slots of a shadow atlas. This atlas can be configured in +the advanced Project Settings (**Rendering > Lights And Shadows > Positional Shadow**). + +The resolution applies to the whole shadow atlas. This atlas is divided into four quadrants: + +![Image](/img/Tutorials/3d/img/lights_and_shadows_shadow_quadrants.webp) + +Each quadrant can be subdivided to allocate any number of shadow maps; the following is the default subdivision: + +![Image](/img/Tutorials/3d/img/lights_and_shadows_shadow_quadrants2.webp) + +The shadow atlas allocates space as follows: + +- The biggest shadow map size (when no subdivision is used) represents a light the size of the screen (or bigger). +- Subdivisions (smaller maps) represent shadows for lights that are further away from view and proportionally smaller. + +Every frame, the following procedure is performed for all lights: + +1. Check if the light is on a slot of the right size. If not, re-render it and move it to a larger/smaller slot. +2. Check if any object affecting the shadow map has changed. If it did, re-render the light. +3. If neither of the above has happened, nothing is done, and the shadow is left untouched. + +If the slots in a quadrant are full, lights are pushed back to smaller slots, +depending on size and distance. If all slots in all quadrants are full, some +lights will not be able to render shadows even if shadows are enabled on them. + +The default shadow allocation strategy allows rendering up to 88 lights with +shadows enabled in the camera frustum (4 + 4 + 16 + 64): + +1. The first and most detailed quadrant can store 4 shadows. +2. The second quadrant can store 4 other shadows. +3. The third quadrant can store 16 shadows, with less detail. +4. The fourth and least detailed quadrant can store 64 shadows, with even less detail. + +Using a higher number of shadows per quadrant allows supporting a greater amount +of total lights with shadows enabled, while also improving performance (as +shadows will be rendered at a lower resolution for each light). However, +increasing the number of shadows per quadrant comes at the cost of lower shadow +quality. + +In some cases, you may want to use a different allocation strategy. For example, +in a top-down game where all lights are around the same size, you may want to +set all quadrants to have the same subdivision so that all lights have shadows +of similar quality level. + +## Balancing performance and quality + +Shadow rendering is a critical topic in 3D rendering performance. It's important +to make the right choices here to avoid creating bottlenecks. + +Directional shadow quality settings can be changed at runtime by calling the +appropriate [class_RenderingServer](class_RenderingServer) methods. + +Positional (omni/spot) shadow quality settings can be changed at runtime on the +root [class_Viewport](class_Viewport). + +### Shadow map size + +High shadow resolutions result in sharper shadows, but at a significant +performance cost. It should also be noted that *sharper shadows are not always +more realistic*. In most cases, this should be kept at its default value of +``4096`` or decreased to ``2048`` for low-end GPUs. + +If positional shadows become too blurry after decreasing the shadow map size, +you can counteract this by adjusting the +[shadow atlas](doc_lights_and_shadows_shadow_atlas) quadrants to contain +fewer shadows. This will allow each shadow to be rendered at a higher resolution. + +### Shadow filter mode + +Several shadow map quality settings can be chosen here. The default **Soft Low** +is a good balance between performance and quality for scenes with detailed +textures, as the texture detail will help make the dithering pattern less noticeable. + +However, in projects with less detailed textures, the shadow dithering pattern +may be more visible. To hide this pattern, you can either enable +[doc_3d_antialiasing_taa](doc_3d_antialiasing_taa), [doc_3d_antialiasing_fsr2](doc_3d_antialiasing_fsr2), +[doc_3d_antialiasing_fxaa](doc_3d_antialiasing_fxaa), or increase the shadow filter quality to +**Soft Medium** or higher. + +The **Soft Very Low** setting will automatically decrease shadow blur to make +artifacts from the low sample count less visible. Conversely, the **Soft High** +and **Soft Ultra** settings will automatically increase shadow blur to better +make use of the increased sample count. + +![Image](/img/Tutorials/3d/img/lights_and_shadows_filter_quality.webp) + +### 16-bits versus 32-bit + +By default, Redot uses 16-bit depth textures for shadow map rendering. This is +recommended in most cases as it performs better without a noticeable difference +in quality. + +If **16 Bits** is disabled, 32-bit depth textures will be used instead. This +can result in less artifacting in large scenes and large lights with shadows +enabled. However, the difference is often barely visible, yet this can have a +significant performance cost. + +### Light/shadow distance fade + +OmniLight3D and SpotLight3D offer several properties to hide distant lights. +This can improve performance significantly in large scenes with dozens of lights +or more. + +- **Enabled:** Controls whether distance fade (a form of :abbr:`LOD (Level of Detail)`) + is enabled. The light will fade out over **Begin + Length**, after which it + will be culled and not sent to the shader at all. Use this to reduce the number + of active lights in a scene and thus improve performance. +- **Begin:** The distance from the camera at which the light begins to fade away + (in 3D units). +- **Shadow:** The distance from the camera at which the shadow begins to fade away + (in 3D units). This can be used to fade out shadows sooner compared to the light, + further improving performance. Only available if shadows are enabled for the light. +- **Length:** The distance over which the light and shadow fades (in 3D units). + The light becomes slowly more transparent over this distance and is completely + invisible at the end. Higher values result in a smoother fade-out transition, + which is more suited when the camera moves fast. + +### PCSS recommendations + +Percentage-closer soft shadows (PCSS) provide a more realistic shadow mapping +appearance, with the penumbra size varying depending on the distance between the +caster and the surface receiving the shadow. This comes at a high performance +cost, especially for directional lights. + +To avoid performance issues, it's recommended to: + +- Only use a handful of lights with PCSS shadows enabled at a given time. The + effect is generally most visible on large, bright lights. Secondary light + sources that are more faint usually don't benefit much from using PCSS + shadows. +- Provide a setting for users to disable PCSS shadows. On directional lights, + this can be done by setting the DirectionalLight3D's + ``light_angular_distance`` property to ``0.0`` in a script. On positional + lights, this can be done by setting the OmniLight3D or SpotLight3D's + ``light_size`` property to ``0.0`` in a script. + +### Projector filter mode + +The way projectors are rendered also has an impact on performance. The +**Rendering > Textures > Light Projectors > Filter** advanced project setting +lets you control how projector textures should be filtered. **Nearest/Linear** do +not use mipmaps, which makes them faster to render. However, projectors will +look grainy at distance. **Nearest/Linear Mipmaps** will look smoother at a +distance, but projectors will look blurry when viewed from oblique angles. This +can be resolved by using **Nearest/Linear Mipmaps Anisotropic**, which is the +highest-quality mode, but also the most expensive. + +If your project has a pixel art style, consider setting the filter to one of the +**Nearest** values so that projectors use nearest-neighbor filtering. Otherwise, +stick to **Linear**. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/mesh_lod.md b/Redot-Documentation/docs/26.1/Tutorials/3d/mesh_lod.md new file mode 100644 index 0000000..8f4fb36 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/mesh_lod.md @@ -0,0 +1,218 @@ + +# Mesh level of detail (LOD) + +Level of detail (LOD) is one of the most important ways to optimize rendering +performance in a 3D project, along with [doc_occlusion_culling](occlusion_culling.md). + +On this page, you'll learn: + +- How mesh LOD can improve your 3D project's rendering performance. +- How to set up mesh LOD in Redot. +- How to measure mesh LOD's effectiveness in your project + (and alternatives you can explore if it doesn't meet your expectations). + +:::info + +You can see how mesh LOD works in action using the +[Occlusion Culling and Mesh LOD demo project](https://github.com/redot-engine/redot-demo-projects/tree/master/3d/occlusion_culling_mesh_lod). + +::: + +## Introduction + +Historically, level of detail in 3D games involved manually authoring meshes +with lower geometry density, then configuring the distance thresholds at which +these lower-detailed meshes should be drawn. This approach is still used today +when increased control is needed. + +However, in projects that have a large amount of detailed 3D assets, setting up +LOD manually can be a very time-consuming process. As a result, automatic mesh +decimation and LOD configuration is becoming increasingly popular. + +Redot provides a way to automatically generate less detailed meshes for LOD +usage on import, then use those LOD meshes when needed automatically. This is +completely transparent to the user. +The [meshoptimizer](https://meshoptimizer.org/) library is used for LOD mesh +generation behind the scenes. + +Mesh LOD works with any node that draws 3D meshes. This includes MeshInstance3D, +MultiMeshInstance3D, GPUParticles3D and CPUParticles3D. + +## Visual comparison + +Here is an example of LOD meshes generated on import. Lower detailed meshes +will be used when the camera is far away from the object: + +
+ From most detailed (left) to least detailed (right), shaded view +
+ From most detailed (left) to least detailed (right), shaded view +
+
+ +Here's the same image with wireframe rendering to make the decimation easier to see: + +
+ From most detailed (left) to least detailed (right), wireframe view +
+ From most detailed (left) to least detailed (right), wireframe view +
+
+ +:::info + +If you need to manually configure level of detail with artist-created meshes, +use [doc_visibility_ranges](visibility_ranges.md) instead of automatic mesh LOD. + +::: + +## Generating mesh LOD + +By default, mesh LOD generation happens automatically for imported 3D scenes +(glTF, .blend, Collada, FBX). Once LOD meshes are generated, they will +automatically be used when rendering the scene. You don't need to configure +anything manually. + +However, mesh LOD generation does **not** automatically happen for imported 3D +meshes (OBJ). This is because OBJ files are not imported as full 3D scenes by +default, but only as individual mesh resources to load into a MeshInstance3D +node (or GPUParticles3D, CPUParticles3D, ...). + +To make an OBJ file have mesh LOD generated for it, select it in the FileSystem +dock, go to the Import dock, change its **Import As** option to **Scene** then +click **Reimport**: + +
+ Changing the import type on an OBJ file in the Import dock +
+ Changing the import type on an OBJ file in the Import dock +
+
+ +This will require restarting the editor after clicking **Reimport**. + +:::note + +The mesh LOD generation process is not perfect, and may occasionally +introduce rendering issues (especially in skinned meshes). Mesh LOD +generation can also take a while on complex meshes. + +If mesh LOD causes a specific mesh to look broken, you can disable LOD +generation for it in the Import dock. This will also speed up resource +importing. This can be done globally in the 3D scene's import options, or on +a per-mesh basis using the Advanced Import Settings dialog. + +See [Importing 3D scenes](doc_importing_3d_scenes_using_the_import_dock) +for more information. + +::: + +## Comparing mesh LOD visuals and performance + +To disable mesh LOD in the editor for comparison purposes, use the +**Disable Mesh LOD** advanced debug draw mode. This can be done using the menu +in the top-left corner of the 3D viewport (labeled **Perspective** or +**Orthogonal** depending on camera mode): + +
+ Disabling mesh LOD in the 3D viewport's top-left menu +
+ Disabling mesh LOD in the 3D viewport's top-left menu +
+
+ +Enable **View Frame Time** in the same menu to view FPS in the top-right corner. +Also enable **View Information** in the same menu to view the number of primitives +(vertices + indices) rendered in the bottom-right corner. + +If mesh LOD is working correctly in your scene and your camera is far away +enough from the mesh, you should notice the number of drawn primitives +decreasing and FPS increasing when mesh LOD is left enabled (unless you are +CPU-bottlenecked). + +To see mesh LOD decimation in action, change the debug draw mode to +**Display Wireframe** in the menu specified above, then adjust the +**Rendering > Mesh LOD > LOD Change > Threshold Pixels** project setting. + +## Configuring mesh LOD performance and quality + +You can adjust how aggressive mesh LOD transitions should be in the root viewport +by changing the **Rendering > Mesh LOD > LOD Change > Threshold Pixels** project +setting. To change this value at runtime, set ``mesh_lod_threshold`` on the +root viewport as follows: + + + + + +```gdscript +get_tree().root.mesh_lod_threshold = 4.0 + +``` + + + + + +```csharp +GetTree().Root.MeshLodThreshold = 4.0f; + +``` + + + + + +Each viewport has its own ``mesh_lod_threshold`` property, which can be set +independently from other viewports. + +The default mesh LOD threshold of 1 pixel is tuned to look *perceptually* +lossless; it provides a significant performance gain with an unnoticeable loss +in quality. Higher values will make LOD transitions happen sooner when the +camera moves away, resulting in higher performance, but lower quality. + +If you need to perform per-object adjustments to mesh LOD, you can adjust how +aggressive LOD transitions should be by adjusting the **LOD Bias** property on +any node that inherits from GeometryInstance3D. Values *above* ``1.0`` will make +LOD transitions happen later than usual (resulting in higher quality, but lower +performance). Values *below* ``1.0`` will make LOD transitions happen sooner than +usual (resulting in lower quality, but higher performance). + +Additionally, ReflectionProbe nodes have their own **Mesh LOD Threshold** property +that can be adjusted to improve rendering performance when the reflection probe +updates. This is especially important for ReflectionProbes that use the **Always** +update mode. + +:::note + +When rendering the scene, mesh LOD selection uses a screen-space metric. +This means it automatically takes camera field of view and viewport +resolution into account. Higher camera FOV and lower viewport resolutions +will make LOD selection more aggressive; the engine will display heavily +decimated models earlier when the camera moves away. + +As a result, unlike [doc_visibility_ranges](visibility_ranges.md), you don't need to do +anything specific in your project to take camera FOV and viewport resolution +into account. + +::: + +## Using mesh LOD with MultiMesh and particles + +For LOD selection, the point of the node's :abbr:`AABB (Axis-Aligned Bounding Box)` +that is the closest to the camera is used as a basis. This applies to any kind +of mesh LOD (including for individual MeshInstance3D)s, but this has some implications +for nodes that display multiple meshes at once, such as MultiMeshInstance3D, +GPUParticles3D and GPUParticles3D. Most importantly, this means that all +instances will be drawn with the same LOD level at a given time. + +If you are noticing incorrect LOD selection with GPUParticles3D, make sure +the node's visibility AABB is configured by selecting the GPUParticles3D +node and using **GPUParticles3D > Generate AABB** at the top of the 3D +viewport. + +If you have instances in a MultiMesh that are far away from each other, they +should be placed in a separate MultiMeshInstance3D node. Doing so will also +improve rendering performance, as frustum and occlusion culling will be able to +cull individual nodes (while they can't cull individual instances in a +MultiMesh). \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/occlusion_culling.md b/Redot-Documentation/docs/26.1/Tutorials/3d/occlusion_culling.md new file mode 100644 index 0000000..901c4b8 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/occlusion_culling.md @@ -0,0 +1,327 @@ + +# Occlusion culling + +In a 3D rendering engine, **occlusion culling** is the process of performing +hidden geometry removal. + +On this page, you'll learn: + +- What are the advantages and pitfalls of occlusion culling. +- How to set up occlusion culling in Redot. +- Troubleshooting common issues with occlusion culling. + +:::info + +You can see how occlusion culling works in action using the +[Occlusion Culling and Mesh LOD demo project](https://github.com/redot-engine/redot-demo-projects/tree/master/3d/occlusion_culling_mesh_lod). + +::: + +## Why use occlusion culling + +In this example scene with hundreds of rooms stacked next to each other, a +dynamic object (red sphere) is hidden behind the wall in the lit room (on the +left of the door): + +
+ Example scene with an occlusion culling-friendly layout +
+ Example scene with an occlusion culling-friendly layout +
+
+ +With occlusion culling disabled, all the rooms behind the lit room have to be +rendered. The dynamic object also has to be rendered: + +
+ Example scene with occlusion culling disabled (wireframe) +
+ Example scene with occlusion culling **disabled** (wireframe) +
+
+ +With occlusion culling enabled, only the rooms that are actually visible have to +be rendered. The dynamic object is also occluded by the wall, and therefore no +longer has to be rendered: + +
+ Example scene with occlusion culling enabled (wireframe) +
+ Example scene with occlusion culling **enabled** (wireframe) +
+
+ +Since the engine has less work to do (fewer vertices to render and fewer draw calls), +performance will increase as long as there are enough occlusion culling opportunities +in the scene. This means occlusion culling is most effective in indoor scenes, +preferably with many smaller rooms instead of fewer larger rooms. Combine +this with [doc_mesh_lod](mesh_lod.md) and [doc_visibility_ranges](visibility_ranges.md) to further improve +performance gains. + +:::note + +When using the Forward+ renderer, the engine already +performs a *depth prepass*. This consists in rendering a depth-only version +of the scene before rendering the scene's actual materials. This is used to +ensure each opaque pixel is only shaded once, reducing the cost of overdraw +significantly. + +The greatest performance benefits can be observed when using the Mobile +renderer, as it does not feature a depth prepass for performance reasons. As +a result, occlusion culling will actively decrease shading overdraw with +that renderer. + +Nonetheless, even when using a depth prepass, there is still a noticeable +benefit to occlusion culling in complex 3D scenes. However, in scenes with +few occlusion culling opportunities, occlusion culling may not be worth the +added setup and CPU usage. + +::: + +## How occlusion culling works in Redot + +:::note + +"occluder" refers to the shape blocking the view, while "occludee" refers to +the object being hidden. + +::: + +In Redot, occlusion culling works by rasterizing the scene's occluder geometry +to a low-resolution buffer on the CPU. This is done using +the software raytracing library [Embree](https://github.com/embree/embree). + +The engine then uses this low-resolution buffer to test the occludee's +:abbr:`AABB (Axis-Aligned Bounding Box)` against the occluder shapes. +The occludee's :abbr:`AABB (Axis-Aligned Bounding Box)` must be *fully occluded* +by the occluder shape to be culled. + +As a result, smaller objects are more likely to be effectively culled than +larger objects. Larger occluders (such as walls) also tend to be much more +effective than smaller ones (such as decoration props). + +## Setting up occlusion culling + +The first step to using occlusion culling is to enable the +**Rendering > **Occlusion Culling > Use Occlusion Culling** project setting. +(Make sure the **Advanced** toggle is enabled in the Project Settings dialog to +be able to see it.) + +This project setting applies immediately, so you don't need to restart the editor. + +After enabling the project setting, you still need to create some occluders. For +performance reasons, the engine doesn't automatically use all visible geometry +as a basis for occlusion culling. Instead, the engine requires a simplified +representation of the scene with only static objects to be baked. + +There are two ways to set up occluders in a scene: + +### Automatically baking occluders (recommended) + +:::note + +Only MeshInstance3D nodes are currently taken into account in the *occluder* +baking process. MultiMeshInstance3D, GPUParticles3D, CPUParticles3D and CSG +nodes are **not** taken into account when baking occluders. If you wish +those to be treated as occluders, you have to manually create occluder +shapes that (roughly) match their geometry. + +Since Redot 4.4, CSG nodes can be taken into account in the baking process if they are +[converted to a MeshInstance3D](doc_csg_tools_converting_to_mesh_instance_3d) +before baking occluders. + +This restriction does not apply to *occludees*. Any node type that inherits +from GeometryInstance3D can be occluded. + +::: + +After enabling the occlusion culling project setting mentioned above, add an +OccluderInstance3D node to the scene containing your 3D level. + +Select the OccluderInstance3D node, then click **Bake Occluders** at the top of +the 3D editor viewport. After baking, the OccluderInstance3D node will contain +an Occluder3D resource that stores a simplified version of your level's +geometry. This occluder geometry appears as purple wireframe lines in the 3D view +(as long as **View Gizmos** is enabled in the **Perspective** menu). +This geometry is then used to provide occlusion culling for both static and +dynamic occludees. + +After baking, you may notice that your dynamic objects (such as the player, +enemies, etc…) are included in the baked mesh. To prevent this, set the +**Bake > Cull Mask** property on the OccluderInstance3D to exclude certain visual +layers from being baked. + +For example, you can disable layer 2 on the cull mask, then configure your +dynamic objects' MeshInstance3D nodes to be located on the visual layer 2 +(instead of layer 1). To do so, select the MeshInstance3D node in question, then +on the **VisualInstance3D > Layers** property, uncheck layer 1 then check layer +2. After configuring both cull mask and layers, bake occluders again by +following the above process. + +### Manually placing occluders + +This approach is more suited for specialized use cases, such as creating occlusion +for MultiMeshInstance3D setups or CSG nodes (due to the aforementioned limitation). + +After enabling the occlusion culling project setting mentioned above, add an +OccluderInstance3D node to the scene containing your 3D level. Select the +OccluderInstance3D node, then choose an occluder type to add in the **Occluder** +property: + +- QuadOccluder3D (a single plane) +- BoxOccluder3D (a cuboid) +- SphereOccluder3D (a sphere-shaped occluder) +- PolygonOccluder3D (a 2D polygon with as many points as you want) + +There is also ArrayOccluder3D, whose points can't be modified in the editor but +can be useful for procedural generation from a script. + +## Previewing occlusion culling + +You can enable a debug draw mode to preview what the occlusion culling is +actually "seeing". In the top-left corner of the 3D editor viewport, click the +**Perspective** button (or **Orthogonal** depending on your current camera +mode), then choose **Display Advanced… > Occlusion Culling Buffer**. This will +display the low-resolution buffer that is used by the engine for occlusion +culling. + +In the same menu, you can also enable **View Information** and **View Frame +Time** to view the number of draw calls and rendered primitives (vertices + +indices) in the bottom-right corner, along with the number of frames per second +rendered in the top-right corner. + +If you toggle occlusion culling in the project settings while this information +is displayed, you can see how much occlusion culling improves performance in +your scene. Note that the performance benefit highly depends on the 3D editor +camera's view angle, as occlusion culling is only effective if there are +occluders in front of the camera. + +To toggle occlusion culling at runtime, set ``use_occlusion_culling`` on the +root viewport as follows: + + + + + +```gdscript +get_tree().root.use_occlusion_culling = true + +``` + + + + + +```csharp +GetTree().Root.UseOcclusionCulling = true; + +``` + + + + + +Toggling occlusion culling at runtime is useful to compare performance on a +running project. + +## Performance considerations + +### Design your levels to take advantage of occlusion culling + +**This is the most important guideline.** A good level design is not just about +what the gameplay demands; it should also be built with occlusion in mind. + +For indoor environments, add opaque walls to "break" the line of sight at +regular intervals and ensure not too much of the scene can be seen at once. + +For large open scenes, use a pyramid-like structure for the terrain's elevation +when possible. This provides the greatest culling opportunities compared to any +other terrain shape. + +### Avoid moving OccluderInstance3D nodes during gameplay + +This includes moving the parents of OccluderInstance3D nodes, as this will cause +the nodes themselves to move in global space, therefore requiring the :abbr:`BVH +(Bounding Volume Hierarchy)` to be rebuilt. + +Toggling an OccluderInstance3D's visibility (or one of its parents' visibility) +is not as expensive, as the update only needs to happen once (rather than +continuously). + +For example, if you have a sliding or rotating door, you can make the +OccluderInstance3D node not be a child of the door itself (so that the occluder +never moves), but you can hide the OccluderInstance3D visibility once the door +starts opening. You can then reshow the OccluderInstance3D once the door is +fully closed. + +If you absolutely have to move an OccluderInstance3D node during gameplay, use a +primitive Occluder3D shape for it instead of a complex baked shape. + +### Use the simplest possible occluder shapes + +If you notice low performance or stuttering in complex 3D scenes, it may mean +that the CPU is overloaded as a result of rendering detailed occluders. +Select the OccluderInstance3D node, +increase the **Bake > Simplification** property then bake occluders again. + +Remember to keep the simplification value reasonable. Values that are too high +for the level's geometry may cause incorrect occlusion culling to occur, as in +[doc_occlusion_culling_troubleshooting_false_negative](doc_occlusion_culling_troubleshooting_false_negative). + +If this still doesn't lead to low enough CPU usage, +you can try adjusting the **Rendering > Occlusion Culling > BVH Build Quality** +project setting and/or decreasing +**Rendering > Occlusion Culling > Occlusion Rays Per Thread**. +You'll need to enable the **Advanced** toggle in the Project Settings dialog to +see those settings. + +## Troubleshooting + +### My occludee isn't being culled when it should be + +**On the occluder side:** + +First, double-check that the **Bake > Cull Mask** property in the +OccluderInstance3D is set to allow baking the meshes you'd like. The visibility +layer of the MeshInstance3D nodes must be present within the cull mask for the +mesh to be included in the bake. + +Also note that occluder baking only takes meshes with *opaque* materials into +account. Surfaces will *transparent* materials will **not** be included in the +bake, even if the texture applied on them is fully opaque. + +Lastly, remember that MultiMeshInstance3D, GPUParticles3D, CPUParticles3D and CSG +nodes are **not** taken into account when baking occluders. As a workaround, you +can add OccluderInstance3D nodes for those manually. + +**On the occludee side:** + +Make sure **Extra Cull Margin** is set as low as possible (it should usually be +``0.0``), and that **Ignore Occlusion Culling** is disabled in the object's +GeometryInstance3D section. + +Also, check the AABB's size (which is represented by an orange box when +selecting the node). This axis-aligned bounding box must be *fully* occluded by +the occluder shapes for the occludee to be hidden. + +### My occludee is being culled when it shouldn't be + +The most likely cause for this is that objects that were included in the +occluder bake have been moved after baking occluders. For instance, this can +occur when moving your level geometry around or rearranging its layout. To fix +this, select the OccluderInstance3D node and bake occluders again. + +This can also happen because dynamic objects were included in the bake, even +though they shouldn't be. Use the +[occlusion culling debug draw mode](doc_occlusion_culling_preview) to look +for occluder shapes that shouldn't be present, then +[adjust the bake cull mask accordingly](doc_occlusion_culling_baking). + +The last possible cause for this is overly aggressive mesh simplification during +the occluder baking process. Select the OccluderInstance3D node, +decrease the **Bake > Simplification** property then bake occluders again. + +As a last resort, you can enable the **Ignore Occlusion Culling** property on +the occludee. This will negate the performance improvements of occlusion culling +for that object, but it makes sense to do this for objects that will never be +culled (such as a first-person view model). \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/particles/attractors.md b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/attractors.md new file mode 100644 index 0000000..43f1cc7 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/attractors.md @@ -0,0 +1,165 @@ + +## 3D Particle attractors + +![Image](/img/Tutorials/3d/particles/img/particle_attractor.webp) + +Particle attractors are nodes that apply a force to all particles within their reach. They pull +particles closer or push them away based on the direction of that force. There are three types +of attractors: [class_GPUParticlesAttractorBox3D](class_GPUParticlesAttractorBox3D), [class_GPUParticlesAttractorSphere3D](class_GPUParticlesAttractorSphere3D), +and [class_GPUParticlesAttractorVectorField3D](class_GPUParticlesAttractorVectorField3D). You can instantiate them at runtime and +change their properties from gameplay code; you can even animate and combine them for complex +attraction effects. + +:::note + +Particle attractors are not yet implemented for 2D particle systems. + +::: + +The first thing you have to do if you want to use attractors is enable the ``Attractor Interaction`` +property on the ParticleProcessMaterial. Do this for every particle system that needs to react to attractors. +Like most properties in Redot, you can also change this at runtime. + +### Common properties + +![Image](/img/Tutorials/3d/particles/img/particle_attractor_common.webp) + + Common attractor properties + +There are some properties that you can find on all attractors. They're located in the +``GPUParticlesAttractor3D`` section in the inspector. + +``Strength`` controls how strong the attractor force is. A positive value pulls particles +closer to the attractor's center, while a negative value pushes them away. + +``Attenuation`` controls the strength falloff within the attractor's influence region. Every +particle attractor has a boundary. Its strength is weakest at the border of this boundary +and strongest at its center. Particles outside of the boundary are not affected by the attractor +at all. The attenuation curve controls how the strength weakens over that distance. A straight +line means that the strength is proportional to the distance: if a particle is halfway +between the boundary and the center, the attractor strength will be half of what it is +at the center. Different curve shapes change how fast particles accelerate towards the +attractor. + +![Image](/img/Tutorials/3d/particles/img/particle_attractor_curve.webp) + + Strength increase variations: constantly over the distance to the attractor (left), fast + at the boundary border and slowly at the center (middle), slowly at the boundary and + fast at the center (right). + +The ``Directionality`` property changes the direction towards which particles are pulled. +At a value of ``0.0``, there is no directionality, which means that particles are pulled towards +the attractor's center. At ``1.0``, the attractor is fully directional, which means particles +will be pulled along the attractor's local ``-Z``-axis. You can change the global direction +by rotating the attractor. If ``Strength`` is negative, particles are instead pulled along +the ``+Z``-axis. + +![Image](/img/Tutorials/3d/particles/img/particle_attractor_direction.webp) + + No directionality (left) vs. full directionality (right). Notice how the particles move along + the attractor's local Z-axis. + +The ``Cull Mask`` property controls which particle systems are affected by an attractor based +on each system's [visibility layers ](class_VisualInstance3D). A particle system is only +affected by an attractor if at least one of the system's visibility layers is enabled in the +attractor's cull mask. + +:::warning + +There is a [known issue ](https://github.com/redot-engine/redot-engine/issues/61014) with +GPU particle attractors that prevent the cull mask from working properly in Redot 4.0. We will +update the documentation as soon as it is fixed. + +::: + +### Box attractors + +![Image](/img/Tutorials/3d/particles/img/particle_attractor_box_entry.webp) + + Box attractor in the node list + +Box attractors have a box-shaped influence region. You control their size with the ``Extents`` +property. Box extents always measure half of the sides of its bounds, so a value of +``(X=1.0,Y=1.0,Z=1.0)`` creates a box with an influence region that is 2 meters wide on each side. + +To create a box attractor, add a new child node to your scene and select ``GPUParticlesAttractorBox3D`` +from the list of available nodes. You can animate the box position or attach it to a +moving node for more dynamic effects. + +![Image](/img/Tutorials/3d/particles/img/particle_attractor_box.webp) + + A box attractor with a negative strength value parts a particle field as it moves through it. + +### Sphere attractors + +![Image](/img/Tutorials/3d/particles/img/particle_attractor_sphere_entry.webp) + + Sphere attractor in the node list + +Sphere attractors have a spherical influence region. You control their size with the ``Radius`` +property. While box attractors don't have to be perfect cubes, sphere attractors will always be +spheres: You can't set width independently from height. If you want to use a sphere attractor for +elongated shapes, you have to change its ``Scale`` in the attractor's ``Node3D`` section. + +To create a sphere attractor, add a new child node to your scene and select ``GPUParticlesAttractorSphere3D`` +from the list of available nodes. You can animate the sphere position or attach it to a +moving node for more dynamic effects. + +![Image](/img/Tutorials/3d/particles/img/particle_attractor_sphere.webp) + + A sphere attractor with a negative strength value parts a particle field as it moves through it. + +### Vector field attractors + +![Image](/img/Tutorials/3d/particles/img/particle_attractor_vector_entry.webp) + + Vector field attractor in the node list + +A vector field is a 3D area that contains vectors positioned on a grid. The grid density controls +how many vectors there are and how far they're spread apart. Each vector in a vector field points +in a specific direction. This can be completely random or aligned in a way that forms distinct +patterns and paths. + +When particles interact with a vector field, their movement direction changes to match the nearest vector +in the field. As a particle moves closer to the next vector in the field, it changes +direction to match that vector's direction. The particle's speed depends on the vector's length. + +Like box attractors, vector field attractors have a box-shaped influence region. You control their size with the ``Extents`` +property, where a value of ``(X=1.0,Y=1.0,Z=1.0)`` creates a box with an influence region that is +2 meters wide on each side. The ``Texture`` property takes a [3D texture ](class_Texture3D) +where every pixel represents a vector with the pixel's color interpreted as the vector's direction and size. + +:::note + +When a texture is used as a vector field, there are two types of conversion you need to be aware of: + +1. The texture coordinates map to the attractor bounds. The image below shows which part of the texture + corresponds to which part of the vector field volume. For example, the bottom half of the texture + affects the top half of the vector field attractor because ``+Y`` points down in the texture UV space, + but up in Redot's world space. +2. The pixel color values map to direction vectors in space. The image below provides an overview. Since + particles can move in two directions along each axis, the lower half of the color range represents + negative direction values while the upper half represents positive direction values. So a yellow pixel + ``(R=1,G=1,B=0)`` maps to the vector ``(X=1,Y=1,Z=-1)`` while a neutral gray ``(R=0.5,G=0.5,B=0.5)`` + results in no movement at all. + +![Image](/img/Tutorials/3d/particles/img/particle_attractor_vector_mapping.webp) + +::: + +To create a vector field attractor, add a new child node to your scene and select ``GPUParticlesAttractorVectorField3D`` +from the list of available nodes. You can animate the attractor's position or attach it to a +moving node for more dynamic effects. + +:::tip + +If you don't have external tools to create vector field textures, you can use +a NoiseTexture3D with a Color Ramp attached as a vector field texture. The +Color Ramp can be modified to adjust how much each coordinate is affected by +the vector field. + +::: + +![Image](/img/Tutorials/3d/particles/img/particle_attractor_vector.webp) + + Two particle systems are affected by the same vector field attractor. :download:`Click here to download the 3D texture <img/particle_vector_field_16x16x16.bmp>`. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/particles/collision.md b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/collision.md new file mode 100644 index 0000000..f04822a --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/collision.md @@ -0,0 +1,172 @@ + +## 3D Particle collisions + +![Image](/img/Tutorials/3d/particles/img/particle_collision.webp) + +Since GPU particles are processed entirely on the GPU, they don't have access to the game's physical +world. If you need particles to collide with the environment, you have to set up particle collision nodes. +There are four of them: [class_GPUParticlesCollisionBox3D](class_GPUParticlesCollisionBox3D), [class_GPUParticlesCollisionSphere3D](class_GPUParticlesCollisionSphere3D), +[class_GPUParticlesCollisionSDF3D](class_GPUParticlesCollisionSDF3D), and [class_GPUParticlesCollisionHeightField3D](class_GPUParticlesCollisionHeightField3D). + +### Common properties + +![Image](/img/Tutorials/3d/particles/img/particle_collision_common.webp) + + Common collision properties + +There are some properties that you can find on all collision nodes. They're located in the +``GPUParticlesCollision3D`` section in the inspector. + +The ``Cull Mask`` property controls which particle systems are affected by a collision node based +on each system's [visibility layers ](class_VisualInstance3D). A particle system collides with a +collision node only if at least one of the system's visibility layers is enabled in the +collider's cull mask. + +:::warning + +There is a [known issue ](https://github.com/redot-engine/redot-engine/issues/61014) with +GPU particle collision that prevent the cull mask from working properly in Redot 4.0. We will +update the documentation as soon as it is fixed. + +::: + +### Box collision + +![Image](/img/Tutorials/3d/particles/img/particle_collision_box_entry.webp) + + Box collision in the node list + +Box collision nodes are shaped like a solid, rectangular box. You control their size with the ``Extents`` +property. Box extents always measure half of the sides of its bounds, so a value of ``(X=1.0,Y=1.0,Z=1.0)`` +creates a box that is 2 meters wide on each side. Box collision nodes are useful for simulating floor +and wall geometry that particles should collide against. + +To create a box collision node, add a new child node to your scene and select ``GPUParticlesCollisionBox3D`` +from the list of available nodes. You can animate the box position or attach it to a +moving node for more dynamic effects. + +![Image](/img/Tutorials/3d/particles/img/particle_collision_box.webp) + + Two particle systems collide with a box collision node + +### Sphere collision + +![Image](/img/Tutorials/3d/particles/img/particle_collision_sphere_entry.webp) + + Sphere collision in the node list + +Sphere collision nodes are shaped like a solid sphere. The ``Radius`` property controls the size of the sphere. +While box collision nodes don't have to be perfect cubes, sphere collision nodes will always be +spheres. If you want to set width independently from height, you have to change the ``Scale`` +property in the ``Node3D`` section. + +To create a sphere collision node, add a new child node to your scene and select ``GPUParticlesCollisionSphere3D`` +from the list of available nodes. You can animate the sphere's position or attach it to a +moving node for more dynamic effects. + +![Image](/img/Tutorials/3d/particles/img/particle_collision_sphere.webp) + + Two particle systems collide with a sphere collision node + +### Height field collision + +![Image](/img/Tutorials/3d/particles/img/particle_collision_height.webp) + + Height field collision in the node list + +Height field particle collision is very useful for large outdoor areas that need to collide with particles. +At runtime, the node creates a height field from all the meshes within its bounds that match its cull mask. +Particles collide against the mesh that this height field represents. Since the height field generation is +done dynamically, it can follow the player camera around and react to changes in the level. Different +settings for the height field density offer a wide range of performance adjustments. + +To create a height field collision node, add a new child node to your scene and select ``GPUParticlesCollisionHeightField3D`` +from the list of available nodes. + +A height field collision node is shaped like a box. The ``Extents`` property controls its size. Extents +always measure half of the sides of its bounds, so a value of ``(X=1.0,Y=1.0,Z=1.0)`` creates a box that +is 2 meters wide on each side. Anything outside of the node's extents is ignored for height field creation. + +The ``Resolution`` property controls how detailed the height field is. A lower resolution performs faster +at the cost of accuracy. If the height field resolution is too low, it may look like particles penetrate level geometry +or get stuck in the air during collision events. They might also ignore some smaller meshes completely. + +![Image](/img/Tutorials/3d/particles/img/particle_heightfield_res.webp) + + At low resolutions, height field collision misses some finer details (left) + +The ``Update Mode`` property controls when the height field is recreated from the meshes within its +bounds. Set it to ``When Moved`` to make it refresh only when it moves. This performs well and is +suited for static scenes that don't change very often. If you need particles to collide with dynamic objects +that change position frequently, you can select ``Always`` to refresh every frame. This comes with a +cost to performance and should only be used when necessary. + +:::note + +It's important to remember that when ``Update Mode`` is set to ``When Moved``, it is the *height field node* +whose movement triggers an update. The height field is not updated when one of the meshes inside it moves. + +::: + +The ``Follow Camera Enabled`` property makes the height field follow the current camera when enabled. It will +update whenever the camera moves. This property can be used to make sure that there is always particle collision +around the player while not wasting performance on regions that are out of sight or too far away. + +### SDF collision + +![Image](/img/Tutorials/3d/particles/img/particle_collision_sdf_entry.webp) + + SDF collision in the node list + +SDF collision nodes create a [signed distance field ](https://www.reddit.com/r/explainlikeimfive/comments/k2zbos/eli5_what_are_distance_fields_in_graphics) +that particles can collide with. SDF collision is similar to height field collision in that it turns multiple +meshes within its bounds into a single collision volume for particles. A major difference is that signed distance +fields can represent holes, tunnels and overhangs, which is impossible to do with height fields alone. The +performance overhead is larger compared to height fields, so they're best suited for small-to-medium-sized environments. + +To create an SDF collision node, add a new child node to your scene and select ``GPUParticlesCollisionSDF3D`` +from the list of available nodes. SDF collision nodes have to be baked in order to have any effect on particles +in the level. To do that, click the ``Bake SDF`` button in the viewport toolbar +while the SDF collision node is selected and choose a directory to store the baked data. Since SDF collision needs +to be baked in the editor, it's static and cannot change at runtime. + +![Image](/img/Tutorials/3d/particles/img/particle_collision_sdf.webp) + + SDF particle collision allows for very detailed 3-dimensional collision shapes + +An SDF collision node is shaped like a box. The ``Extents`` property controls its size. Extents +always measure half of the sides of its bounds, so a value of ``(X=1.0,Y=1.0,Z=1.0)`` creates a box that +is 2 meters wide on each side. Anything outside of the node's extents is ignored for collision. + +The ``Resolution`` property controls how detailed the distance field is. A lower resolution performs faster +at the cost of accuracy. If the resolution is too low, it may look like particles penetrate level geometry +or get stuck in the air during collision events. They might also ignore some smaller meshes completely. + +![Image](/img/Tutorials/3d/particles/img/particle_collision_sdf_res.webp) + + The same area covered by a signed distance field at different resolutions: 16 (left) and 256 (right) + +The ``Thickness`` property gives the distance field, which is usually hollow on the inside, a thickness to +prevent particles from penetrating at high speeds. If you find that some particles don't collide with the +level geometry and instead shoot right through it, try setting this property to a higher value. + +The ``Bake Mask`` property controls which meshes will be considered when the SDF is baked. Only meshes that +render on the active layers in the bake mask contribute to particle collision. + +### Troubleshooting + +For particle collision to work, the particle's [visibility AABB ](doc_3d_particles_properties_draw) +must overlap with the collider's AABB. If collisions appear to be not working +despite colliders being set up, generate an updated visibility AABB by selecting +the GPUParticles3D node and choosing **GPUParticles3D > Generate Visibility AABB…** +at the top of the 3D editor viewport. + +If the particles move fast and colliders are thin. There are two solutions for this: + +- Make the colliders thicker. For instance, if particles cannot get below a + solid floor, you could make the collider representing the floor thicker than + its actual visual representation. The heightfield collider automatically + handles this by design, as heightfields cannot represent "room over room" + collision. +- Increased ``Fixed FPS`` in the GPUParticles3D node, which will perform collision + checks more often. This comes at a performance cost, so avoid setting this too high. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/particles/complex_shapes.md b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/complex_shapes.md new file mode 100644 index 0000000..2aa1fc3 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/complex_shapes.md @@ -0,0 +1,83 @@ + +## Complex emission shapes + +![Image](/img/Tutorials/3d/particles/img/particle_complex_emission.webp) + +When it is not enough to emit particles from one of the simple shapes available +in the [process material ](doc_process_material_properties_shapes), Redot provides +a way to emit particles from arbitrary, complex shapes. The shapes are generated from +meshes in the scene and stored as textures in the particle process material. This is a +very versatile workflow that has allowed users to use particle systems for things that +go beyond traditional use cases, like foliage, leaves on a tree, or complex +holographic effects. + +:::note + +When you create emission points from meshes, you can only select a single node as +emission source. If you want particles to emit from multiple shapes, you either +have to create several particle systems or combine the meshes into one in an +external DCC software. + +::: + +![Image](/img/Tutorials/3d/particles/img/particle_create_emission_points.webp) + + Create particle emission points... + +![Image](/img/Tutorials/3d/particles/img/particle_select_emission_mesh.webp) + + \...from a mesh instance as the source + +![Image](/img/Tutorials/3d/particles/img/particle_emission_density.webp) + + More points = higher particle density + +To make use of this feature, start by creating a particle system in the current scene. +Add a mesh instance that serves as the source of the particle emission points. With the +particle system selected, navigate to the viewport menu and select the *GPUParticles3D* +entry. From there, select ``Create Emission Points From Node``. + +A dialog window will pop up and ask you to select a node as the emission source. +Choose one of the mesh instances in the scene and confirm your selection. The next +dialog window deals with the amount of points and how to generate them. + +``Emission Points`` controls the total number of points that you are about to generate. +Particles will spawn from these points, so what to enter here depends on the +size of the source mesh (how much area you have to cover) and the desired density of +the particles. + +``Emission Source`` offers 3 different options for how the points are generated. +Select ``Surface Points`` if all you want to do is distribute the emission points across the +surface of the mesh. Select ``Surface Points + Normal (Directed)`` if you also want to +generate information about the surface normals and make particles move in the direction +that the normals point at. The last option, ``Volume``, creates emission points everywhere +inside the mesh, not just across its surface. + +The emission points are stored in the particle system's local coordinate system, so +you can move the particle node around and the emission points will follow. This might be +useful when you want to use the same particle system in several different places. On the +other hand, you might have to regenerate the emission points when you move either +the particle system or the source mesh. + +### Emission shape textures + +![Image](/img/Tutorials/3d/particles/img/particle_emission_textures.webp) + + The available emission shape textures + +All the data for complex particle emission shapes is stored in a set of textures. How +many, depends on the type of emission shape you use. If you set the ``Shape`` property +in the ``Emission Shape`` group on the particle process material to ``Points``, you +have access to 2 texture properties, the ``Point Texture`` and the ``Color Texture``. +Set it to ``Directed Points`` and there is a third property called ``Normal Texture``. + +``Point Texture`` contains all possible emission points that were generated in the +previous step. A point is randomly selected for every particle when it spawns. +``Normal Texture``, if it exists, provides a direction vector at that same location. +If the ``Color Texture`` property is also set, it provides color for the particle, +sampled at the same location as the other two textures and modulating any other color +that was set up on the process material. + +There is also the ``Point Count`` property that you can use to change the number of +emission points at any time after creating the emission shape. This includes dynamically +at runtime while the playing the game. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/particles/creating_a_3d_particle_system.md b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/creating_a_3d_particle_system.md new file mode 100644 index 0000000..4460bb7 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/creating_a_3d_particle_system.md @@ -0,0 +1,93 @@ + +## Creating a 3D particle system + +![Image](/img/Tutorials/3d/particles/img/particle_node_new.webp) + + Required particle node properties + +To get started with particles, the first thing we need to do is add a ``GPUParticles3D`` +node to the scene. Before we can actually see any particles, we have to set up two parameters on the node: +the ``Process Material`` and at least one ``Draw Pass``. + +### The process material + +To add a process material to your particles node, go to ``Process Material`` in the inspector panel. +Click on the box next to ``Process Material`` and from the dropdown menu select ``New ParticleProcessMaterial``. + +![Image](/img/Tutorials/3d/particles/img/particle_new_process_material.webp) + + Creating a process material + +[class_ParticleProcessMaterial](class_ParticleProcessMaterial) is a special kind of material. We don't use it to draw any objects. +We use it to update particle data and behavior on the GPU instead of the CPU, which comes with a massive performance +boost. A click on the newly added material displays a long list of properties that you can set to +control each particle's behavior. + +### Draw passes + +![Image](/img/Tutorials/3d/particles/img/particle_first_draw_pass.webp) + + At least one draw pass is required + +In order to render any particles, at least one draw pass needs to be defined. To do that, go to +``Draw Passes`` in the inspector panel. Click on the box next to ``Pass 1`` and select ``New QuadMesh`` +from the dropdown menu. After that, click on the mesh and set its ``Size`` to 0.1 for both ``x`` +and ``y``. Reducing the mesh's size makes it a little easier to tell the individual particle +meshes apart at this stage. + +You can use up to 4 draw passes per particle system. Each pass can render a different +mesh with its own unique material. All draw passes use the data that is computed by the process material, +which is an efficient method for composing complex effects: Compute particle +behavior once and feed it to multiple render passes. + +![Image](/img/Tutorials/3d/particles/img/particle_two_draw_passes.webp) + + Using multiple draw passes: yellow rectangles (pass1) and blue spheres (pass 2) + +If you followed the steps above, your particle system should now be emitting particles in a waterfall-like fashion, +making them move downwards and disappear after a few seconds. This is the foundation for all +particle effects. Take a look at the documentation for [particle ](properties.md) and +[particle material ](process_material_properties.md) properties to +learn how to make particle effects more interesting. + +![Image](/img/Tutorials/3d/particles/img/particle_basic_system.webp) + +### Particle conversion + +![Image](/img/Tutorials/3d/particles/img/particle_convert_cpu.webp) + + Turning GPU into CPU particles + +You can convert GPU particles to CPU particles at any time using the entry in the viewport +menu. When you do so, keep in mind that not every feature of GPU particles is available for +CPU particles, so the resulting particle system will look and behave differently from the +original. + +You can also convert CPU particles to GPU particles if you no longer need to use CPU particles. +This is also done from the viewport menu. + +Some of the most notable features that are lost during the conversion include: + +- multiple draw passes +- turbulence +- sub-emitters +- trails +- attractors +- collision + +You also lose the following properties: + +- ``Amount Ratio`` +- ``Interp to End`` +- ``Damping as Friction`` +- ``Emission Shape Offset`` +- ``Emission Shape Scale`` +- ``Inherit Velocity Ratio`` +- ``Velocity Pivot`` +- ``Directional Velocity`` +- ``Radial Velocity`` +- ``Velocity Limit`` +- ``Scale Over Velocity`` + +Converting GPU particles to CPU particles can become necessary when you want to release a game +on older devices that don't support modern graphics APIs. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/particles/index.md b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/index.md new file mode 100644 index 0000000..b4dd844 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/index.md @@ -0,0 +1,16 @@ +# Particles + +This section contains tutorials and documentation about particles in Redot Engine. + +## Articles + +- [Attractors](attractors) +- [Collision](collision) +- [Complex shapes](complex_shapes) +- [Creating a 3d particle system](creating_a_3d_particle_system) +- [Process material properties](process_material_properties) +- [Properties](properties) +- [Subemitters](subemitters) +- [Trails](trails) +- [Turbulence](turbulence) + diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/particles/process_material_properties.md b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/process_material_properties.md new file mode 100644 index 0000000..9f8140e --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/process_material_properties.md @@ -0,0 +1,396 @@ + +## Process material properties + +![Image](/img/Tutorials/3d/particles/img/particle_minmaxcurve.webp) + + Min, max, and curve properties + +The properties in this material control how particles behave and change over their lifetime. +A lot of them have ``Min``, ``Max``, and ``Curve`` values that allow you to fine-tune +their behavior. The relationship between these values is this: When a particle is spawned, +the property is set with a random value between ``Min`` and ``Max``. If ``Min`` and ``Max`` are +the same, the value will always be the same for every particle. If the ``Curve`` is also set, +the value of the property will be multiplied by the value of the curve at the current point +in a particle's lifetime. Use the curve to change a property over the particle lifetime. Very +complex behavior can be expressed this way. + +### Time + +The ``Lifetime Randomness`` property controls how much randomness to apply to each particle's +lifetime. A value of ``0`` means there is no randomness at all and all particles live for +the same amount of time, set by the [Lifetime ](doc_3d_particles_properties_time) property. A value of ``1`` means +that a particle's lifetime is completely random within the range of [0.0, ``Lifetime``]. + +## Particle flags + +The ``Align Y`` property aligns each particle's Y-axis with its velocity. Enabling this +property is the same as setting the [Transform Align ](doc_3d_particles_properties_draw) property to +``Y to Velocity``. + +The [`Rotate Y`` property works with the properties in the `Angle ](#angle) and +[Angular Velocity ](#angular-velocity) groups to control particle rotation. ``Rotate Y`` +has to be enabled if you want to apply any rotation to particles. The exception to this +is any particle that uses the [Standard Material ](../standard_material_3d.md) +where the ``Billboard`` property is set to ``Particle Billboard``. In that case, particles +rotate even without ``Rotate Y`` enabled. + +When the ``Disable Z`` property is enabled, particles will not move along the Z-axis. +Whether that is going to be the particle system's local Z-axis or the world Z-axis is +determined by the [Local Coords ](doc_3d_particles_properties_draw) property. + +The ``Damping as Friction`` property changes the behavior of damping from a constant +deceleration to a deceleration based on speed. + +## Spawn + +### Emission shape + +Particles can emit from a single point in space or in a way that they fill out a shape. +The ``Shape`` property controls that shape. ``Point`` is the default value. All +particles emit from a single point in the center of the particle system. When set to ``Sphere`` +or ``Box``, particles emit in a way that they fill out a sphere or a box shape evenly. +You have full control over the size of these shapes. ``Sphere Surface`` works like ``Sphere``, +but instead of filling it out, all particles spawn on the sphere's surface. + +![Image](/img/Tutorials/3d/particles/img/particle_shapes_simple.webp) + + Particles emitting from a point (left), in a sphere (middle), and in a box (right) + +![Image](/img/Tutorials/3d/particles/img/particle_ring.webp) + + A ring-shaped particle system + +The ``Ring`` emission shape makes particles emit in the shape of a ring. You can control the ring's +direction by changing the ``Ring Axis`` property. ``Ring Height`` controls the thickness +of the ring along its axis. ``Ring Radius`` and ``Ring Inner Radius`` control how wide +the ring is and how large the hole in the middle should be. The image shows a particle +system with a radius of ``2`` and an inner radius of ``1.5``, the axis points along the +global Z-axis. + +In addition to these relatively simple shapes, you can select the ``Points`` or +``Directed Points`` option to create highly complex emission shapes. See the +[Complex emission shapes ](complex_shapes.md) section for a detailed +explanation of how to set these up. + +### Angle + +The [`Angle`` property controls a particle's starting rotation `as described above ](#process-material-properties). +In order to have an actual effect on the particle, you have to enable one of two properties: [Rotate Y ](#particle-flags) +rotates the particle around the particle system's Y-axis. The ``Billboard`` property in +the [Standard Material ](../standard_material_3d.md), if it is set to ``Particle Billboard``, rotates +the particle around the axis that points from the particle to the camera. + +### Direction + +:::note + +The ``Direction`` property alone is not enough to see any particle movement. Whatever +values you set here only take effect once velocity or acceleration properties are set, too. + +::: + +The ``Direction`` property is a vector that controls each particle's direction of movement +at the moment it is spawned. A value of ``(X=1,Y=0,Z=0)`` would make all particles move +sideways along the X-axis. For something like a fountain where particles shoot out up in the +air, a value of ``(X=0,Y=1,Z=0)`` would be a good starting point. + +![Image](/img/Tutorials/3d/particles/img/particle_direction.webp) + + Different direction values: Y-axis only (left), equal values for X and Y (middle), X and Y with gravity enabled (right) + +After setting a direction, you will notice that all particles move in the same direction in +a straight line. The ``Spread`` property adds some variation and randomness to each particle's +direction. The higher the value, the stronger the deviation from the original path. A value +of ``0`` means there is no spread at all while a value of ``180`` makes particles shoot out in +every direction. You could use this for something like pieces of debris during an explosion effect. + +![Image](/img/Tutorials/3d/particles/img/particle_spread.webp) + + No spread (left), 45 degree angle (middle), full 180 degrees (right) + +The ``Flatness`` property limits the spread along the Y-axis. A value of ``0`` means there +is no limit and a value of ``1`` will eliminate all particle movement along the Y-axis. The +particles will spread out completely "flat". + +You won't see any actual movement until you also set some values for the velocity and +acceleration properties below, so let's take a look at those next. + +### Initial velocity + +While the ``Direction`` property controls a particle's movement direction, the ``Initial Velocity`` +controls how fast it goes. It's separated into ``Velocity Min`` and ``Velocity Max``, both +set to ``0`` by default, which is why you don't see any movement initially. As soon as you set +values for either of these properties [as described above ](#process-material-properties), the +particles begin to move. The direction is multiplied by these values, so you can make particles +move in the opposite direction by setting a negative velocity. + +## Accelerations + +### Gravity + +The next few property groups work closely together to control particle movement and rotation. +``Gravity`` drags particles in the direction it points at, which is straight down at the strength +of Earth's gravity by default. Gravity affects all particle movement. +If your game uses physics and the world's gravity can change at runtime, you can use this property +to keep the game's gravity in sync with particle gravity. A ``Gravity`` value of ``(X=0,Y=0,Z=0)`` means +no particle will ever move at all if none of the other movement properties are set. + +![Image](/img/Tutorials/3d/particles/img/particle_gravity.webp) + + Left\: (X=0,Y=-9.8,Z=0), middle\: (X=0,Y=9.8,Z=0), right\: (X=4,Y=2,Z=0). + +### Angular velocity + +[`Angular Velocity`` controls a particle's speed of rotation `as described above ](#process-material-properties). +You can reverse the direction by using negative numbers for ``Velocity Min`` or ``Velocity Max``. Like the +[Angle ](#angle) property, the rotation will only be visible if the [Rotate Y ](#particle-flags) flag is set +or the ``Particle Billboard`` mode is selected in the [Standard Material ](../standard_material_3d.md). + +:::note + +The [Damping ](#damping) property has no effect on the angular velocity. + +::: + +### Linear acceleration + +A particle's velocity is a constant value: once it's set, it doesn't change and the particle will +always move at the same speed. You can use the ``Linear Accel`` property to +change the speed of movement over a particle's lifetime [as described above ](#process-material-properties). +Positive values will speed up the particle and make it move faster. Negative values will slow it +down until it stops and starts moving in the other direction. + +![Image](/img/Tutorials/3d/particles/img/particle_accel_linear.webp) + + Negative (top) and positive (bottom) linear acceleration + +It's important to keep in mind that when we change acceleration, we're not changing the velocity +directly, we're changing the *change* in velocity. A value of ``0`` on the acceleration curve +does not stop the particle's movement, it stops the change in the particle's movement. Whatever +its velocity was at that moment, it will keep moving at that velocity until the acceleration is +changed again. + +### Radial acceleration + +The ``Radial Accel`` property adds a gravity-like force to all particles, with the origin +of that force at the particle system's current location. Negative values make particles move +towards the center, like the force of gravity from a planet on objects in its orbit. Positive +values make particles move away from the center. + +![Image](/img/Tutorials/3d/particles/img/particle_accel_radial.webp) + + Negative (left) and positive (right) radial acceleration + +### Tangential acceleration + +![Image](/img/Tutorials/3d/particles/img/particle_tangent.webp) + + Tangents on a circle + +This property adds particle acceleration in the direction of the tangent to a circle on the particle +system's XZ-plane with the origin at the system's center and a radius the distance between each +particle's current location and the system's center projected onto that plane. + +Let's unpack that. + +A tangent to a circle is a straight line that "touches" the circle in a right angle to the circle's +radius at the touch point. A circle on the particle system's XZ-plane is the circle that you see +when you look straight down at the particle system from above. + +![Image](/img/Tutorials/3d/particles/img/particle_accel_tangent.webp) + + Tangential acceleration from above + +``Tangential Accel`` is always limited to that plane and never move particles along the system's Y-axis. +A particle's location is enough to define such a circle where the distance to the system's center is +the radius if we ignore the vector's Y component. + +The ``Tangential Accel`` property will make particles orbit the particle system's center, but the +radius will increase constantly. Viewed from above, particles will move away from the center +in a spiral. Negative values reverse the direction. + +### Damping + +The ``Damping`` property gradually stops all movement. Each frame, a particle's movement +is slowed down a little unless the total acceleration is greater than the damping effect. If +it isn't, the particle will keep slowing down until it doesn't move at all. The greater the value, the less +time it takes to bring particles to a complete halt. + +### Attractor interaction + +If you want the particle system to interact with [particle attractors ](attractors.md), +you have to check the ``Enabled`` property. When it is disabled, the particle system +ignores all particle attractors. + +## Display + +### Scale + +[`Scale`` controls a particle's size `as described above ](#process-material-properties). You can set +different values for ``Scale Min`` and ``Scale Max`` to randomize each particle's size. Negative values +are not allowed, so you won't be able to flip particles with this property. If you emit particles as +billboards, the ``Keep Size`` property on the [Standard Material ](../standard_material_3d.md) +in your draw passes has to be enabled for any scaling to have an effect. + +### Color + +The ``Color`` property controls a particle's initial color. It will have an effect only after the +``Use As Albedo`` property in the ``Vertex Color`` group of the [Standard Material ](../standard_material_3d.md) +is enabled. This property is multiplied with color coming from the particle material's +own ``Color`` or ``Texture`` property. + +![Image](/img/Tutorials/3d/particles/img/particle_ramp.webp) + + Setting up a color ramp + +There are two ``Ramp`` properties in the ``Color`` group. These allow you to define a range of colors +that are used to set the particle's color. The ``Color Ramp`` property changes a particle's color +over the course of its lifetime. It moves through the entire range of colors you defined. +The ``Color Initial Ramp`` property selects the particle's initial color from a random +position on the color ramp. + +To set up a color ramp, click on the box next to the property name and from the dropdown menu +select ``New GradientTexture1D``. Click on the box again to open the texture's details. +Find the ``Gradient`` property, click on the box next to it and select ``New Gradient``. +Click on that box again and you will see a color range. Click anywhere on that range +to insert a new marker. You can move the marker with the mouse and delete it by clicking +the right mouse button. When a marker is selected, you can use the color picker next to +the range to change its color. + +### Hue variation + +Like the ``Color`` property, ``Hue Variation`` controls a particle's color, but in a +different way. It does so not by setting color values directly, but by +*shifting the color's hue*. + +Hue describes a color's pigment: red, orange, yellow, green and so on. It does not +tell you anything about how bright or how saturated the color is. The ``Hue Variation`` +property controls the range of available hues [as described above ](#process-material-properties). + +It works on top of the particle's current color. The values you set for +``Variation Min`` and ``Variation Max`` control how far the hue is allowed to shift +in either direction. A higher value leads to more color variation while a low value +limits the available colors to the closest neighbors of the original color. + +![Image](/img/Tutorials/3d/particles/img/particle_hue.webp) + + Different values for hue variation, both times with blue as base color: 0.6 (left) and 0.1 (right) + +### Animation + +The ``Animation`` property group controls the behavior of sprite +sheet animations in the particle's [Standard Material ](../standard_material_3d.md). +The [`Min``, ``Max``, and ``Curve`` values work `as described above ](#process-material-properties). + +An animated sprite sheet is a texture that contains several smaller images aligned on a grid. +The images are shown one after the other so fast that they combine to play a short +animation, like a flipbook. You can use them for animated particles like smoke or fire. +These are the steps to create an animated particle system: + +![Image](/img/Tutorials/3d/particles/img/particle_sprite.webp) + + An 8x8 animated smoke sprite sheet + +#. Import a sprite sheet texture into the engine. If you don't have one at hand, you can download the :download:`high-res version of the example image <img/particle_sprite_smoke.webp>`. +#. Set up a particle system with at least one draw pass and assign a ``Standard Material`` to the mesh in that draw pass. +#. Assign the sprite sheet to the ``Texture`` property in the ``Albedo`` group +#. Set the material's ``Billboard`` property to ``Particle Billboard``. Doing so makes the ``Particles Anim`` group available in the material. +#. Set ``H Frames`` to the number of columns and ``V Frames`` to the number of rows in the sprite sheet. +#. Check ``Loop`` if you want the animation to keep repeating. + +That's it for the Standard Material. You won't see any animation right away. This is +where the ``Animation`` properties come in. The ``Speed`` properties control how fast +the sprite sheet animates. Set ``Speed Min`` and ``Speed Max`` to ``1`` and you should see the +animation playing. The ``Offset`` properties control where the animation starts on a +newly spawned particle. By default, it will always be the first image in the sequence. +You can add some variety by changing ``Offset Min`` and ``Offset Max`` to randomize +the starting position. + +![Image](/img/Tutorials/3d/particles/img/particle_animate.webp) + + Three different particle systems using the same smoke sprite sheet + +Depending on how many images your sprite sheet contains and for how long your +particle is alive, the animation might not look smooth. The relationship between +particle lifetime, animation speed, and number of images in the sprite sheet is +this: + +:::note + +At an animation speed of ``1.0``, the animation will reach the last image +in the sequence just as the particle's lifetime ends. + +$$ +Animation\ FPS = \frac{Number\ of\ images}{Lifetime} +$$ + +::: + +If your sprite sheet contains +64 (8x8) images and the particle's lifetime is set to ``1 second``, the animation +will be very smooth at **64 FPS** (1 second / 64 images). if the lifetime is set to ``2 seconds``, it +will still be fairly smooth at **32 FPS**. But if the particle is alive for +``8 seconds``, the animation will be visibly choppy at **8 FPS**. In order to make the +animation smooth again, you need to increase the animation speed to something like ``3`` +to reach an acceptable framerate. + +![Image](/img/Tutorials/3d/particles/img/particle_animate_lifetime.webp) + + The same particle system at different lifetimes: 1 second (left), 2 seconds (middle), 8 seconds (right) + +Note that the GPUParticles3D node's **Fixed FPS** also affects animation +playback. For smooth animation playback, it's recommended to set it to 0 so that +the particle is simulated on every rendered frame. If this is not an option for +your use case, set **Fixed FPS** to be equal to the effective framerate used by +the flipbook animation (see above for the formula). + +### Turbulence + +Turbulence adds noise to particle movement, creating interesting and lively patterns. +Check the box next to the ``Enabled`` property to activate it. A number +of new properties show up that control the movement speed, noise pattern and overall influence +on the particle system. You can find a detailed explanation of these in the section on +[particle turbulence ](turbulence.md). + +## Collision + +The ``Mode`` property controls how and if emitters collide with particle collision nodes. Set it +to ``Disabled`` to disable any collision for this particle system. Set it to ``Hide On Contact`` +if you want particles to disappear as soon as they collide. Set it to ``Constant`` to make +particles collide and bounce around. You will see two new properties appear in the inspector. +They control how particles behave during collision events. + +A high ``Friction`` value will reduce sliding along surfaces. This is especially +helpful if particles collide with sloped surfaces and you want them to stay in +place instead of sliding all the way to the bottom, like snow falling on a mountain. +A high ``Bounce`` value will make particles bounce off surfaces they collide with, +like rubber balls on a solid floor. + +If the ``Use Scale`` property is enabled, the [collision base size ](doc_3d_particles_properties_collision) +is multiplied by the particle's [current scale ](#scale). You can use this to +make sure that the rendered size and the collision size match for particles +with random scale or scale that varies over time. + +You can learn more about particle collisions in the [Collisions ](collision.md) +section in this manual. + +## Sub-emitter + +![Image](/img/Tutorials/3d/particles/img/particle_sub_mode.webp) + + The available sub-emitter modes + +The ``Mode`` property controls how and when sub-emitters are spawned. Set it to ``Disabled`` +and no sub-emitters will ever be spawned. Set it to ``Constant`` to make sub-emitters +spawn continuously at a constant rate. The ``Frequency`` property controls how often +that happens within the span of one second. Set the mode to ``At End`` to make the sub-emitter +spawn at the end of the parent particle's lifetime, right before it is destroyed. The +``Amount At End`` property controls how many sub-emitters will be spawned. Set the +mode to ``At Collision`` to make sub-emitters spawn when a particle collides with the +environment. The ``Amount At Collision`` property controls how many sub-emitters will be spawned. + +When the ``Keep Velocity`` property is enabled, the newly spawned sub-emitter starts off +with the parent particle's velocity at the time the sub-emitter is created. + +See the [Sub-emitters ](subemitters.md) section in this manual for a detailed explanation of how +to add a sub-emitter to a particle system. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/particles/properties.md b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/properties.md new file mode 100644 index 0000000..3993efe --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/properties.md @@ -0,0 +1,186 @@ + +## 3D Particle system properties + +### Emitter properties + +![Image](/img/Tutorials/3d/particles/img/particle_props_emitter.webp) + +The checkbox next to the ``Emitting`` property activates and deactivates the particle system. Particles will +only be processed and rendered if the box is checked. You can set this property at runtime if you +want to activate or deactivate particle systems dynamically. + +The ``Amount`` property controls the maximum number of particles visible at any given time. Increase the +value to spawn more particles at the cost of performance. + +The ``Amount Ratio`` property is the ratio of particles compared to the amount that will be emitted. +If it's less than ``1.0``, the amount of particles emitted through the lifetime will be the ``Amount`` * +``Amount Ratio``. Changing this value while emitted doesn't affect already created particles and doesn't +cause the particle system to restart. It's useful for making effects where the number of emitted particles +varies over time. + +You can set another particle node as a ``Sub Emitter``, which will be spawned as a child of each +particle. See the [Sub-emitters ](subemitters.md) section in this manual for a detailed explanation of how +to add a sub-emitter to a particle system. + +### Time properties + +![Image](/img/Tutorials/3d/particles/img/particle_props_time.webp) + +The ``Lifetime`` property controls how long each particle exists before it disappears again. It +is measured in seconds. A lot of particle properties can be set to change over the particle's +lifetime and blend smoothly from one value to another. + +``Lifetime`` and ``Amount`` are related. They determine the particle system's emission rate. +Whenever you want to know how many particles are spawned per second, this is the formula you +would use: + +$$ +Particles per second = \frac{Amount}{Lifetime} + +$$ + +Example: Emitting 32 particles with a lifetime of 4 seconds each would mean the system emits +8 particles per second. + +The ``Interp to End`` property causes all the particles in the node to interpolate towards +the end of their lifetime. + +If the checkbox next to the ``One Shot`` property is checked, the particle system will emit ``amount`` particles +and then disable itself. It "runs" only once. This property is unchecked by default, so the system will +keep emitting particles until it is disabled or destroyed manually. One-shot particles are a good fit for +effects that react to a single event, like item pickups or splinters that burst away when a bullet hits a wall. + +The ``Preprocess`` property is a way to fast-forward to a point in the middle of the +particle system's lifetime and start rendering from there. It is measured in seconds. A value of +``1`` means that when the particle system starts, it will look as if it has been +running for one second already. + +This can be useful if you want the particle system to look like it has been active for a while even +though it was just loaded into the scene. Consider the example below. Both particle systems simulate +dust flying around in the area. With a preprocess value of ``0``, there wouldn't be any dust for the +first couple of seconds because the system has not yet emitted enough particles for the effect to +become noticeable. This can be seen in the video on the left. Compare that to the video on the +right where the particle system is preprocessed for ``4`` seconds. The dust is fully visible from +the very beginning because we skipped the first four seconds of "setup" time. + +![Image](/img/Tutorials/3d/particles/img/particle_preprocess.webp) + + No preprocess (left) vs. 4 seconds of preprocess (right) + +You can slow down or speed up the particle system with the ``Speed Scale`` property. This applies +to processing the data as well as rendering the particles. Set it to ``0`` to pause the particle +system completely or set it to something like ``2`` to make it move twice as fast. + +![Image](/img/Tutorials/3d/particles/img/particle_speed_scale.webp) + + Different speed scale values: 0.1 (left), 0.5 (middle), 1.0 (right) + +The ``Explosiveness`` property controls whether particles are emitted sequentially or simultaneously. +A value of ``0`` means that particles emit one after the other. +A value of ``1`` means that all ``amount`` particles emit at the same time, giving +the effect a more "explosive" appearance. + +The ``Randomness`` property adds some randomness to the particle emission timing. When set to ``0``, +there is no randomness at all and the interval between the emission of one particle and +the next is always the same: the particles are emitted at *regular* intervals. A ``Randomness`` +value of ``1`` makes the interval completely random. You can use this property to break +up some of the uniformity in your effects. When ``Explosiveness`` is set to ``1``, this +property has no effect. + +![Image](/img/Tutorials/3d/particles/img/particle_interpolate.webp) + + Interpolation off (left) vs. on (right) + +The ``Fixed FPS`` property limits how often the particle system is processed. This includes +property updates as well as collision and attractors. This can improve performance a lot, +especially in scenes that make heavy use of particle collision. Note that this does not +change the speed at which particles move or rotate. You would use the ``Speed Scale`` +property for that. + +When you set ``Fixed FPS`` to very low values, you will notice that +the particle animation starts to look choppy. This can sometimes be desired if it fits +the art direction, but most of the time, you'll want particle systems to animate smoothly. +That's what the ``Interpolate`` property does. It blends particle properties between +updates so that even a particle system running at ``10`` FPS appears as smooth as +running at ``60``. + +:::note + +When using [particle collision ](collision.md), tunneling can occur +if the particles move fast and colliders are thin. This can be remedied by increasing +``Fixed FPS`` (at a performance cost). + +::: + +### Collision properties + +:::info + +Setting up particle collision requires following further steps described in +[doc_3d_particles_collision](collision.md). + +::: + +The ``Base Size`` property defines each particle's default collision size, which is used +to check whether a particle is currently colliding with the environment. You would usually want this +to be about the same size as the particle. It can make sense to increase this value +for particles that are very small and move very fast to prevent them from clipping +through the collision geometry. + +### Drawing properties + +![Image](/img/Tutorials/3d/particles/img/particle_drawing.webp) + +The ``Visibility AABB`` property defines a box around the particle system's origin. +As long as any part of this box is in the camera's field of view, the particle system +is visible. As soon as it leaves the camera's field of view, the particle system stops +being rendered at all. You can use this property to boost performance by keeping the +box as small as possible. + +One thing to keep in mind when you set a size for the ``Visibility AABB`` is that particles +that are outside of its bounds disappear instantly when it leaves the camera's field of view. +Particle collision will also not occur outside the ``Visibility AABB``. +While not technically a bug, this can have a negative effect on the visual experience. + +When the ``Local Coords`` property is checked, all particle calculations use the local +coordinate system to determine things like up and down, gravity, and movement direction. +Up and down, for example, would follow the particle system's or its parent node's rotation. +When the property is unchecked, the global world space is used for these calculations: +Down will always be -Y in world space, regardless of the particle system's rotation. + +![Image](/img/Tutorials/3d/particles/img/particle_coords.webp) + + Local space coordinates (left) vs. world space coordinates (right) + +The ``Draw Order`` property controls the order in which individual particles are drawn. ``Index`` means +that they are drawn in the order of emission: particles that are spawned later are drawn +on top of earlier ones. ``Lifetime`` means that they are drawn in the order of their +remaining lifetime. ``Reverse Lifetime`` reverses the ``Lifetime`` draw order. ``View Depth`` +means particles are drawn according to their distance from the camera: The ones closer +to the camera on top of those farther away. + +The ``Transform Align`` property controls the particle's default rotation. ``Disabled`` +means they don't align in any +particular way. Instead, their rotation is determined by the values set in the process +material. ``Z-Billboard`` means that the particles will always face the camera. This is +similar to the ``Billboard`` property in the [Standard Material ](../standard_material_3d.md). +``Y to Velocity`` means that each particle's Y-axis aligns with its movement +direction. This can be useful for things like bullets or arrows, where you want particles +to always point "forward". ``Z-Billboard + Y to Velocity`` combines the previous two modes. +Each particle's Z-axis will point towards the camera while its Y-axis will align with +their velocity. + +### Trail properties + +![Image](/img/Tutorials/3d/particles/img/particle_trail.webp) + + Particle trail properties + +The ``Enabled`` property controls whether particles are rendered as trails. The box needs +to be checked if you want to make use of particle trails. + +The ``Length Secs`` property controls for how long a trail should be emitted. The longer +this duration is, the longer the trail will be. + +See the [Particle trails ](trails.md) section in this manual for a detailed +explanation of how particle trails work and how to set them up. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/particles/subemitters.md b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/subemitters.md new file mode 100644 index 0000000..c0945da --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/subemitters.md @@ -0,0 +1,71 @@ + +## Particle sub-emitters + +![Image](/img/Tutorials/3d/particles/img/particle_sub_chain.webp) + +Sometimes a visual effect cannot be created with a single particle system alone. +Sometimes a particle system needs to be spawned as a response to something that happens in +another particle system. Fireworks are a good example of that. They usually consist of +several stages of explosions that happen in sequence. Sub-emitters are a good way to achieve +this kind of effect. + +![Image](/img/Tutorials/3d/particles/img/particle_sub_assign.webp) + + Click to assign a sub-emitter... + +![Image](/img/Tutorials/3d/particles/img/particle_sub_list.webp) + + \...and select one from the scene + +A sub-emitter is a particle system that spawns as a child of another particle system. +You can add sub-emitters to sub-emitters, chaining particle effects as deep as you like. + +To create a sub-emitter, you need at least two particle systems in the same scene. One of them will be the +parent and one will be set as the child. Find the ``Sub Emitter`` property on the parent +and click the box next to it to assign the sub-emitter. You will see a list of available particle +systems in the scene. Select one and click the confirmation button. + +Particle systems from instanced scenes can be set as sub-emitters too, as long as the +``Editable Children`` property is enabled on the instanced scene. This also works the other +way around: You can assign a sub-emitter to a particle system in an instanced scene, +even one coming from a different instanced scene. + +:::note + +When you set a particle system as the sub-emitter of another, the system stops +emitting, even if the ``Emitting`` property was checked. Don't worry, it didn't break. This happens +to every particle system as soon as it becomes a sub-emitter. You also won't be able to +re-enable the property as long as the particle system is used as a sub-emitter. + +::: + +:::warning + +Even though the parent particle system can be selected from the list of available particle +systems, a particle system which is its own sub-emitter does not work in Redot. It will +simply not spawn. The same is true for any other kind of recursive or self-referential +sub-emitter setup. + +::: + +### Emitter mode + +When you assign a sub-emitter, you don't see it spawn right away. Emitting is disabled +by default and needs to be enabled first. Set the ``Mode`` property in the ``Sub Emitter`` group +of the [ParticleProcessMaterial ](doc_process_material_properties_subemitter) to something other than ``Disabled``. + +The emitter mode also determines how many sub-emitter particles are spawned. ``Constant`` +spawns a single particle at a frequency set by the ``Frequency`` property. For ``At End`` +and ``At Collision`` you can set the amount directly with the ``Amount At End`` and the +``Amount At Collision`` properties. + +### Limitations + +One thing to keep in mind is that the total number of active particles from the sub-emitter +is always capped by the ``Amount`` property on the sub-emitter particle system. If you find +that there are not enough particles spawned from the sub-emitter, you might have to increase +the amount in the particle system. + +Some emitter properties are ignored when a particle system is spawned as a sub-emitter. +The ``Explosiveness`` property, for example, has no effect. Depending on the emitter mode, +the particles are either spawned sequentially at fixed intervals or explosively all at once. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/particles/trails.md b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/trails.md new file mode 100644 index 0000000..d7f0c11 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/trails.md @@ -0,0 +1,124 @@ + +## 3D Particle trails + +![Image](/img/Tutorials/3d/particles/img/particle_trails.webp) + +![Image](/img/Tutorials/3d/particles/img/particle_trail_params.webp) + + Setting up particle trails + +Redot provides several types of trails you can add to a particle system. Before you can +work with trails, you need to set up a couple of parameters first. Create a new particle +system and assign a process material [as described before ](creating_a_3d_particle_system.md). +In the ``Trails`` group of the particle system, check the box next to ``Enabled`` and +increase the emission duration by setting ``Lifetime`` to something like ``0.8``. On +the process material, set ``Direction`` to ``(X=0,Y=1.0,Z=0)`` and ``Initial Velocity`` to +``10.0`` for both ``Min`` and ``Max``. + +The only thing that's still missing is a mesh for the draw pass. The type of mesh that you +set here controls what kind of particle trail you will end up with. + +### Ribbon trails + +![Image](/img/Tutorials/3d/particles/img/particle_ribbon_mesh.webp) + + Important ribbon mesh parameters + +The simplest type of particle trail is the ribbon trail. Navigate to the ``Draw Passes`` +section and select ``New RibbonTrailMesh`` from the options for ``Pass 1``. A +[RibbonTrailMesh ](class_RibbonTrailMesh) is a simple quad that is divided into +sections and then stretched and repeated along those sections. + +Assign a new [Standard Material ](../standard_material_3d.md) to the ``Material`` +property and enable ``Use Particle Trails`` in the ``Transform`` property group. The +particles should now be emitting in trails. + +You have two options for the ribbon mesh ``Shape`` parameter. ``Cross`` creates two +perpendicular quads, making the particle trail a little more three-dimensional. This +really only makes sense if you don't draw the trails in ``Particle Billboard`` mode +and helps when looking at the particles from different angles. The ``Flat`` option +limits the mesh to a single quad and works best with billboard particles. + +The ``Size`` parameter controls the trail's width. Use it to make trails wider or +more narrow. + +``Sections``, ``Section Length`` and ``Section Segments`` all work together to +control how smooth the particle trail looks. When a particle trail does not travel +in a straight line, the more sections it has the smoother it looks as it bends and swirls. +``Section Length`` controls the length of each section. Multiply this value by +the number of sections to know the trail's total length. + +![Image](/img/Tutorials/3d/particles/img/particle_ribbon_sections.webp) + + 3 sections, 1m section length (left) vs. 12 sections, 0.25m section length (right). Notice how the total length of the trails stays the same. + +The ``Section Segments`` parameter further subdivides each section into segments. +It has no effect on the smoothness of the trail's sections, though. Instead, it controls +the smoothness of the particle trail's overall shape. The ``Curve`` property defines +this shape. Click the box next to ``Curve`` and assign or create a new curve. The +trail will be shaped just like the curve with the curve's value at ``0.0`` at the +trail's head and the curve's value at ``1.0`` at the trail's tail. + +![Image](/img/Tutorials/3d/particles/img/particle_ribbon_curve.webp) + + Particle trails shaped by different curves. The trails move from left to right. + +Depending on the complexity of the curve, the particle trail's shape will not look +very smooth when the number of sections is low. This is where the ``Section Segments`` property +comes in. Increasing the amount of section segments adds more vertices to the trail's +sides so that it can follow the curve more closely. + +![Image](/img/Tutorials/3d/particles/img/particle_ribbon_segments.webp) + + Particle trail shape smoothness: 1 segment per section (top), 12 segments per section (bottom) + +### Tube trails + +Tube trails share a lot of their properties with ribbon trails. The big difference between them +is that tube trails emit cylindrical meshes instead of quads. + +![Image](/img/Tutorials/3d/particles/img/particle_tube.webp) + + Tube trails emit cylindrical particles + +To create a tube trail, navigate to the ``Draw Passes`` section and select ``New TubeTrailMesh`` +from the options for ``Pass 1``. A [TubeTrailMesh ](class_TubeTrailMesh) is a cylinder +that is divided into sections and then stretched and repeated along those sections. Assign a +new [Standard Material ](../standard_material_3d.md) to the ``Material`` property and enable +``Use Particle Trails`` in the ``Transform`` property group. The particles should now be emitting +in long, cylindrical trails. + +![Image](/img/Tutorials/3d/particles/img/particle_tube_mesh.webp) + + Important tube mesh parameters + +The ``Radius`` and ``Radial Steps`` properties are to tube trails what ``Size`` is to ribbon trails. +``Radius`` defines the radius of the tube and increases or decreases its overall size. ``Radial Steps`` +controls the number of sides around the tube's circumference. A higher value increases the resolution +of the tube's cap. + +``Sections`` and ``Section Length`` work the same for tube trails and ribbon trails. They control how +smooth the tube trail looks when it is bending and twisting instead of moving in a straight line. +Increasing the number of sections will make it look smoother. Change the ``Section Length`` property +to change the length of each section and with it the total length of the trail. ``Section Rings`` +is the tube equivalent of the ``Section Segments`` property for ribbons. It subdivides the sections +and adds more geometry to the tube to better fit the custom shape defined in the ``Curve`` property. + +You can shape tube trails with curves, just as you can with ribbon trails. Click the box next to the +``Curve`` property and assign or create a new curve. The trail will be shaped like the curve with +the curve's value at ``0.0`` at the trail's head and the curve's value at ``1.0`` at the trail's tail. + +![Image](/img/Tutorials/3d/particles/img/particle_tube_curve.webp) + + Particle tube trails with a custom curve shape: 4 radial steps, 3 sections, 1 section ring (left), + 12 radial steps, 9 sections, 3 section rings (right) + +An important property you might want to set is ``Transform Align`` in the particle +system's ``Drawing`` group. If you leave it as is, the tubes will not preserve volume; they +flatten out as they move because their Y-axis keeps pointing up even as they change direction. +This can cause a lot of rendering artifacts. Set the property to ``Y to Velocity`` instead +and each particle trail keeps its Y-axis aligned along the direction of its movement. + +![Image](/img/Tutorials/3d/particles/img/particle_tube_align.webp) + + Particle tube trails without alignment (left) and with Y-axis aligned to velocity (right) \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/particles/turbulence.md b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/turbulence.md new file mode 100644 index 0000000..e2a1ae3 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/particles/turbulence.md @@ -0,0 +1,107 @@ + +## Particle turbulence + +![Image](/img/Tutorials/3d/particles/img/particle_turbulence.webp) + +Turbulence uses a noise texture to add variation and interesting patterns to particle movement. +It can be combined with [particle attractors ](attractors.md) and +[collision ](collision.md) nodes to create even more complex looking behavior. + +![Image](/img/Tutorials/3d/particles/img/particle_turbulence_properties.webp) + + Particle turbulence properties + +There are two things you have to do before turbulence has any effect on a particle system. First you must +add movement to the particle system. Turbulence modifies a particle's movement +direction and speed, but it doesn't create any. It is enough to give the particle system some +gravity, but you can just as well create a number of attractors if you want the particles +to follow a more complex movement path. Second, you need to [enable turbulence in the particle process material ](doc_process_material_properties_turbulence). +Once enabled, you have access to all the turbulence properties. + +:::warning + +Turbulence makes use of 3D noise, which has a high performance cost on the GPU. +Only enable turbulence on a few particle systems on screen at most. +Using turbulence is not recommended when targeting mobile/web platforms. + +::: + +### Noise properties + +The basis for particle turbulence is a noise pattern. There are several +properties that allow you to manipulate different attributes of this pattern. + +The ``Noise Strength`` property controls the pattern's contrast, which affects the overall turbulence +sharpness. A lower value creates a softer pattern where individual movement paths are +not as sharply separated from another. Set this to a higher number to make the pattern more +distinct. + +![Image](/img/Tutorials/3d/particles/img/particle_turbulence_strength.webp) + + At a value of 1 (left), the noise strength produces softer turbulence patterns than at 20 (right) + +The ``Noise Scale`` property controls the pattern's frequency. It basically changes the noise texture's UV scale +where a smaller value produces finer detail, but repeating patterns become noticeable faster. A larger value +results in a weaker turbulence pattern overall, but the particle system can cover a larger area before repetition +starts to become an issue. + +![Image](/img/Tutorials/3d/particles/img/particle_turbulence_scale.webp) + + Turbulence noise scale produces finer details at a value of 1.5 (left) than at 6 (right) + +The ``Noise Speed`` property takes a vector and controls the noise panning speed and direction. +This allows you to move the noise pattern over time, which adds another layer of movement +variation to the particle system. + +:::warning + +Don't mix up particle movement speed and noise panning speed! They are two different things. +Particle movement is determined by a number of properties, including the turbulence noise. +The ``Noise Speed`` property moves the pattern itself, which in turn changes where the +noise affects the particles. + +::: + +At a value of ``(X=0,Y=0,Z=0)``, the noise pattern doesn't move at all. The influence on particle +movement stays the same at any given point. Set the speed to ``(X=1,Y=0,Z=0)`` instead, and the +noise pattern moves along the X-axis. + +![Image](/img/Tutorials/3d/particles/img/particle_turbulence_speed.webp) + + Different noise speed values. Left\: (X=0,Y=0,Z=0), middle\: (X=0.5,Y=0.5,Z=0.5), right\: (X=0,Y=-2,Z=0). + +The ``Noise Speed Random`` property adds some randomness to the noise panning speed. This helps +with breaking up visible patterns, especially at higher panning speeds when repetition becomes +noticeable faster. + +### Influence properties + +The influence properties determine how much each particle is affected by turbulence. Use +``Influence Min`` to set a minimum value and ``Influence Max`` to set a maximum value. When a +particle spawns, the influence is randomly chosen from within this range. You can +also set up a curve with the ``Influence Over Life`` property that modifies that value +over each particle's lifetime. These three properties together control the strength of +the turbulence's effect on the particle system [as described before ](process_material_properties.md). + +Since these properties affect the overall influence of the turbulence over a particle system, +both movement direction and speed change as you set different values. A stronger influence causes +a particle to move faster and all particles to follow along narrower paths as a result of that. + +![Image](/img/Tutorials/3d/particles/img/particle_turbulence_influence.webp) + + Notice how the particle paths are more narrow and less spread out at high influence values (right) + +### Displacement properties + +Displacement changes a particle's starting position. Use ``Initial Displacement Min`` to set a +lower limit and ``Initial Displacement Max`` to set an upper limit. When a particle spawns, the +amount of displacement is randomly chosen from within this range and multiplied by a random +direction. + +Displacement is very useful to break up regular shapes or to create complex shapes from simpler +ones. The only difference between the particle systems in the screenshot below is the value +given to the displacement properties. + +![Image](/img/Tutorials/3d/particles/img/particle_turbulence_displacement.webp) + + No displacement (left), displacement value of 5 (middle), displacement range [-20, 20] (right) \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/physical_light_and_camera_units.md b/Redot-Documentation/docs/26.1/Tutorials/3d/physical_light_and_camera_units.md new file mode 100644 index 0000000..57523ec --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/physical_light_and_camera_units.md @@ -0,0 +1,274 @@ + +# Physical light and camera units + +## Why use physical light and camera units? + +Redot uses arbitrary units for many physical properties that apply to light like +color, energy, camera field of view, and exposure. By default, these properties +use arbitrary units, because using accurate physical units comes with a few +tradeoffs that aren't worth it for many games. As Redot favors ease of use by +default, physical light units are disabled by default. + +### Advantages of physical units + +If you aim for photorealism in your project, using real world units as a basis +can help make things easier to adjust. References for real world materials, +lights and scene brightness are wildly available on websites such as +[Physically Based](https://physicallybased.info/). + +Using real world units in Redot can also be useful when porting a scene from +other 3D software that uses physical light units (such as Blender). + +### Disadvantages of physical units + +The biggest disadvantage of using physical light units is you will have to pay +close attention to the dynamic range in use at a given time. You can run into +floating point precision errors when mixing very high light intensities with +very low light intensities. + +In practice, this means that you will have to manually manage your exposure +settings to ensure that you aren't over-exposing or under-exposing your scene +too much. Auto-exposure can help you balance the light in a scene to bring it +into a normal range, but it can't recover lost precision from a dynamic range +that is too high. + +Using physical light and camera units will not automatically make your project +look *better*. Sometimes, moving away from realism can actually make a scene +look better to the human eye. Also, using physical units requires a greater +amount of rigor compared to non-physical units. Most benefits of physical units +can only be obtained if the units are correctly set to match real world +reference. + +:::note + +Physical light units are only available in 3D rendering, not 2D. + +::: + +## Setting up physical light units + +Physical light units can be enabled separately from physical camera units. + +To enable physical light units correctly, there are 4 steps required: + +1. Enable the project setting. +2. Configure the camera. +3. Configure the environment. +4. Configure Light3D nodes. + +Since physical light and camera units only require a handful of calculations to +handle unit conversion, enabling them doesn't have any noticeable performance +impact on the CPU. However, on the GPU side, physical camera units currently +enforce depth of field. This has a moderate performance impact. To alleviate +this performance impact, depth of field quality can be decreased in the advanced +Project Settings. + +### Enable the project setting + +Open the Project Settings, enable the **Advanced** toggle then enable +**Rendering > Lights And Shadows > Use Physical Light Units**. Restart the editor. + +### Configure the camera + +:::warning + +When physical light units are enabled and if you have a WorldEnvironment +node in your scene (i.e. the editor Environment is disabled), you **must** +have a [class_CameraAttributes](class_CameraAttributes) resource assigned to the +WorldEnvironment node. Otherwise, the 3D editor viewport will appear +extremely bright if you have a visible DirectionalLight3D node. + +::: + +On the Camera3D node, you can add a [class_CameraAttributes](class_CameraAttributes) +resource to its **Attributes** property. This resource is used to control the +camera's depth of field and exposure. When using +[class_CameraAttributesPhysical](class_CameraAttributesPhysical), its focal length property is also used to +adjust the camera's field of view. + +When physical light units are enabled, the following additional properties +become available in CameraAttributesPhysical's **Exposure** section: + +- **Aperture:** The size of the aperture of the camera, measured in f-stops. An + f-stop is a unitless ratio between the focal length of the camera and the + diameter of the aperture. A high aperture setting will result in a smaller + aperture which leads to a dimmer image and sharper focus. A low aperture + results in a wide aperture which lets in more light resulting in a brighter, + less-focused image. +- **Shutter Speed:** The time for shutter to open and close, measured in + *inverse seconds* (``1/N``). A lower value will let in more light leading to a + brighter image, while a higher value will let in less light leading to a + darker image. *When getting or setting this property with a script, the unit + is in seconds instead of inverse seconds.* +- **Sensitivity:** The sensitivity of camera sensors, measured in ISO. A higher + sensitivity results in a brighter image. When auto exposure is enabled, this + can be used as a method of exposure compensation. Doubling the value will + increase the exposure value (measured in EV100) by 1 stop. +- **Multiplier:** A *non-physical* exposure multiplier. Higher values will + increase the scene's brightness. This can be used for post-processing + adjustments or for animation purposes. + +The default **Aperture** value of 16 f-stops is appropriate for outdoors at +daytime (i.e. for use with a default DirectionalLight3D). For indoor lighting, a +value between 2 and 4 is more appropriate. + +Typical shutter speed used in photography and movie production is 1/50 (0.02 +seconds). Night-time photography generally uses a shutter around 1/10 (0.1 +seconds), while sports photography uses a shutter speed between 1/250 (0.004 +seconds) and 1/1000 (0.001 seconds) to reduce motion blur. + +In real life, sensitivity is usually set between 50 ISO and 400 ISO for daytime +outdoor photography depending on weather conditions. Higher values are used for +indoor or night-time photography. + +:::note + +Unlike real life cameras, the adverse effects of increasing ISO sensitivity +or decreasing shutter speed (such as visible grain or light trails) are not +simulated in Redot. + +::: + +See [doc_physical_light_and_camera_units_setting_up_physical_camera_units](doc_physical_light_and_camera_units_setting_up_physical_camera_units) +for a description of CameraAttributesPhysical properties that are also available when +**not** using physical light units. + +### Configure the environment + +:::warning + +The default configuration is designed for daytime outdoor scenes. Night-time +and indoor scenes will need adjustments to the DirectionalLight3D and +WorldEnvironment background intensity to look correct. Otherwise, positional +lights will be barely visible at their default intensity. + +::: + +If you haven't added a [class_WorldEnvironment](class_WorldEnvironment) and [class_Camera3D](class_Camera3D) +node to the current scene yet, do so now by clicking the 3 vertical dots at the +top of the 3D editor viewport. Click **Add Sun to Scene**, open the dialog again +then click **Add Environment to Scene**. + +After enabling physical light units, a new property becomes available to edit in +the [class_Environment](class_Environment) resource: + +- **Background Intensity:** The background sky's intensity in + [nits](https://en.wikipedia.org/wiki/Candela_per_square_metre) + (candelas per square meter). This also affects ambient and reflected light if + their respective modes are set to **Background**. If a custom **Background Energy** + is set, this energy is multiplied by the intensity. + +### Configure the light nodes + +After enabling physical light units, 2 new properties become available in Light3D nodes: + +- **Intensity:** The light's intensity in [lux](https://en.wikipedia.org/wiki/Lux) (DirectionalLight3D) or + [lumens](https://en.wikipedia.org/wiki/Lumen_(unit))_ (OmniLight3D/SpotLight3D). + If a custom **Energy** is set, this energy is multiplied by the intensity. +- **Temperature:** The light's *color temperature* defined in Kelvin. + If a custom **Color** is set, this color is multiplied by the color temperature. + +**OmniLight3D/SpotLight3D intensity** + +Lumens are a measure of luminous flux, which is the total amount of visible +light emitted by a light source per unit of time. + +For SpotLight3Ds, we assume that the area outside the visible cone is surrounded +by a perfect light absorbing material. Accordingly, the apparent brightness of +the cone area does *not* change as the cone increases and decreases in size. + +A typical household lightbulb can range from around 600 lumens to 1200 lumens. +A candle is about 13 lumens, while a streetlight can be approximately 60000 lumens. + +**DirectionalLight3D intensity** + +Lux is a measure pf luminous flux per unit area, it is equal to one lumen per +square metre. Lux is the measure of how much light hits a surface at a given +time. + +With DirectionalLight3D, on a clear sunny day, a surface in direct sunlight may +receive approximately 100000 lux. A typical room in a home may receive +approximately 50 lux, while the moonlit ground may receive approximately 0.1 +lux. + +**Color temperature** + +6500 Kelvin is white. Higher values result in colder (bluer) colors, while lower +values result in warmer (more orange) colors. + +The sun on a cloudy day is approximately 6500 Kelvin. On a clear day, the sun is +between 5500 to 6000 Kelvin. On a clear day at sunrise or sunset, the sun ranges +to around 1850 Kelvin. + +
+ Color temperature chart from 1,000 Kelvin (left) to 12,500 Kelvin (right) +
+ Color temperature chart from 1,000 Kelvin (left) to 12,500 Kelvin (right) +
+
+ +Other Light3D properties such as **Energy** and **Color** remain editable for +animation purposes, and when you occasionally need to create lights with +non-realistic properties. + +## Setting up physical camera units + +Physical camera units can be enabled separately from physical light units. + +After adding a [class_CameraAttributesPhysical](class_CameraAttributesPhysical) resource to the **Camera +Attributes** property of a Camera3D nodes, some properties such as **FOV** will +no longer be editable. Instead, these properties are now governed by the +CameraAttributesPhysical's properties, such as focal length and aperture. + +CameraAttributesPhysical offers the following properties in its **Frustum** section: + +- **Focus Distance:** Distance from camera of object that will be in focus, + measured in meters. Internally, this will be clamped to be at least 1 + millimeter larger than the **Focal Length**. +- **Focal Length:** Distance between camera lens and camera aperture, measured + in millimeters. Controls field of view and depth of field. A larger focal + length will result in a smaller field of view and a narrower depth of field + meaning fewer objects will be in focus. A smaller focal length will result in + a wider field of view and a larger depth of field, which means more objects will be + in focus. This property overrides the Camera3D's **FOV** and **Keep Aspect** + properties, making them read-only in the inspector. +- **Near/Far:** The near and far clip distances in meters. These behave the same + as the Camera3D properties of the same name. Lower **Near** values allow the + camera to display objects that are very close, at the cost of potential + precision (Z-fighting) issues in the distance. Higher **Far** values allow the + camera to see further away, also at the cost of potential precision + (Z-fighting) issues in the distance. + +The default focal length of 35 mm corresponds to a wide angle lens. It still +results in a field of view that is noticeably narrower compared to the default +"practical" vertical FOV of 75 degrees. This is because non-gaming use cases +such as filmmaking and photography favor using a narrower field of view for a +more cinematic appearance. + +Common focal length values used in filmmaking and photography are: + +- **Fisheye (ultrawide angle):** Below 15 mm. Nearly no depth of field visible. +- **Wide angle:** Between 15 mm and 50 mm. Reduced depth of field. +- **Standard:** Between 50 mm and 100 mm. Standard depth of field. +- **Telephoto:** Greater than 100 mm. Increased depth of field. + +Like when using the **Keep Height** aspect mode, the effective field of view +depends on the viewport's aspect ratio, with wider aspect ratios automatically +resulting in a wider *horizontal* field of view. + +Automatic exposure adjustment based on the camera's average brightness level can +also be enabled in the **Auto Exposure** section, with the following properties: + +- **Min Sensitivity:** The darkest brightness the camera is allowed to get to, + measured in EV100. +- **Max Sensitivity:** The brightest the camera is allowed to get to, measured in EV100. +- **Speed:** The speed of the auto exposure effect. Affects the time needed for + the camera to perform auto exposure. Higher values allow for faster + transitions, but the resulting adjustments may look distracting depending on + the scene. +- **Scale:** The scale of the auto exposure effect. Affects the intensity of + auto exposure. + +EV100 is an exposure value (EV) measured at an ISO sensitivity of 100. See +[this table](https://en.wikipedia.org/wiki/Exposure_value#Tabulated_exposure_values) +for common EV100 values found in real life. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/procedural_geometry/arraymesh.md b/Redot-Documentation/docs/26.1/Tutorials/3d/procedural_geometry/arraymesh.md new file mode 100644 index 0000000..23198ea --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/procedural_geometry/arraymesh.md @@ -0,0 +1,450 @@ + +# Using the ArrayMesh + +This tutorial will present the basics of using an [ArrayMesh ](class_arraymesh). + +To do so, we will use the function [add_surface_from_arrays() ](class_ArrayMesh_method_add_surface_from_arrays), +which takes up to five parameters. The first two are required, while the last three are optional. + +The first parameter is the ``PrimitiveType``, an OpenGL concept that instructs the GPU +how to arrange the primitive based on the vertices given, i.e. whether they represent triangles, +lines, points, etc. See [Mesh.PrimitiveType ](enum_Mesh_PrimitiveType) for the options available. + +The second parameter, ``arrays``, is the actual Array that stores the mesh information. The array is a normal Redot array that +is constructed with empty brackets ``[]``. It stores a ``Packed**Array`` (e.g. PackedVector3Array, +PackedInt32Array, etc.) for each type of information that will be used to build the surface. + +Common elements of ``arrays`` are listed below, together with the position they must have within ``arrays``. +See [Mesh.ArrayType ](enum_Mesh_ArrayType) for a full list. + +.. list-table:: + :class: wrap-normal + :width: 100% + :widths: auto + :header-rows: 1 + + * - Index + - Mesh.ArrayType Enum + - Array type + + * - 0 + - ``ARRAY_VERTEX`` + - [PackedVector3Array ](class_PackedVector3Array) or [PackedVector2Array ](class_PackedVector2Array) + + * - 1 + - ``ARRAY_NORMAL`` + - [PackedVector3Array ](class_PackedVector3Array) + + * - 2 + - ``ARRAY_TANGENT`` + - [PackedFloat32Array ](class_PackedFloat32Array) or [PackedFloat64Array ](class_PackedFloat64Array) of groups of 4 floats. The first 3 floats determine the tangent, and the last float the binormal + direction as -1 or 1. + + * - 3 + - ``ARRAY_COLOR`` + - [PackedColorArray ](class_PackedColorArray) + + * - 4 + - ``ARRAY_TEX_UV`` + - [PackedVector2Array ](class_PackedVector2Array) or [PackedVector3Array ](class_PackedVector3Array) + + * - 5 + - ``ARRAY_TEX_UV2`` + - [PackedVector2Array ](class_PackedVector2Array) or [PackedVector3Array ](class_PackedVector3Array) + + * - 10 + - ``ARRAY_BONES`` + - [PackedFloat32Array ](class_PackedFloat32Array) of groups of 4 floats or [PackedInt32Array ](class_PackedInt32Array) of groups of 4 ints. Each group lists indexes of 4 bones that affects a given vertex. + + * - 11 + - ``ARRAY_WEIGHTS`` + - [PackedFloat32Array ](class_PackedFloat32Array) or [PackedFloat64Array ](class_PackedFloat64Array) of groups of 4 floats. Each float lists the amount of weight the corresponding bone in ``ARRAY_BONES`` has on a given vertex. + + * - 12 + - ``ARRAY_INDEX`` + - [PackedInt32Array ](class_PackedInt32Array) + +In most cases when creating a mesh, we define it by its vertex positions. So usually, the array of vertices (at index 0) is required, while the index array (at index 12) is optional and +will only be used if included. It is also possible to create a mesh with only the index array and no vertex array, but that's beyond the scope of this tutorial. + +All the other arrays carry information about the vertices. They are optional and will only be used if included. Some of these arrays (e.g. ``ARRAY_COLOR``) +use one entry per vertex to provide extra information about vertices. They must have the same size as the vertex array. Other arrays (e.g. ``ARRAY_TANGENT``) use +four entries to describe a single vertex. These must be exactly four times larger than the vertex array. + +For normal usage, the last three parameters in [add_surface_from_arrays() ](class_arraymesh_method_add_surface_from_arrays) are typically left empty. + +## Setting up the ArrayMesh + +In the editor, create a [MeshInstance3D ](class_meshinstance3d) and add an [ArrayMesh ](class_arraymesh) to it in the Inspector. +Normally, adding an ArrayMesh in the editor is not useful, but in this case it allows us to access the ArrayMesh +from code without creating one. + +Next, add a script to the MeshInstance3D. + +Under ``_ready()``, create a new Array. + + + + + +```gdscript +var surface_array = [] + +``` + + + + + +```csharp +Godot.Collections.Array surfaceArray = []; + +``` + + + + + +This will be the array that we keep our surface information in - it will hold +all the arrays of data that the surface needs. Redot will expect it to be of +size ``Mesh.ARRAY_MAX``, so resize it accordingly. + + + + + +```gdscript +var surface_array = [] +surface_array.resize(Mesh.ARRAY_MAX) + +``` + + + + + +```csharp +Godot.Collections.Array surfaceArray = []; +surfaceArray.Resize((int)Mesh.ArrayType.Max); + +``` + + + + + +Next create the arrays for each data type you will use. + + + + + +```gdscript +var verts = PackedVector3Array() +var uvs = PackedVector2Array() +var normals = PackedVector3Array() +var indices = PackedInt32Array() + +``` + + + + + +```csharp +List verts = []; +List uvs = []; +List normals = []; +List indices = []; + +``` + + + + + +Once you have filled your data arrays with your geometry you can create a mesh +by adding each array to ``surface_array`` and then committing to the mesh. + + + + + +```gdscript +surface_array[Mesh.ARRAY_VERTEX] = verts +surface_array[Mesh.ARRAY_TEX_UV] = uvs +surface_array[Mesh.ARRAY_NORMAL] = normals +surface_array[Mesh.ARRAY_INDEX] = indices + +# No blendshapes, lods, or compression used. +mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array) + +``` + + + + + +```csharp +surfaceArray[(int)Mesh.ArrayType.Vertex] = verts.ToArray(); +surfaceArray[(int)Mesh.ArrayType.TexUV] = uvs.ToArray(); +surfaceArray[(int)Mesh.ArrayType.Normal] = normals.ToArray(); +surfaceArray[(int)Mesh.ArrayType.Index] = indices.ToArray(); + +var arrMesh = Mesh as ArrayMesh; +if (arrMesh != null) +{ + // No blendshapes, lods, or compression used. + arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, surfaceArray); +} + +``` + + + + + +:::note +In this example, we used ``Mesh.PRIMITIVE_TRIANGLES``, but you can use any primitive type +available from mesh. + +::: + +Put together, the full code looks like: + + + + + +```gdscript +extends MeshInstance3D + +func _ready(): + var surface_array = [] + surface_array.resize(Mesh.ARRAY_MAX) + + # PackedVector**Arrays for mesh construction. + var verts = PackedVector3Array() + var uvs = PackedVector2Array() + var normals = PackedVector3Array() + var indices = PackedInt32Array() + + ####################################### + ## Insert code here to generate mesh ## + ####################################### + + # Assign arrays to surface array. + surface_array[Mesh.ARRAY_VERTEX] = verts + surface_array[Mesh.ARRAY_TEX_UV] = uvs + surface_array[Mesh.ARRAY_NORMAL] = normals + surface_array[Mesh.ARRAY_INDEX] = indices + + # Create mesh surface from mesh array. + # No blendshapes, lods, or compression used. + mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array) + +``` + + + + + +```csharp +public partial class MyMeshInstance3D : MeshInstance3D +{ + public override void _Ready() + { + Godot.Collections.Array surfaceArray = []; + surfaceArray.Resize((int)Mesh.ArrayType.Max); + + // C# arrays cannot be resized or expanded, so use Lists to create geometry. + List verts = []; + List uvs = []; + List normals = []; + List indices = []; + + /*********************************** + * Insert code here to generate mesh. + * *********************************/ + + // Convert Lists to arrays and assign to surface array + surfaceArray[(int)Mesh.ArrayType.Vertex] = verts.ToArray(); + surfaceArray[(int)Mesh.ArrayType.TexUV] = uvs.ToArray(); + surfaceArray[(int)Mesh.ArrayType.Normal] = normals.ToArray(); + surfaceArray[(int)Mesh.ArrayType.Index] = indices.ToArray(); + + var arrMesh = Mesh as ArrayMesh; + if (arrMesh != null) + { + // Create mesh surface from mesh array + // No blendshapes, lods, or compression used. + arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, surfaceArray); + } + } +} + +``` + + + + + +The code that goes in the middle can be whatever you want. Below we will present some +example code for generating a sphere. + +## Generating geometry + +Here is sample code for generating a sphere. Although the code is presented in +GDScript, there is nothing Redot specific about the approach to generating it. +This implementation has nothing in particular to do with ArrayMeshes and is just a +generic approach to generating a sphere. If you are having trouble understanding it +or want to learn more about procedural geometry in general, you can use any tutorial +that you find online. + + + + + +```gdscript +extends MeshInstance3D + +var rings = 50 +var radial_segments = 50 +var radius = 1 + +func _ready(): + + # Insert setting up the PackedVector**Arrays here. + + # Vertex indices. + var thisrow = 0 + var prevrow = 0 + var point = 0 + + # Loop over rings. + for i in range(rings + 1): + var v = float(i) / rings + var w = sin(PI * v) + var y = cos(PI * v) + + # Loop over segments in ring. + for j in range(radial_segments + 1): + var u = float(j) / radial_segments + var x = sin(u * PI * 2.0) + var z = cos(u * PI * 2.0) + var vert = Vector3(x * radius * w, y * radius, z * radius * w) + verts.append(vert) + normals.append(vert.normalized()) + uvs.append(Vector2(u, v)) + point += 1 + + # Create triangles in ring using indices. + if i > 0 and j > 0: + indices.append(prevrow + j - 1) + indices.append(prevrow + j) + indices.append(thisrow + j - 1) + + indices.append(prevrow + j) + indices.append(thisrow + j) + indices.append(thisrow + j - 1) + + prevrow = thisrow + thisrow = point + + # Insert committing to the ArrayMesh here. + +``` + + + + + +```csharp +public partial class MyMeshInstance3D : MeshInstance3D +{ + private int _rings = 50; + private int _radialSegments = 50; + private float _radius = 1; + + public override void _Ready() + { + // Insert setting up the surface array and lists here. + + // Vertex indices. + var thisRow = 0; + var prevRow = 0; + var point = 0; + + // Loop over rings. + for (var i = 0; i < _rings + 1; i++) + { + var v = ((float)i) / _rings; + var w = Mathf.Sin(Mathf.Pi * v); + var y = Mathf.Cos(Mathf.Pi * v); + + // Loop over segments in ring. + for (var j = 0; j < _radialSegments + 1; j++) + { + var u = ((float)j) / _radialSegments; + var x = Mathf.Sin(u * Mathf.Pi * 2); + var z = Mathf.Cos(u * Mathf.Pi * 2); + var vert = new Vector3(x * _radius * w, y * _radius, z * _radius * w); + verts.Add(vert); + normals.Add(vert.Normalized()); + uvs.Add(new Vector2(u, v)); + point += 1; + + // Create triangles in ring using indices. + if (i > 0 && j > 0) + { + indices.Add(prevRow + j - 1); + indices.Add(prevRow + j); + indices.Add(thisRow + j - 1); + + indices.Add(prevRow + j); + indices.Add(thisRow + j); + indices.Add(thisRow + j - 1); + } + } + + prevRow = thisRow; + thisRow = point; + } + + // Insert committing to the ArrayMesh here. + } +} + +``` + + + + + +## Saving + +Finally, we can use the [ResourceSaver ](class_resourcesaver) class to save the ArrayMesh. +This is useful when you want to generate a mesh and then use it later without having to re-generate it. + + + + + +```gdscript +# Saves mesh to a .tres file with compression enabled. +ResourceSaver.save(mesh, "res://sphere.tres", ResourceSaver.FLAG_COMPRESS) + +``` + + + + + +```csharp +// Saves mesh to a .tres file with compression enabled. +ResourceSaver.Save(Mesh, "res://sphere.tres", ResourceSaver.SaverFlags.Compress); +``` + + + + diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/procedural_geometry/immediatemesh.md b/Redot-Documentation/docs/26.1/Tutorials/3d/procedural_geometry/immediatemesh.md new file mode 100644 index 0000000..1c39c45 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/procedural_geometry/immediatemesh.md @@ -0,0 +1,123 @@ + +# Using ImmediateMesh + +The [ImmediateMesh ](class_ImmediateMesh) is a convenient tool to create +dynamic geometry using an OpenGL 1.x-style API. Which makes it both approachable +to use and efficient for meshes which need to be updated every frame. + +Generating complex geometry (several thousand vertices) with this tool is inefficient, even if it's +done only once. Instead, it is designed to generate simple geometry that changes every frame. + +First, you need to create a [MeshInstance3D ](class_meshinstance3d) and add +an [ImmediateMesh ](class_ImmediateMesh) to it in the Inspector. + +Next, add a script to the MeshInstance3D. The code for the ImmediateMesh should +go in the ``_process()`` function if you want it to update each frame, or in the +``_ready()`` function if you want to create the mesh once and not update it. If +you only generate a surface once, the ImmediateMesh is just as efficient as any +other kind of mesh as the generated mesh is cached and reused. + +To begin generating geometry you must call ``surface_begin()``. +``surface_begin()`` takes a ``PrimitiveType`` as an argument. ``PrimitiveType`` +instructs the GPU how to arrange the primitive based on the vertices given +whether it is triangles, lines, points, etc. A complete list can be found under +the [Mesh ](class_mesh) class reference page. + +Once you have called ``surface_begin()`` you are ready to start adding vertices. +You add vertices one at a time. First you add vertex specific attributes such as +normals or UVs using ``surface_set_****()`` (e.g. ``surface_set_normal()``). +Then you call ``surface_add_vertex()`` to add a vertex with those attributes. +For example: + + + + + +```gdscript +# Add a vertex with normal and uv. +surface_set_normal(Vector3(0, 1, 0)) +surface_set_uv(Vector2(1, 1)) +surface_add_vertex(Vector3(0, 0, 1)) + +``` + + + + + +Only attributes added before the call to ``surface_add_vertex()`` will be +included in that vertex. If you add an attribute twice before calling +``surface_add_vertex()``, only the second call will be used. + +Finally, once you have added all your vertices call ``surface_end()`` to signal +that you have finished generating the surface. You can call ``surface_begin()`` +and ``surface_end()`` multiple times to generate multiple surfaces for the mesh. + +The example code below draws a single triangle in the ``_ready()`` function. + + + + + +```gdscript +extends MeshInstance3D + +func _ready(): + # Begin draw. + mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLES) + + # Prepare attributes for add_vertex. + mesh.surface_set_normal(Vector3(0, 0, 1)) + mesh.surface_set_uv(Vector2(0, 0)) + # Call last for each vertex, adds the above attributes. + mesh.surface_add_vertex(Vector3(-1, -1, 0)) + + mesh.surface_set_normal(Vector3(0, 0, 1)) + mesh.surface_set_uv(Vector2(0, 1)) + mesh.surface_add_vertex(Vector3(-1, 1, 0)) + + mesh.surface_set_normal(Vector3(0, 0, 1)) + mesh.surface_set_uv(Vector2(1, 1)) + mesh.surface_add_vertex(Vector3(1, 1, 0)) + + # End drawing. + mesh.surface_end() + +``` + + + + + +The ImmediateMesh can also be used across frames. Each time you call +``surface_begin()`` and ``surface_end()``, you are adding a new surface to the +ImmediateMesh. If you want to recreate the mesh from scratch each frame, call +``clear_surfaces()`` before calling ``surface_begin()``. + + + + + +```gdscript +extends MeshInstance3D + +func _process(delta): + + # Clean up before drawing. + mesh.clear_surfaces() + + # Begin draw. + mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLES) + + # Draw mesh. + + # End drawing. + mesh.surface_end() + +``` + + + + + +The above code will dynamically create and draw a single surface each frame. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/procedural_geometry/index.md b/Redot-Documentation/docs/26.1/Tutorials/3d/procedural_geometry/index.md new file mode 100644 index 0000000..f793f65 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/procedural_geometry/index.md @@ -0,0 +1,11 @@ +# Procedural geometry + +This section contains tutorials and documentation about procedural geometry in Redot Engine. + +## Articles + +- [Using the ArrayMesh](arraymesh) +- [Using ImmediateMesh](immediatemesh) +- [Using the MeshDataTool](meshdatatool) +- [Using the SurfaceTool](surfacetool) + diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/procedural_geometry/meshdatatool.md b/Redot-Documentation/docs/26.1/Tutorials/3d/procedural_geometry/meshdatatool.md new file mode 100644 index 0000000..9567572 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/procedural_geometry/meshdatatool.md @@ -0,0 +1,157 @@ + +# Using the MeshDataTool + +The [MeshDataTool ](class_meshdatatool) is not used to generate geometry. But it is helpful for dynamically altering geometry, for example +if you want to write a script to tessellate, simplify, or deform meshes. + +The MeshDataTool is not as fast as altering arrays directly using ArrayMesh. However, it provides more information +and tools to work with meshes than the ArrayMesh does. When the MeshDataTool +is used, it calculates mesh data that is not available in ArrayMeshes such as faces and edges, which are necessary +for certain mesh algorithms. If you do not need this extra information then it may be better to use an ArrayMesh. + +:::note +MeshDataTool can only be used on Meshes that use the PrimitiveType ``Mesh.PRIMITIVE_TRIANGLES``. + +::: + +We initialize the MeshDataTool from an ArrayMesh by calling ``create_from_surface()``. If there is already data initialized in the MeshDataTool, +calling ``create_from_surface()`` will clear it for you. Alternatively, you can call ``clear()`` yourself before re-using the MeshDataTool. + +In the examples below, assume an ArrayMesh called ``mesh`` has already been created. See [ArrayMesh tutorial ](arraymesh.md) for an example of mesh generation. + + + + + +```gdscript +var mdt = MeshDataTool.new() +mdt.create_from_surface(mesh, 0) + +``` + + + + + +``create_from_surface()`` uses the vertex arrays from the ArrayMesh to calculate two additional arrays, +one for edges and one for faces, for a total of three arrays. + +An edge is a connection between any two vertices. Each edge in the edge array contains a reference to +the two vertices it is composed of, and up to two faces that it is contained within. + +A face is a triangle made up of three vertices and three corresponding edges. Each face in the face array contains +a reference to the three vertices and three edges it is composed of. + +The vertex array contains edge, face, normal, color, tangent, uv, uv2, bone, and weight information connected +with each vertex. + +To access information from these arrays you use a function of the form ``get_****()``: + + + + + +```gdscript +mdt.get_vertex_count() # Returns number of vertices in vertex array. +mdt.get_vertex_faces(0) # Returns array of faces that contain vertex[0]. +mdt.get_face_normal(1) # Calculates and returns face normal of the second face. +mdt.get_edge_vertex(10, 1) # Returns the second vertex comprising the edge at index 10. + +``` + + + + + +What you choose to do with these functions is up to you. A common use case is to iterate over all vertices +and transform them in some way: + + + + + +```gdscript +for i in range(get_vertex_count): + var vert = mdt.get_vertex(i) + vert *= 2.0 # Scales the vertex by doubling size. + mdt.set_vertex(i, vert) + +``` + + + + + +These modifications are not done in place on the ArrayMesh. If you are dynamically updating an existing ArrayMesh, +first delete the existing surface before adding a new one using [commit_to_surface() ](class_meshdatatool_method_commit_to_surface): + + + + + +```gdscript +mesh.clear_surfaces() # Deletes all of the mesh's surfaces. +mdt.commit_to_surface(mesh) + +``` + + + + + +Below is a complete example that turns a spherical mesh called ``mesh`` into a randomly deformed blob complete with updated normals and vertex colors. +See [ArrayMesh tutorial ](arraymesh.md) for how to generate the base mesh. + + + + + +```gdscript +extends MeshInstance3D + +var fnl = FastNoiseLite.new() +var mdt = MeshDataTool.new() + +func _ready(): + fnl.frequency = 0.7 + + mdt.create_from_surface(mesh, 0) + + for i in range(mdt.get_vertex_count()): + var vertex = mdt.get_vertex(i).normalized() + # Push out vertex by noise. + vertex = vertex * (fnl.get_noise_3dv(vertex) * 0.5 + 0.75) + mdt.set_vertex(i, vertex) + + # Calculate vertex normals, face-by-face. + for i in range(mdt.get_face_count()): + # Get the index in the vertex array. + var a = mdt.get_face_vertex(i, 0) + var b = mdt.get_face_vertex(i, 1) + var c = mdt.get_face_vertex(i, 2) + # Get vertex position using vertex index. + var ap = mdt.get_vertex(a) + var bp = mdt.get_vertex(b) + var cp = mdt.get_vertex(c) + # Calculate face normal. + var n = (bp - cp).cross(ap - bp).normalized() + # Add face normal to current vertex normal. + # This will not result in perfect normals, but it will be close. + mdt.set_vertex_normal(a, n + mdt.get_vertex_normal(a)) + mdt.set_vertex_normal(b, n + mdt.get_vertex_normal(b)) + mdt.set_vertex_normal(c, n + mdt.get_vertex_normal(c)) + + # Run through vertices one last time to normalize normals and + # set color to normal. + for i in range(mdt.get_vertex_count()): + var v = mdt.get_vertex_normal(i).normalized() + mdt.set_vertex_normal(i, v) + mdt.set_vertex_color(i, Color(v.x, v.y, v.z)) + + mesh.clear_surfaces() + mdt.commit_to_surface(mesh) +``` + + + + diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/procedural_geometry/surfacetool.md b/Redot-Documentation/docs/26.1/Tutorials/3d/procedural_geometry/surfacetool.md new file mode 100644 index 0000000..e723975 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/procedural_geometry/surfacetool.md @@ -0,0 +1,248 @@ + +# Using the SurfaceTool + +The [SurfaceTool ](class_surfacetool) provides a useful interface for constructing geometry. +The interface is similar to the [ImmediateMesh ](class_ImmediateMesh) class. You +set each per-vertex attribute (e.g. normal, uv, color) and then when you add a vertex it +captures the attributes. + +The SurfaceTool also provides some useful helper functions like ``index()`` and ``generate_normals()``. + +Attributes are added before each vertex is added: + + + + + +```gdscript +st.set_normal() # Overwritten by normal below. +st.set_normal() # Added to next vertex. +st.set_color() # Added to next vertex. +st.add_vertex() # Captures normal and color above. +st.set_normal() # Normal never added to a vertex. + +``` + + + + + +```csharp +st.SetNormal(); // Overwritten by normal below. +st.SetNormal(); // Added to next vertex. +st.SetColor(); // Added to next vertex. +st.AddVertex(); // Captures normal and color above. +st.SetNormal(); // Normal never added to a vertex. + +``` + + + + + +When finished generating your geometry with the [SurfaceTool ](class_surfacetool) +call ``commit()`` to finish generating the mesh. If an [ArrayMesh ](class_ArrayMesh) is passed +to ``commit()`` then it appends a new surface to the end of the ArrayMesh. While if nothing is passed +in, ``commit()`` returns an ArrayMesh. + + + + + +```gdscript +st.commit(mesh) +# Or: +var mesh = st.commit() + +``` + + + + + +```csharp +st.Commit(mesh); +// Or: +var mesh = st.Commit(); + +``` + + + + + +Code creates a triangle with indices + + + + + +```gdscript +var st = SurfaceTool.new() + +st.begin(Mesh.PRIMITIVE_TRIANGLES) + +# Prepare attributes for add_vertex. +st.set_normal(Vector3(0, 0, 1)) +st.set_uv(Vector2(0, 0)) +# Call last for each vertex, adds the above attributes. +st.add_vertex(Vector3(-1, -1, 0)) + +st.set_normal(Vector3(0, 0, 1)) +st.set_uv(Vector2(0, 1)) +st.add_vertex(Vector3(-1, 1, 0)) + +st.set_normal(Vector3(0, 0, 1)) +st.set_uv(Vector2(1, 1)) +st.add_vertex(Vector3(1, 1, 0)) + +# Commit to a mesh. +var mesh = st.commit() + +``` + + + + + +```csharp +var st = new SurfaceTool(); + +st.Begin(Mesh.PrimitiveType.Triangles); + +// Prepare attributes for AddVertex. +st.SetNormal(new Vector3(0, 0, 1)); +st.SetUV(new Vector2(0, 0)); +// Call last for each vertex, adds the above attributes. +st.AddVertex(new Vector3(-1, -1, 0)); + +st.SetNormal(new Vector3(0, 0, 1)); +st.SetUV(new Vector2(0, 1)); +st.AddVertex(new Vector3(-1, 1, 0)); + +st.SetNormal(new Vector3(0, 0, 1)); +st.SetUV(new Vector2(1, 1)); +st.AddVertex(new Vector3(1, 1, 0)); + +// Commit to a mesh. +var mesh = st.Commit(); + +``` + + + + + +You can optionally add an index array, either by calling ``add_index()`` and adding +vertices to the index array or by calling ``index()`` which shrinks the vertex array +to remove duplicate vertices. + + + + + +```gdscript +# Creates a quad from four corner vertices. +# add_index does not need to be called before add_vertex. +st.add_index(0) +st.add_index(1) +st.add_index(2) + +st.add_index(1) +st.add_index(3) +st.add_index(2) + +# Alternatively: +st.index() + +``` + + + + + +```csharp +// Creates a quad from four corner vertices. +// AddIndex does not need to be called before AddVertex. +st.AddIndex(0); +st.AddIndex(1); +st.AddIndex(2); + +st.AddIndex(1); +st.AddIndex(3); +st.AddIndex(2); + +// Alternatively: +st.Index(); + +``` + + + + + +Similarly, if you have an index array, but you want each vertex to be unique (e.g. because +you want to use unique normals or colors per face instead of per-vertex), you can call ``deindex()``. + + + + + +```gdscript +st.deindex() + +``` + + + + + +```csharp +st.Deindex(); + +``` + + + + + +If you don't add custom normals yourself, you can add them using ``generate_normals()``, which should +be called after generating geometry and before committing the mesh using ``commit()`` or +``commit_to_arrays()``. Calling ``generate_normals(true)`` will flip the resulting normals. As a side +note, ``generate_normals()`` only works if the primitive type is set to ``Mesh.PRIMITIVE_TRIANGLES``. + +You may notice that normal mapping or other material properties look broken on +the generated mesh. This is because normal mapping **requires** the mesh to +feature *tangents*, which are separate from *normals*. You can either add custom +tangents manually, or generate them automatically with +``generate_tangents()``. This method requires that each vertex have UVs and +normals set already. + + + + + +```gdscript +st.generate_normals() +st.generate_tangents() + +``` + + + + + +```csharp +st.GenerateNormals(); +st.GenerateTangents(); + +``` + + + + + +By default, when generating normals, they will be calculated on a per-face basis. If you want +smooth vertex normals, when adding vertices, call ``add_smooth_group()``. ``add_smooth_group()`` +needs to be called while building the geometry, e.g. before the call to ``add_vertex()`` +(if non-indexed) or ``add_index()`` (if indexed). \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/resolution_scaling.md b/Redot-Documentation/docs/26.1/Tutorials/3d/resolution_scaling.md new file mode 100644 index 0000000..0d79641 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/resolution_scaling.md @@ -0,0 +1,240 @@ + +# Resolution scaling + +## Why use resolution scaling? + +With the ever-increasing rendering complexity of modern games, rendering at +native resolution isn't always viable anymore, especially on lower-end GPUs. + +Resolution scaling is one of the most direct ways to influence the GPU +requirements of a scene. In scenes that are bottlenecked by the GPU (rather than +by the CPU), decreasing the resolution scale can improve performance +significantly. Resolution scaling is particularly important on mobile GPUs where +performance and power budgets are limited. + +While resolution scaling is an important tool to have, remember that resolution +scaling is not intended to be a replacement for decreasing graphics settings on +lower-end hardware. Consider exposing both resolution scale and graphics +settings in your in-game menus. + +:::info + +You can compare resolution scaling modes and factors in action using the +[3D Antialiasing demo project](https://github.com/redot-engine/redot-demo-projects/tree/master/3d/antialiasing). + +::: + +:::note + +Resolution scaling is currently not available for 2D rendering, but it can be +simulated using the ``viewport`` stretch mode. See [doc_multiple_resolutions](../rendering/multiple_resolutions.md) +for more information. + +::: + +## Resolution scaling options + +In the advanced Project Settings' **Rendering > Scaling 3D** section, you can +find several options for 3D resolution scaling: + +### Scaling mode + +- **Bilinear:** Standard bilinear filtering (default). +- **FSR 1.0:** [AMD FidelityFX Super Resolution 1.0](https://gpuopen.com/fidelityfx-superresolution/). + Slower, but higher quality compared to bilinear scaling. On very slow GPUs, + the cost of FSR1 may be too expensive to be worth using it over bilinear + scaling. +- **FSR 2.2:** AMD FidelityFX Super Resolution 2.2 (since Redot 4.2). Slowest, + but even higher quality compared to FSR1 and bilinear scaling. On slow GPUs, + the cost of FSR2 may be too expensive to be worth using it over bilinear + scaling or FSR1. To match FSR2 performance with FSR1, you need to use a lower + resolution scale factor. + +Here are comparison images between native resolution, bilinear scaling with 50% +resolution scale, FSR1, and FSR2 scaling with 50% resolution scale: + +![Image](/img/Tutorials/3d/img/resolution_scaling_bilinear_0.5.png) + +![Image](/img/Tutorials/3d/img/resolution_scaling_fsr1_0.5.png) + +![Image](/img/Tutorials/3d/img/resolution_scaling_fsr2_0.5.webp) + +FSR1 upscaling works best when coupled with another form of antialiasing. +Temporal antialiasing (TAA) or multisample antialiasing (MSAA) should preferably +be used in this case, as FXAA does not add temporal information and introduces +more blurring to the image. + +On the other hand, FSR2 provides its own temporal antialiasing. This means you +don't need to enable other antialiasing methods for the resulting image to look +smooth. The **Use TAA** project setting is ignored when FSR2 is used as the 3D +scaling method, since FSR2's temporal antialiasing takes priority. + +Here's the same comparison, but with 4× MSAA enabled on all images: + +![Image](/img/Tutorials/3d/img/resolution_scaling_bilinear_msaa_4x_0.5.png) + +![Image](/img/Tutorials/3d/img/resolution_scaling_fsr1_msaa_4x_0.5.png) + +![Image](/img/Tutorials/3d/img/resolution_scaling_fsr2_msaa_4x_0.5.webp) + +Notice how the edge upscaling of FSR1 becomes much more convincing once 4× +MSAA is enabled. However, FSR2 doesn't benefit much from enabling MSAA since it +already performs temporal antialiasing. + +### Rendering scale + +The **Rendering > Scaling 3D > Scale** setting adjusts the resolution scale. +``1.0`` represents the full resolution scale, with the 3D rendering resolution +matching the 2D rendering resolution. Resolution scales *below* ``1.0`` can be +used to speed up rendering, at the cost of a blurrier final image and more aliasing. + +The rendering scale can be adjusted at runtime by changing the ``scaling_3d_scale`` +property on a [class_Viewport](class_Viewport) node. + +Resolution scales *above* ``1.0`` can be used for supersample antialiasing +(SSAA). This will provide antialiasing at a *very* high performance cost, and is +**not recommended** for most use cases. See [doc_3d_antialiasing](3d_antialiasing.md) for more +information. + +The tables below list common screen resolutions, the resulting 3D rendering +resolution and the number of megapixels that need to be rendered each frame +depending on the rendering scale option. Rows are sorted from fastest to slowest +in each table. + +:::note + +The resolution scale is defined on a **per-axis** basis. For example, this +means that halving the resolution scale factor will reduce the number of +rendered megapixels per frame by a factor of 4, not 2. Therefore, very low +or very high resolution scale factors can have a greater performance impact +than expected. + +::: + +**1920×1080 (Full HD)** + +| Resolution scale factor | 3D rendering resolution | Megapixels rendered per frame | +| --- | --- | --- | +| ``0.50`` | 960×540 | 0.52 MPix | +| ``0.67`` | 1286×723 | 0.93 MPix | +| ``0.75`` | 1440×810 | 1.17 MPix | +| ``0.85`` | 1632×918 | 1.50 MPix | +| ``1.00`` **(native)** | **1920×1080** | **2.07 MPix** | +| ``1.33`` (supersampling) | 2553×1436 | 3.67 MPix | +| ``1.50`` (supersampling) | 2880×1620 | 4.67 MPix | +| ``2.00`` (supersampling) | 3840×2160 | 8.29 MPix | + +**2560×1440 (QHD)** + +| Resolution scale factor | 3D rendering resolution | Megapixels rendered per frame | +| --- | --- | --- | +| ``0.50`` | 1280×720 | 0.92 MPix | +| ``0.67`` | 1715×964 | 1.65 MPix | +| ``0.75`` | 1920×1080 | 2.07 MPix | +| ``0.85`` | 2176×1224 | 2.66 MPix | +| ``1.00`` **(native)** | **2560×1440** | **3.69 MPix** | +| ``1.33`` (supersampling) | 3404×1915 | 6.52 MPix | +| ``1.50`` (supersampling) | 3840×2160 | 8.29 MPix | +| ``2.00`` (supersampling) | 5120×2880 | 14.75 MPix | + +**3840×2160 (Ultra HD "4K")** + +| Resolution scale factor | 3D rendering resolution | Megapixels rendered per frame | +| --- | --- | --- | +| ``0.50`` | 1920×1080 | 2.07 MPix | +| ``0.67`` | 2572×1447 | 3.72 MPix | +| ``0.75`` | 2880×1620 | 4.67 MPix | +| ``0.85`` | 3264×1836 | 5.99 MPix | +| ``1.00`` **(native)** | **3840×2160** | **8.29 MPix** | +| ``1.33`` (supersampling) | 5107×2872 | 14.67 MPix | +| ``1.50`` (supersampling) | 5760×3240 | 18.66 MPix | +| ``2.00`` (supersampling) | 7680×4320 | 33.18 MPix | + +### FSR Sharpness + +When using the FSR1 or FSR2 scaling modes, the sharpness can be controlled using the +**Rendering > Scaling 3D > FSR Sharpness** advanced project setting. + +The intensity is inverted compared to most other sharpness sliders: *lower* +values will result in a sharper final image, while *higher* values will *reduce* +the impact of the sharpening filter. ``0.0`` is the sharpest, while ``2.0`` is +the least sharp. The default value of ``0.2`` provides a balance between +preserving the original image's sharpness and avoiding additional aliasing due +to oversharpening. + +:::note + +If you wish to use sharpening when rendering at native resolution, Redot +currently doesn't allow using the sharpening component of FSR1 (RCAS) +independently from the upscaling component (EASU). + +As a workaround, you can set the 3D rendering scale to ``0.99``, set the +scaling mode to **FSR 1.0** then adjust FSR sharpness as needed. This allows +using FSR1 while rendering at a near-native resolution. + +Alternatively, you can set the scaling mode to **FSR 2.2** with the 3D +rendering scale set to ``1.0`` if you have enough GPU headroom. This also +provides high-quality temporal antialiasing. The **FSR Sharpness** setting +remains functional in this case. + +::: + +### Mipmap bias + +Redot automatically uses a negative texture mipmap bias when the 3D resolution +scale is set below ``1.0``. This allows for better preservation of texture +detail at the cost of a grainy appearance on detailed textures. + +The texture LOD bias currently affects both 2D and 3D rendering in the same way. +However, keep in mind it only has an effect on textures with mipmaps enabled. +Textures used in 2D don't have mipmaps enabled by default, which means only 3D +rendering is affected unless you enabled mipmaps on 2D textures in the Import +dock. + +The formula used to determine the texture mipmap bias is: +``log2f(min(scaling_3d_scale, 1.0)) + custom_texture_mipmap_bias`` + +To counteract the blurriness added by some antialiasing methods, Redot also adds +a ``-0.25`` offset when FXAA is enabled, and a ``-0.5`` offset when TAA is +enabled. If both are enabled at the same time, a ``-0.75`` offset is used. This +mipmap bias offset is applied *before* the resolution scaling offset, so it does +not change depending on resolution scale. + +The texture LOD bias can manually be changed by adjusting the **Rendering > +Textures > Default Filters > Texture Mipmap Bias** advanced project setting. It +can also be changed at runtime on [Viewports](class_Viewport) by +adjusting the ``texture_mipmap_bias`` property. + +:::warning + +Adjusting the mipmap LOD bias manually can be useful in certain scenarios, +but this should be done carefully to prevent the final image from looking +grainy in motion. + +*Negative* mipmap LOD bias can also decrease performance due to +higher-resolution mips having to be sampled further away. Recommended values +for a manual offset are between ``-0.5`` and ``0.0``. + +*Positive* mipmap LOD bias will make mipmapped textures appear blurrier than +intended. This may improve performance slightly, but is otherwise not +recommended as the loss in visual quality is usually not worth the +performance gain. + +::: + +The example below shows an extreme case, with a mipmap LOD bias of ``-1.0`` and +anisotropic filtering disabled to make the difference more noticeable: + +![Image](/img/Tutorials/3d/img/resolution_scaling_texture_mipmap_bias_comparison.png) + +## Troubleshooting + +### Performance does not increase much when decreasing resolution scale + +If performance doesn't increase much when decreasing resolution scale to a value +like ``0.5``, it likely means the performance bottleneck is elsewhere in your +scene. For example, your scene could have too many draw calls, causing a CPU +bottleneck to occur. Likewise, you may have too many graphics effects enabled +for your GPU to handle (such as SDFGI, SSAO or SSR). + +See the [doc_performance](../performance/index.md) tutorials for more information. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/spring_arm.md b/Redot-Documentation/docs/26.1/Tutorials/3d/spring_arm.md new file mode 100644 index 0000000..4d1b06d --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/spring_arm.md @@ -0,0 +1,120 @@ + +# Third-person camera with spring arm + +## Introduction + +3D games will often have a third-person camera that follows and +rotates around something such as a player character or a vehicle. + +In Redot, this can be done by setting a [Camera3D](class_Camera3D) as a child of a node. +However, if you try this without any extra steps, you'll notice that the camera clips through geometry and hides the scene. + +This is where the [SpringArm3D](class_SpringArm3D) node comes in. + +## What is a spring arm? + +A spring arm has two main components that affect its behavior. + +The "length" of the spring arm is how far from its global position to check for collisions: + +![Image](/img/Tutorials/3d/img/spring_arm_position_length.webp) + +The "shape" of the spring arm is what it uses to check for collisions. The spring arm will "sweep" this shape from its origin out towards its length. + +![Image](/img/Tutorials/3d/img/spring_arm_shape.webp) + +The spring arm tries to keep all of its children at the end of its length. When the shape collides with something, the children are instead placed at or near that collision point: + +![Image](/img/Tutorials/3d/img/spring_arm_children.webp) + +## Spring arm with a camera + +When a camera is placed as a child of a spring arm, a pyramid representing the camera will be used as the shape. + +This pyramid represents the **near plane** of the camera: + +![Image](/img/Tutorials/3d/img/spring_arm_camera_shape.webp) + +:::note +If the spring arm is given a specific shape, then that shape will **always** be used. +The camera's shape is only used if the camera is a **direct child** of the spring arm. + +If no shape is provided and the camera is not a direct child, the spring arm will fall back to using a ray cast which is inaccurate for camera collisions and not recommended. + +::: + +Every physics process frame, the spring arm will perform a motion cast to check if anything is collided with: + +![Image](/img/Tutorials/3d/img/spring_arm_camera_motion_cast.webp) + +When the shape hits something, the camera will be placed at or near the collision point: + +![Image](/img/Tutorials/3d/img/spring_arm_camera_collision.webp) + +## Setting up the spring arm and camera + +Let's add a spring arm camera setup to the platformer demo. + +:::note +You can download the Platformer 3D demo on `GitHub `_ or using the `Asset Library `_. + +::: + +In general, for a third-person camera setup, you will have three nodes as children of the node that you're following: + +- `Node3D` (the "pivot point" for the camera) + + - `SpringArm3D` + + - `Camera3D` + +Open the ``player/player.tscn`` scene. Set these up as children of our player and give them unique names so we can find them in our script. **Make sure to delete the existing camera node!** + +![Image](/img/Tutorials/3d/img/spring_arm_editor_setup.webp) + +Let's move the pivot point up by ``2`` on the Y-axis so that it's not on the ground: + +![Image](/img/Tutorials/3d/img/spring_arm_pivot_setup.webp) + +Give the spring arm a length of ``3`` so that it is placed behind the character: + +![Image](/img/Tutorials/3d/img/spring_arm_length_setup.webp) + +:::note +Leave the **Shape** of the spring arm as ````. This way, it will use the camera's pyramid shape. +If you want, you can also try other shapes - a sphere is a common choice since it slides smoothly along edges. + +::: + +Update the top of ``player/player.gd`` to grab the camera and the pivot points by their unique names: + +```gdscript +# Comment out this existing camera line. +# @onready var _camera := $Target/Camera3D as Camera3D + +@onready var _camera := %Camera3D as Camera3D +@onready var _camera_pivot := %CameraPivot as Node3D + +``` + +Add an ``_unhandled_input`` function to check for camera movement and then rotate the pivot point accordingly: + +```gdscript +@export_range(0.0, 1.0) var mouse_sensitivity = 0.01 +@export var tilt_limit = deg_to_rad(75) + +func _unhandled_input(event: InputEvent) -> void: + if event is InputEventMouseMotion: + _camera_pivot.rotation.x -= event.relative.y * mouse_sensitivity + # Prevent the camera from rotating too far up or down. + _camera_pivot.rotation.x = clampf(_camera_pivot.rotation.x, -tilt_limit, tilt_limit) + _camera_pivot.rotation.y += -event.relative.x * mouse_sensitivity + +``` + +By rotating the pivot point, the spring arm will also be rotated and it will change where the camera is positioned. +Run the game and notice that mouse movement now rotates the camera around the character. If the camera moves into a wall, it collides with it. + + diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/standard_material_3d.md b/Redot-Documentation/docs/26.1/Tutorials/3d/standard_material_3d.md new file mode 100644 index 0000000..bd8c754 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/standard_material_3d.md @@ -0,0 +1,730 @@ + +# Standard Material 3D and ORM Material 3D + +## Introduction + +``StandardMaterial3D`` and ``ORMMaterial3D`` (Occlusion, Roughness, Metallic) +are default 3D materials that aim to provide most of the features artists look +for in a material, without the need for writing shader code. However, they can +be converted to shader code if additional functionality is needed. + +This tutorial explains the parameters present in both materials. + +There are 4 ways to add these materials to an object. A material can be added in +the *Material* property of the mesh. It can be added in the *Material* property of +the node using the mesh (such as a MeshInstance3D node), the *Material Override* property +of the node using the mesh, and the *Material Overlay*. + +![Image](/img/Tutorials/3d/img/add_material.webp) + +If you add a material to the mesh itself, every time that mesh is used it will have that +material. If you add a material to the node using the mesh, the material will only be used +by that node, it will also override the material property of the mesh. If a material is +added in the *Material Override* property of the node, it will only be used by that node. +It will also override the regular material property of the node and the material property of +the mesh. + +The *Material Overlay* property will render a material **over** the current one being used by +the mesh. As an example, this can be used to put a transparent shield effect on a mesh. + +## BaseMaterial 3D settings + +StandardMaterial3D has many settings that determine the look of a material. All of these are +under the BaseMaterial3D category + +![Image](/img/Tutorials/3d/img/spatial_material1.webp) + +ORM materials are almost exactly the same with one difference. Instead of separate settings +and textures for occlusion, roughness, and metallic, there is a single ORM texture. The different +color channels of that texture are used for each parameter. Programs such as Substance Painter +and Armor Paint will give you the option to export in this format, for these two programs it's +with the export preset for unreal engine, which also uses ORM textures. + +## Transparency + +By default, materials in Redot are opaque. This is fast to render, but it means +the material can't be seen through even if you use a transparent texture in the +**Albedo > Texture** property (or set **Albedo > Color** to a transparent color). + +To be able to see through a material, the material needs to be made *transparent*. +Redot offers several transparency modes: + +- **Disabled:** Material is opaque. This is the fastest to render, with all + rendering features supported. + +- **Alpha:** Material is transparent. Semi-transparent areas are drawn with + blending. This is slow to render, but it allows for partial transparency (also + known as translucency). Materials using alpha blending also can't cast + shadows, and are not visible in screen-space reflections. + + - **Alpha** is a good fit for particle effects and VFX. + +- **Alpha Scissor:** Material is transparent. Semi-transparent areas whose + opacity is below **Alpha Scissor Threshold** are not drawn (above this + opacity, these are drawn as opaque). This is faster to render than Alpha and + doesn't exhibit transparency sorting issues. The downside is that this results + in "all or nothing" transparency, with no intermediate values possible. + Materials using alpha scissor can cast shadows. + + - **Alpha Scissor** is ideal for foliage and fences, since these have hard + edges and require correct sorting to look good. + +- **Alpha Hash:** Material is transparent. Semi-transparent areas are drawn + using dithering. This is also "all or nothing" transparency, but dithering + helps represent partially opaque areas with limited precision depending on + viewport resolution. Materials using alpha hash can cast shadows. + + - **Alpha Hash** is suited for realistic-looking hair, although stylized hair + may work better with alpha scissor. + +- **Depth Pre-Pass:** This renders the object's fully opaque pixels via the + opaque pipeline first, then renders the rest with alpha blending. This allows + transparency sorting to be *mostly* correct (albeit not fully so, as partially + transparent regions may still exhibit incorrect sorting). Materials using + depth prepass can cast shadows. + +:::note + +Redot will automatically force the material to be transparent with alpha +blending if *any* of these conditions is met: + +- Setting the transparency mode to **Alpha** (as described here). +- Setting a blend mode other than the default **Mix** +- Enabling **Refraction**, **Proximity Fade**, or **Distance Fade**. + +::: + +Comparison between alpha blending (left) and alpha scissor (right) transparency: + +![Image](/img/Tutorials/3d/img/spatial_material12.png) + +:::warning + +Alpha-blended transparency has several +[limitations](doc_3d_rendering_limitations_transparency_sorting): + +- Alpha-blended materials are significantly slower to render, especially if + they overlap. +- Alpha-blended materials may exhibit sorting issues when transparent + surfaces overlap each other. This means that surfaces may render in the + incorrect order, with surfaces in the back appearing to be in front of + those which are actually closer to the camera. +- Alpha-blended materials don't cast shadows, although they can receive shadows. +- Alpha-blended materials don't appear in any reflections (other than + reflection probes). +- Screen-space reflections and sharp SDFGI reflections don't appear on + alpha-blended materials. When SDFGI is enabled, rough reflections are used + as a fallback regardless of material roughness. + +Before using the **Alpha** transparency mode, always consider whether +another transparency mode is more suited for your needs. + +::: + +### Alpha Antialiasing + +:::note + +This property is only visible when the transparency mode is +**Alpha Scissor** or **Alpha Hash**. + +::: + +While alpha scissor and alpha hash materials are faster to render than +alpha-blended materials, they exhibit hard edges between opaque and transparent +regions. While it's possible to use post-processing-based :ref:`antialiasing +techniques <doc_3d_antialiasing>` such as FXAA and TAA, this is not always +desired as these techniques tend to make the final result look blurrier or +exhibit ghosting artifacts. + +There are 3 alpha antialiasing modes available: + +- **Disabled:** No alpha antialiasing. Edges of transparent materials will + appear aliased unless a post-processing-based antialiasing solution is used. +- **Alpha Edge Blend:** Results in a smooth transition between opaque and + transparent areas. Also known as "alpha to coverage". +- **Alpha Edge Clip:** Results in a sharp, but still antialiased transition + between opaque and transparent areas. Also known as "alpha to coverage + alpha + to one". + +When the alpha antialiasing mode is set to **Alpha Edge Blend** or **Alpha Edge +Clip**, a new **Alpha Antialiasing Edge** property becomes visible below in the +inspector. This property controls the threshold below which pixels should be +made transparent. While you've already defined an alpha scissor threshold (when +using **Alpha Scissor** only), this additional threshold is used to smoothly +transition between opaque and transparent pixels. **Alpha Antialiasing Edge** +must *always* be set to a value that is strictly below the alpha scissor +threshold. The default of ``0.3`` is a sensible value with an alpha scissor of +threshold of ``0.5``, but remember to adjust this alpha antialiasing edge when +modifying the alpha scissor threshold. + +If you find the antialiasing effect not effective enough, try increasing **Alpha +Antialiasing Edge** while making sure it's below **Alpha Scissor Threshold** (if +the material uses alpha scissor). On the other hand, if you notice the texture's +appearance visibly changing as the camera moves closer to the material, try +decreasing **Alpha Antialiasing Edge**. + +:::important + +For best results, MSAA 3D should be set to at least 2× in the Project +Settings when using alpha antialiasing. This is because this feature relies +on alpha to coverage, which is a feature provided by MSAA. + +Without MSAA, a fixed dithering pattern is applied on the material's edges, +which isn't very effective at smoothing out edges (although it can still +help a little). + +::: + +### Blend Mode + +Controls the blend mode for the material. Keep in mind that any mode +other than *Mix* forces the object to go through the transparent pipeline. + +* **Mix:** Default blend mode, alpha controls how much the object is visible. +* **Add:** The final color of the object is added to the color of the screen, + nice for flares or some fire-like effects. +* **Sub:** The final color of the object is subtracted from the color of the + screen. +* **Mul:** The final color of the object is multiplied with the color of the + screen. + +![Image](/img/Tutorials/3d/img/spatial_material8.png) + +### Cull Mode + +Determines which side of the object is not drawn when backfaces are rendered: + +* **Back:** The back of the object is culled when not visible (default). +* **Front:** The front of the object is culled when not visible. +* **Disabled:** Used for objects that are double-sided (no culling is performed). + +:::note + +By default, Blender has backface culling disabled on materials and will +export materials to match how they render in Blender. This means that +materials in Redot will have their cull mode set to **Disabled**. This can +decrease performance since backfaces will be rendered, even when they are +being culled by other faces. To resolve this, enable **Backface Culling** in +Blender's Materials tab, then export the scene to glTF again. + +::: + +### Depth Draw Mode + +Specifies when depth rendering must take place. + +* **Opaque Only (default):** Depth is only drawn for opaque objects. +* **Always:** Depth draw is drawn for both opaque and transparent objects. +* **Never:** No depth draw takes place + (do not confuse this with the No Depth Test option below). +* **Depth Pre-Pass:** For transparent objects, an opaque pass is made first + with the opaque parts, then transparency is drawn above. + Use this option with transparent grass or tree foliage. + +![Image](/img/Tutorials/3d/img/material_depth_draw.png) + +### No Depth Test + +In order for close objects to appear over far away objects, depth testing +is performed. Disabling it has the result of objects appearing over +(or under) everything else. + +Disabling this makes the most sense for drawing indicators in world space, +and works very well with the *Render Priority* property of Material +(see the bottom of this page). + +![Image](/img/Tutorials/3d/img/spatial_material3.png) + +## Shading + +### Shading mode + +Materials support three shading modes: **Per-Pixel**, **Per-Vertex**, and +**Unshaded**. + +
+ Three spheres showing the Per-Pixel, Per-Vertex, and Unshaded modes. +
+ +The **Per-Pixel** shading mode calculates lighting for each pixel, and is a good +fit for most use cases. However, in some cases you may want to increase +performance by using another shading mode. + +The **Per-Vertex** shading mode, often called "vertex shading" or "vertex lighting", +instead calculates lighting once for each vertex, and interpolates the result +between each pixel. + +On low-end or mobile devices, using per-vertex lighting can considerably increase +rendering performance. When rendering several layers of transparency, +such as when using particle systems, using per-vertex shading can improve +performance, especially when the camera is close to particles. + +You can also use per-vertex lighting to achieve a retro look. + +
+ Two cubes with a brick texture, one shaded and one unshaded. +
+ Texture from [AmbientCG](https://ambientcg.com/view?id=Bricks051) +
+
+ +The **Unshaded** shading mode does not calculate lighting at all. Instead, the +**Albedo** color is output directly. Lights will not affect the material at all, +and unshaded materials will tend to appear considerably brighter than shaded +materials. + +Rendering unshaded is useful for some specific visual effects. If maximum +performance is needed, it can also be used for particles, or low-end or +mobile devices. + +### Diffuse Mode + +Specifies the algorithm used by diffuse scattering of light when hitting +the object. The default is **Burley**. Other modes are also available: + +* **Burley:** Default mode, the original Disney Principled PBS diffuse algorithm. +* **Lambert:** Is not affected by roughness. +* **Lambert Wrap:** Extends Lambert to cover more than 90 degrees when + roughness increases. Works great for hair and simulating cheap + subsurface scattering. This implementation is energy conserving. +* **Toon:** Provides a hard cut for lighting, with smoothing affected by roughness. + It is recommended you disable sky contribution from your environment's + ambient light settings or disable ambient light in the StandardMaterial3D + to achieve a better effect. + +![Image](/img/Tutorials/3d/img/spatial_material6.webp) + +### Specular Mode + +Specifies how the specular blob will be rendered. The specular blob +represents the shape of a light source reflected in the object. + +* **SchlickGGX:** The most common blob used by PBR 3D engines nowadays. +* **Toon:** Creates a toon blob, which changes size depending on roughness. +* **Disabled:** Sometimes the blob gets in the way. Begone! + +![Image](/img/Tutorials/3d/img/spatial_material7.webp) + +### Disable Ambient Light + +Makes the object not receive any kind of ambient lighting that would +otherwise light it. + +### Disable Fog + +Makes the object unaffected by depth-based or volumetric fog. This is useful for particles or other additively blended materials that would otherwise show the shape of the mesh (even in places where it would be invisible without the fog). + +## Vertex Color + +This setting allows choosing what is done by default to vertex colors that come +from your 3D modeling application. By default, they are ignored. + +![Image](/img/Tutorials/3d/img/spatial_material4.webp) + +### Use as Albedo + +Choosing this option means vertex color is used as albedo color. + +### Is sRGB + +Most 3D modeling software will likely export vertex colors as sRGB, so toggling +this option on will help them look correct. + +## Albedo + +*Albedo* is the base color for the material, on which all the other settings +operate. When set to *Unshaded*, this is the only color that is visible. In +previous versions of Redot, this channel was named *Diffuse*. The change +of name mainly happened because, in PBR (Physically Based Rendering), this color affects many +more calculations than just the diffuse lighting path. + +Albedo color and texture can be used together as they are multiplied. + +*Alpha channel* in albedo color and texture is also used for the +object transparency. If you use a color or texture with *alpha channel*, +make sure to either enable transparency or *alpha scissoring* for it to work. + +## Metallic + +Redot uses a metallic model over competing models due to its simplicity. +This parameter defines how reflective the material is. The more reflective, the +less diffuse/ambient light affects the material and the more light is reflected. +This model is called "energy-conserving". + +The *Specular* parameter is a general amount for the reflectivity (unlike +*Metallic*, this is not energy-conserving, so leave it at ``0.5`` and don't touch +it unless you need to). + +The minimum internal reflectivity is ``0.04``, so it's impossible to make a +material completely unreflective, just like in real life. + +![Image](/img/Tutorials/3d/img/spatial_material13.png) + +## Roughness + +*Roughness* affects the way reflection happens. A value of ``0`` makes it a +perfect mirror while a value of ``1`` completely blurs the reflection (simulating +natural microsurfacing). Most common types of materials can be achieved with +the right combination of *Metallic* and *Roughness*. + +![Image](/img/Tutorials/3d/img/spatial_material14.png) + +## Emission + +*Emission* specifies how much light is emitted by the material (keep in mind this +does not include light surrounding geometry unless [VoxelGI](global_illumination/using_voxel_gi.md) +or [SDFGI](global_illumination/using_sdfgi.md) are used). This value is added to the resulting +final image and is not affected by other lighting in the scene. + +![Image](/img/Tutorials/3d/img/spatial_material15.png) + +## Normal map + +Normal mapping allows you to set a texture that represents finer shape detail. +This does not modify geometry, only the incident angle for light. In Redot, +only the red and green channels of normal maps are used for better compression +and wider compatibility. + +![Image](/img/Tutorials/3d/img/spatial_material16.png) + +:::note + +Redot requires the normal map to use the X+, Y+ and Z+ coordinates, this is +known as OpenGL style. If you've imported a material made to be used with +another engine it may be DirectX style, in which case the normal map needs to +be converted so its Y axis is flipped. + +More information about normal maps (including a coordinate order table for +popular engines) can be found +[here](http://wiki.polycount.com/wiki/Normal_Map_Technical_Details). + +::: + +## Rim + +Some fabrics have small micro-fur that causes light to scatter around it. Redot +emulates this with the *Rim* parameter. Unlike other rim lighting implementations, +which just use the emission channel, this one actually takes light into account +(no light means no rim). This makes the effect considerably more believable. + +![Image](/img/Tutorials/3d/img/spatial_material17.png) + +Rim size depends on roughness, and there is a special parameter to specify how +it must be colored. If *Tint* is ``0``, the color of the light is used for the +rim. If *Tint* is ``1``, then the albedo of the material is used. Using +intermediate values generally works best. + +## Clearcoat + +The *Clearcoat* parameter is used to add a secondary pass of transparent coat +to the material. This is common in car paint and toys. In practice, it's a +smaller specular blob added on top of the existing material. + +![Image](/img/Tutorials/3d/img/clearcoat_comparison.png) + +## Anisotropy + +This changes the shape of the specular blob and aligns it to tangent space. +Anisotropy is commonly used with hair, or to make materials such as brushed +aluminum more realistic. It works especially well when combined with flowmaps. + +![Image](/img/Tutorials/3d/img/spatial_material18.png) + +## Ambient Occlusion + +It is possible to specify a baked ambient occlusion map. This map affects how +much ambient light reaches each surface of the object (it does not affect direct +light by default). While it is possible to use Screen-Space Ambient Occlusion +(SSAO) to generate ambient occlusion, nothing beats the quality of a well-baked +AO map. It is recommended to bake ambient occlusion whenever possible. + +![Image](/img/Tutorials/3d/img/spatial_material19.png) + +## Height + +Setting a height map on a material produces a ray-marched search to emulate the +proper displacement of cavities along the view direction. This only creates an +illusion of depth, and does not add real geometry — for a height map shape used +for physics collision (such as terrain), see [class_HeightMapShape3D](class_HeightMapShape3D). It +may not work for complex objects, but it produces a realistic depth effect for +textures. For best results, *Height* should be used together with normal +mapping. + +![Image](/img/Tutorials/3d/img/spatial_material20.png) + +## Subsurface Scattering + +*This is only available in the Forward+ renderer, not the Mobile or Compatibility +renderers.* + +This effect emulates light that penetrates an object's surface, is scattered, +and then comes out. It is useful to create realistic skin, marble, colored +liquids, etc. + +![Image](/img/Tutorials/3d/img/spatial_material21.png) + +## Back Lighting + +This controls how much light from the lit side (visible to light) is transferred +to the dark side (opposite from the light). This works well for thin objects +such as plant leaves, grass, human ears, etc. + +## Refraction + +When refraction is enabled, Redot attempts to fetch information from behind the +object being rendered. This allows distorting the transparency in a way similar +to refraction in real life. + +Remember to use a transparent albedo texture (or reduce the albedo color's alpha +channel) to make refraction visible, as refraction relies on transparency to +have a visible effect. + +Refraction also takes the material roughness into account. Higher roughness +values will make the objects behind the refraction look blurrier, which +simulates real life behavior. If you can't see behind the object when refraction +is enabled and albedo transparency is reduced, decrease the material's +**Roughness** value. + +A normal map can optionally be specified in the **Refraction Texture** property +to allow distorting the refraction's direction on a per-pixel basis. + +![Image](/img/Tutorials/3d/img/spatial_material23.png) + +:::note + +Refraction is implemented as a screen-space effect and forces the material +to be transparent. This makes the effect relatively fast, but this results +in some limitations: + +- [Transparency sorting](doc_3d_rendering_limitations_transparency_sorting) + issues may occur. +- The refractive material cannot refract onto itself, or onto other + transparent materials. A refractive material behind another transparent + material will be invisible. +- Off-screen objects cannot appear in the refraction. This is most + noticeable with high refraction strength values. +- Opaque materials in front of the refractive material will appear to have + "refracted" edges, even though they shouldn't. + +::: + +## Detail + +Redot allows using secondary albedo and normal maps to generate a detail +texture, which can be blended in many ways. By combining this with secondary +UV or triplanar modes, many interesting textures can be achieved. + +![Image](/img/Tutorials/3d/img/spatial_material24.png) + +There are several settings that control how detail is used. + +Mask: The detail mask is a black and white image used to control where the +blending takes place on a texture. White is for the detail textures, Black +is for the regular material textures, different shades of gray are for +partial blending of the material textures and detail textures. + +Blend Mode: These four modes control how the textures are blended together. + +- Mix: Combines pixel values of both textures. At black, only show the material texture, + at white, only show the detail texture. Values of gray create a smooth blend between + the two. + +- Add: Adds pixel values of one Texture with the other. Unlike mix mode + both textures are completely mixed at white parts of a mask and not at gray + parts. The original texture is mostly unchanged at black + +- Sub: Subtracts pixel values of one texture with the other. The second + texture is completely subtracted at white parts of a mask with only a little + subtraction in black parts, gray parts being different levels of subtraction + based on the exact texture. + +- Mul: Multiplies the RGB channel numbers for each pixel from the top texture + with the values for the corresponding pixel from the bottom texture. + +Albedo: This is where you put an albedo texture you want to blend. If nothing +is in this slot it will be interpreted as white by default. + +Normal: This is where you put a normal texture you want to blend. If nothing is +in this slot it will be interpreted as a flat normal map. This can still be used +even if the material does not have normal map enabled. + +## UV1 and UV2 + +Redot supports two UV channels per material. Secondary UV is often useful for +ambient occlusion or emission (baked light). UVs can be scaled and offset, +which is useful when using repeating textures. + +### Triplanar Mapping + +Triplanar mapping is supported for both UV1 and UV2. This is an alternative way +to obtain texture coordinates, sometimes called "Autotexture". Textures are +sampled in X, Y and Z and blended by the normal. Triplanar mapping can be +performed in either world space or object space. + +In the image below, you can see how all primitives share the same material with +world triplanar, so the brick texture continues smoothly between them. + +![Image](/img/Tutorials/3d/img/spatial_material25.png) + +### World Triplanar + +When using triplanar mapping, it is computed in object local space. This +option makes it use world space instead. + +## Sampling + +### Filter + +The filtering method for the textures used by the material. See [this page](class_BaseMaterial3D_property_texture_filter) +for a full list of options and their description. + +### Repeat + +if the textures used by the material repeat, and how they repeat. See [this page](class_BaseMaterial3D_property_texture_repeat) +for a full list of options and their description. + +## Shadows + +### Do Not Receive Shadows + +Makes the object not receive any kind of shadow that would otherwise +be cast onto it. + +### Use Shadow to Opacity + +Lighting modifies the alpha so shadowed areas are opaque and non-shadowed +areas are transparent. Useful for overlaying shadows onto a camera feed in AR. + +## Billboard + +### Billboard Mode + +Enables billboard mode for drawing materials. This controls how the object +faces the camera: + +* **Disabled:** Billboard mode is disabled. +* **Enabled:** Billboard mode is enabled. The object's -Z axis will always + face the camera's viewing plane. +* **Y-Billboard:** The object's X axis will always be aligned with the camera's viewing plane. +* **Particle Billboard:** Most suited for particle systems, because it allows + specifying [flipbook animation](doc_process_material_properties_animation). + +![Image](/img/Tutorials/3d/img/spatial_material9.webp) + +The **Particles Anim** section is only visible when the billboard mode is **Particle Billboard**. + +### Billboard Keep Scale + +Enables scaling a mesh in billboard mode. + +## Grow + +Grows the object vertices in the direction pointed by their normals: + +![Image](/img/Tutorials/3d/img/spatial_material10.png) + +This is commonly used to create cheap outlines. Add a second material pass, +make it black and unshaded, reverse culling (Cull Front), and add some grow: + +![Image](/img/Tutorials/3d/img/spatial_material11.png) + +:::note + +For Grow to work as expected, the mesh must have connected faces with shared +vertices, or "smooth shading". If the mesh has disconnected faces with unique +vertices, or "flat shading", the mesh will appear to have gaps when using Grow. + +::: + +## Transform + +### Fixed Size + +This causes the object to be rendered at the same size no matter the distance. +This is useful mostly for indicators (no depth test and high render priority) +and some types of billboards. + +### Use Point Size + +This option is only effective when the geometry rendered is made of points +(generally it's made of triangles when imported from 3D modeling software). If +so, then those points can be resized (see below). + +### Point Size + +When drawing points, specify the point size in pixels. + +### Transmission + +This controls how much light from the lit side (visible to light) is transferred +to the dark side (opposite from the light). This works well for thin objects +such as plant leaves, grass, human ears, etc. + +![Image](/img/Tutorials/3d/img/spatial_material22.png) + +## Proximity and Distance Fade + +Redot allows materials to fade by proximity to each other as well as depending +on the distance from the viewer. Proximity fade is useful for effects such as +soft particles or a mass of water with a smooth blending to the shores. + +![Image](/img/Tutorials/3d/img/spatial_material_proxfade.gif) + +Distance fade is useful for light shafts or indicators that are only present +after a given distance. + +Keep in mind enabling proximity fade or distance fade with **Pixel Alpha** mode +enables alpha blending. Alpha blending is more GPU-intensive and can cause +transparency sorting issues. Alpha blending also disables many material +features such as the ability to cast shadows. + +:::note + +To hide a character when they get too close to the camera, consider using +**Pixel Dither** or better, **Object Dither** (which is even faster than +**Pixel Dither**). + +::: + +**Pixel Alpha** mode: The actual transparency of a pixel of the object changes +with distance to the camera. This is the most effect, but forces the material +into the transparency pipeline (which leads, for example, to no shadows). + +![Image](/img/Tutorials/3d/img/standart_material_distance_fade_pixel_alpha_mode.webp) + +**Pixel Dither** mode: What this does is sort of approximate the transparency +by only having a fraction of the pixels rendered. + +![Image](/img/Tutorials/3d/img/standart_material_distance_fade_pixel_dither_mode.webp) + +**Object Dither** mode: Like the previous mode, but the calculated transparency +is the same across the entire object's surface. + +![Image](/img/Tutorials/3d/img/standart_material_distance_fade_object_dither_mode.webp) + +## Material Settings + +## Render priority + +The rendering order of objects can be changed, although this is mostly +useful for transparent objects (or opaque objects that perform depth draw +but no color draw, such as cracks on the floor). + +Objects are sorted by an opaque/transparent queue, then [render_priority](class_Material_property_render_priority), +with higher priority being drawn later. Transparent objects are also sorted by depth. + +Depth testing overrules priority. Priority alone cannot force opaque objects to be drawn over each other. + +## Next Pass + +Setting [next_pass](class_Material_property_next_pass) on a material +will cause an object to be rendered again with that next material. + +Materials are sorted by an opaque/transparent queue, then [render_priority](class_Material_property_render_priority), +with higher priority being drawn later. + +![Image](/img/Tutorials/3d/img/next_pass.webp) + +Depth will test equal between both materials unless the grow setting or other vertex transformations are used. +Multiple transparent passes should use [render_priority](class_Material_property_render_priority) to ensure correct ordering. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/using_decals.md b/Redot-Documentation/docs/26.1/Tutorials/3d/using_decals.md new file mode 100644 index 0000000..902539a --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/using_decals.md @@ -0,0 +1,262 @@ + +# Using decals + +:::note + +Decals are only supported in the Forward+ and Mobile renderers, not the +Compatibility renderer. + +If using the Compatibility renderer, consider using Sprite3D as an alternative +for projecting decals onto (mostly) flat surfaces. + +::: + +Decals are projected textures that apply on opaque or transparent surfaces in +3D. This projection happens in real-time and doesn't rely on mesh generation. +This allows you to move decals every frame with only a small performance impact, +even when applied on complex meshes. + +While decals cannot add actual geometry detail onto the projected surface, +decals can still make use of physically-based rendering to provide similar +properties to full-blown :abbr:`PBR (Physically-Based Rendering)` materials. + +On this page, you'll learn: + +- How to set up decals in the 3D editor. +- How to create decals during gameplay in a 3D scene (such as bullet impacts). +- How to balance decal configuration between performance and quality. + +:::info + +The Redot demo projects repository contains a +[3D decals demo](https://github.com/redot-engine/redot-demo-projects/tree/master/3d/decals). + +If you're looking to write arbitrary 3D text on top of a surface, use +[doc_3d_text](3d_text.md) placed close to a surface instead of a Decal node. + +::: + +## Use cases + +### Static decoration + +Sometimes, the fastest way to add texture detail to a scene is to use decals. +This is especially the case for organic detail, such as patches of dirt or sand +scattered on a large surface. Decals can help break up texture repetition in +scenes and make patterns look more natural. On a smaller scale, decals can also +be used to create detail variations for objects. For example, decals can be used +to add nuts and bolts on top of hard-surface geometry. + +Since decals can inject their own :abbr:`PBR (Physically-Based Rendering)` +properties on top of the projected surfaces, they can also be used to create +footprints or wet puddles. + +
+ Dirt added on top of level geometry using decals +
+ Dirt added on top of level geometry using decals +
+
+ +### Dynamic gameplay elements + +Decals can represent temporary or persistent gameplay effects such as bullet +impacts and explosion scorches. + +Using an AnimationPlayer node or a script, decals can be made to fade over time +(and then be removed using ``queue_free()``) to improve performance. + +### Blob shadows + +Blob shadows are frequently used in mobile projects (or to follow a retro art +style), as real-time lighting tends to be too expensive on low-end mobile +devices. However, when relying on baked lightmaps with fully baked lights, +dynamic objects will not cast *any* shadow from those lights. This makes dynamic +objects in lightmapped scenes look flat in comparison to real-time lighting, +with dynamic objects almost looking like they're floating. + +Thanks to blob shadows, dynamic objects can still cast an approximative shadow. +Not only this helps with depth perception in the scene, but this can also be a +gameplay element, especially in 3D platformers. The blob shadow's length can be +extended to let the player know where they will land if they fall straight down. + +Even with real-time lighting, blob shadows can still be useful as a form of +ambient occlusion for situations where SSAO is too expensive or too unstable due +to its screen-space nature. For example, vehicles' underside shadows are +well-represented using blob shadows. + +
+ Blob shadow under object comparison +
+ Blob shadow under object comparison +
+
+ +## Quick start guide + +### Creating decals in the editor + +1. Create a Decal node in the 3D editor. +2. In the inspector, expand the **Textures** section and load a texture in + **Textures > Albedo**. +3. Move the Decal node towards an object, then rotate it so the decal is visible + (and in the right orientation). If the decal appears mirrored, try to rotate + it by 180 degrees. You can double-check whether it's in the right orientation + by increasing **Parameters > Normal Fade** to 0.5. This will prevent the Decal + from being projected on surfaces that are not facing the decal. +4. If your decal is meant to affect static objects only, configure it to prevent + affecting dynamic objects (or vice versa). To do so, change the decal's + **Cull Mask** property to exclude certain layers. After doing this, modify + your dynamic objects' MeshInstance3D nodes to change their visibility layers. + For instance, you can move them from layer 1 to layer 2, then disable layer 2 + in the decal's **Cull Mask** property. + +## Decal node properties + +- **Extents:** The size of the decal. The Y axis determines the length of the + decal's projection. Keep the projection length as short as possible to improve + culling opportunities, therefore improving performance. + +### Textures + +- **Albedo:** The albedo (diffuse/color) map to use for the decal. In + most situations, this is the texture you want to set first. If using a normal + or ORM map, an albedo map *must* be set to provide an alpha channel. This + alpha channel will be used as a mask to determine how much the normal/ORM maps + will affect the underlying surface. +- **Normal:** The normal map to use for the decal. This can be used + to increase perceived detail on the decal by modifying how light reacts to it. + The impact of this texture is multiplied by the albedo texture's alpha channel + (but not **Albedo Mix**). +- **ORM:** The Occlusion/Roughness/Metallic map to use for the decal. + This is an optimized format for storing PBR material maps. Ambient Occlusion + map is stored in the red channel, roughness map in the green channel, metallic + map in the blue channel. The impact of this texture is multiplied by the + albedo texture's alpha channel (but not **Albedo Mix**). +- **Emission:** The emission texture to use for the decal. Unlike + **Albedo**, this texture will appear to glow in the dark. + +### Parameters + +- **Emission Energy:** The brightness of the emission texture. +- **Modulate:** Multiplies the color of the albedo and emission textures. Use + this to tint decals (e.g. for paint decals, or to increase variation by + randomizing each decal's modulation). +- **Albedo Mix:** The opacity of the albedo texture. Unlike using an albedo + texture with a more transparent alpha channel, decreasing this value below + ``1.0`` does *not* reduce the impact of the normal/ORM texture on the + underlying surface. Set this to ``0.0`` when creating normal/ORM-only decals + such as footsteps or wet puddles. +- **Normal Fade:** Fades the Decal if the angle between the Decal's + :abbr:`AABB (Axis-Aligned Bounding Box)` and the target surface becomes too large. + A value of ``0.0`` projects the decal regardless of angle, while a value of ``0.999`` + limits the decal to surfaces that are nearly perpendicular. Setting **Normal + Fade** to a value greater than ``0.0`` has a small performance cost due to the + added normal angle computations. + +### Vertical Fade + +- **Upper Fade:** The curve over which the decal will fade as the surface gets + further from the center of the :abbr:`AABB (Axis-Aligned Bounding Box)` + (towards the decal's projection angle). Only positive values are valid. +- **Lower Fade:** The curve over which the decal will fade as the surface gets + further from the center of the :abbr:`AABB (Axis-Aligned Bounding Box)` (away + from the decal's projection angle). Only positive values are valid. + +### Distance Fade + +- **Enabled:** Controls whether distance fade (a form of :abbr:`LOD (Level of Detail)`) + is enabled. The decal will fade out over **Begin + Length**, after which it + will be culled and not sent to the shader at all. Use this to reduce the number + of active decals in a scene and thus improve performance. +- **Begin:** The distance from the camera at which the decal begins to fade away + (in 3D units). +- **Length:** The distance over which the decal fades (in 3D units). The decal + becomes slowly more transparent over this distance and is completely invisible + at the end. Higher values result in a smoother fade-out transition, which is + more suited when the camera moves fast. + +### Cull Mask + +- **Cull Mask:** Specifies which VisualInstance3D layers this decal will project + on. By default, decals affect all layers. This is used so you can specify which + types of objects receive the decal and which do not. This is especially useful + so you can ensure that dynamic objects don't accidentally receive a Decal + intended for the terrain under them. + +## Decal rendering order + +By default, decals are ordered based on the size of their :abbr:`AABB +(Axis-Aligned Bounding Box)` and the distance to the camera. AABBs that are +closer to the camera are rendered first, which means that decal rendering order +can sometimes appear to change depending on camera position if some decals are +positioned at the same location. + +To resolve this, you can adjust the **Sorting Offset** property in the +VisualInstance3D section of the Decal node inspector. This offset is not a +strict priority order, but a *guideline* that the renderer will use as the AABB +size still affects how decal sorting works. Therefore, higher values will +*always* result in the decal being drawn above other decals with a lower sorting +offset. + +If you want to ensure a decal is always rendered on top of other decals, +you need to set its **Sorting Offset** property to a positive value greater than +the AABB length of the largest decal that may overlap it. To make this decal +drawn behind other decals instead, set the **Sorting Offset** to the same +negative value. + +
+ VisualInstance3D Sorting Offset comparison on Decals +
+ VisualInstance3D Sorting Offset comparison on Decals +
+
+ +## Tweaking performance and quality + +Decal rendering performance is mostly determined by their screen coverage, but +also their number. In general, a few large decals that cover up most of the +screen will be more expensive to render than many small decals that are +scattered around. + +To improve rendering performance, you can enable the **Distance Fade** property +as described above. This will make distant decals fade out when they are far +away from the camera (and may have little to no impact on the final scene +rendering). Using node groups, you can also prevent non-essential decorative +decals from spawning based on user configuration. + +The way decals are rendered also has an impact on performance. The +[Rendering > Textures > Decals > Filter](class_ProjectSettings_property_rendering/textures/decals/filter) +advanced project setting lets you control how decal +textures should be filtered. **Nearest/Linear** does not use mipmaps. However, +decals will look grainy at a distance. **Nearest/Linear Mipmaps** will look +smoother at a distance, but decals will look blurry when viewed from oblique +angles. This can be resolved by using **Nearest/Linear Mipmaps Anisotropic**, +which provides the highest quality, but is also slower to render. + +If your project has a pixel art style, consider setting the filter to one of the +**Nearest** values so that decals use nearest-neighbor filtering. Otherwise, +stick to **Linear**. + +## Limitations + +Decals cannot affect material properties other than the ones listed above, +such as height (for parallax mapping). + +For performance reasons, decals use purely fixed rendering logic. This means +decals cannot use custom shaders. However, custom shaders on the projected +surfaces are able to read the information that is overridden by decals on top of +them, such as roughness and metallic. + +When using the Forward+ renderer, Redot uses a *clustering* approach for +decal rendering. As many decals as desired can be added (as long as +performance allows). However, there's still a default limit of 512 *clustered +elements* that can be present in the current camera view. A clustered element is +an omni light, a spot light, a [decal](using_decals.md) or a +[reflection probe](global_illumination/reflection_probes.md). This limit can be increased by adjusting +[Max Clustered Elements](class_ProjectSettings_property_rendering/limits/cluster_builder/max_clustered_elements) +in **Project Settings > Rendering > Limits > Cluster Builder**. + +When using the Mobile renderer, only 8 decals can be applied on each +individual Mesh *resource*. If there are more decals affecting a single mesh, +not all of them will be rendered on the mesh. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/using_gridmaps.md b/Redot-Documentation/docs/26.1/Tutorials/3d/using_gridmaps.md new file mode 100644 index 0000000..ff8c5af --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/using_gridmaps.md @@ -0,0 +1,151 @@ + +### Using GridMaps + +## Introduction + +[Gridmaps](class_GridMap) are a tool for creating 3D +game levels, similar to the way [TileMap](../2d/using_tilemaps.md) +works in 2D. You start with a predefined collection of 3D meshes (a +[class_MeshLibrary](class_MeshLibrary)) that can be placed on a grid, +as if you were building a level with an unlimited amount of Lego blocks. + +Collisions and navigation can also be added to the meshes, just like you +would do with the tiles of a tilemap. + +## Example project + +To learn how GridMaps work, start by downloading the sample project: +[gridmap_starter.zip](https://github.com/redot-engine/redot-docs-site-project-starters/releases/download/latest-4.x/gridmap_starter.zip). + +Unzip this project and add it to the Project Manager using the "Import" +button. + +## Creating a MeshLibrary + +To begin, you need a [class_MeshLibrary](class_MeshLibrary), which is a collection +of individual meshes that can be used in the gridmap. Open the "mesh_library_source.tscn" +scene to see an example of how to set up the mesh library. + +![Image](/img/Tutorials/3d/img/gridmap_meshlibrary1.png) + +As you can see, this scene has a [class_Node3D](class_Node3D) node as its root, and +a number of [class_MeshInstance3D](class_MeshInstance3D) node children. + +If you don't need any physics in your scene, then you're done. However, in most +cases you'll want to assign collision bodies to the meshes. + +## Collisions + +You can manually assign a [class_StaticBody3D](class_StaticBody3D) and +[class_CollisionShape3D](class_CollisionShape3D) to each mesh. Alternatively, you can use the "Mesh" menu +to automatically create the collision body based on the mesh data. + +![Image](/img/Tutorials/3d/img/gridmap_create_body.png) + +Note that a "Convex" collision body will work better for simple meshes. For more +complex shapes, select "Create Trimesh Static Body". Once each mesh has +a physics body and collision shape assigned, your mesh library is ready to +be used. + +![Image](/img/Tutorials/3d/img/gridmap_mesh_scene.png) + +## Materials + +Only the materials from within the meshes are used when generating the mesh +library. Materials set on the node will be ignored. + +## NavigationMeshes + +Like all mesh instances, MeshLibrary items can be assigned a [class_NavigationMesh](class_NavigationMesh) +resource, which can be created manually, or baked as described below. + +To create the NavigationMesh from a MeshLibrary scene export, place a +[class_NavigationRegion3D](class_NavigationRegion3D) child node below the main MeshInstance3D for the GridMap +item. Add a valid NavigationMesh resource to the NavigationRegion3D and some source +geometry nodes below and bake the NavigationMesh. + +:::note + +With small grid cells it is often necessary to reduce the NavigationMesh properties +for agent radius and region minimum size. + +::: + +![Image](/img/Tutorials/3d/img/meshlibrary_scene.png) + +Nodes below the NavigationRegion3D are ignored for the MeshLibrary scene export, so +additional nodes can be added as source geometry just for baking the navmesh. + +:::warning + +The baked cell size of the NavigationMesh must match the NavigationServer map cell +size to properly merge the navigation meshes of different grid cells. + +::: + +## MeshLibrary format + +To summarize the specific constraints of the MeshLibrary format, a MeshLibrary +scene has a Node3D as the root node, and several child nodes which will become +MeshLibrary items. Each child of the root node should: + +- Be a [class_MeshInstance3D](class_MeshInstance3D), which will become the MeshLibrary item. Only + this visual mesh will be exported. +- Have a material, in the mesh's material slot, *not* the MeshInstance3D's + material slots. +- Have up to one [class_StaticBody3D](class_StaticBody3D) child, for collision. The + StaticBody3D should have one or more [class_CollisionShape3D](class_CollisionShape3D) children. +- Have up to one [class_NavigationRegion3D](class_NavigationRegion3D) child, for navigation. The + NavigationRegion3D can have one or more additional [class_MeshInstance3D](class_MeshInstance3D) + children, which can be baked for navigation, but won't be exported as a visual + mesh. + +Only this specific format is recognized. Other node types placed as children +will not be recognized and exported. GridMap is not a general-purpose system for +placing *nodes* on a grid, but rather a specific, optimized system, designed to +place *meshes* with collisions and navigation. + +## Exporting the MeshLibrary + +To export the library, click on **Scene > Export As... > MeshLibrary...**, and save it +as a resource. + +![Image](/img/Tutorials/3d/img/gridmap_export.png) + +You can find an already exported MeshLibrary in the project named "MeshLibrary.tres". + +## Using GridMap + +Create a new scene and add a GridMap node. Add the mesh library by dragging +the resource file from the FileSystem dock and dropping it in the "Theme" property +in the Inspector. + +![Image](/img/Tutorials/3d/img/gridmap_main.png) + +The "Cell/Size" property should be set to the size of your meshes. You can leave +it at the default value for the demo. Set the "Center Y" property to "Off". + +Now you can start designing the level by choosing a tile from the palette and +placing it with Left-Click in the editor window. Use Right-click to remove a tile. + +Use the arrows next to the "GridMap" menu to change the floor that you are working on. + +Click on the "GridMap" menu to see options and shortcuts. For example, pressing +`S` rotates a tile around the y-axis. + +![Image](/img/Tutorials/3d/img/gridmap_menu.png) + +Holding `Shift` and dragging with the left mouse button will draw a selection +box. You can duplicate or clear the selected area using the respective menu +options. + +![Image](/img/Tutorials/3d/img/gridmap_select.png) + +In the menu, you can also change the axis you're drawing on, as well as shift +the drawing plane higher or lower on its axis. + +![Image](/img/Tutorials/3d/img/gridmap_shift_axis.png) + +## Using GridMap in code + +See [class_GridMap](class_GridMap) for details on the node's methods and member variables. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/using_multi_mesh_instance.md b/Redot-Documentation/docs/26.1/Tutorials/3d/using_multi_mesh_instance.md new file mode 100644 index 0000000..947b1d3 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/using_multi_mesh_instance.md @@ -0,0 +1,99 @@ +:::warning +This page is marked as outdated and may not reflect current Redot behavior. +::: + +# Using MultiMeshInstance3D + +## Introduction + +In a normal scenario, you would use a [MeshInstance3D](class_MeshInstance3D) +node to display a 3D mesh like a human model for the main character, but in some +cases, you would like to create multiple instances of the same mesh in a scene. +You *could* duplicate the same node multiple times and adjust the transforms +manually. This may be a tedious process and the result may look mechanical. +Also, this method is not conducive to rapid iterations. +[MultiMeshInstance3D](class_MultiMeshInstance3D) is one of the possible +solutions to this problem. + +MultiMeshInstance3D, as the name suggests, creates multiple copies of a +MeshInstance over a surface of a specific mesh. An example would be having a +tree mesh populate a landscape mesh with trees of random scales and orientations. + +## Setting up the nodes + +The basic setup requires three nodes: the MultiMeshInstance3D node +and two MeshInstance3D nodes. + +One node is used as the target, the surface mesh that you want to place multiple meshes +on. In the tree example, this would be the landscape. + +The other node is used as the source, the mesh that you want to have duplicated. +In the tree case, this would be the tree itself. + +In our example, we would use a [Node3D](class_Node3D) node as the root node of +the scene. Your scene tree would look like this: + +![Image](/img/Tutorials/3d/img/multimesh_scene_tree.png) + +:::note +For simplicity's sake, this tutorial uses built-in primitives. + +::: + +Now you have everything ready. Select the MultiMeshInstance3D node and look at the +toolbar, you should see an extra button called ``MultiMesh`` next to ``View``. +Click it and select *Populate surface* in the dropdown menu. A new window titled +*Populate MultiMesh* will pop up. + +![Image](/img/Tutorials/3d/img/multimesh_toolbar.png) + +![Image](/img/Tutorials/3d/img/multimesh_settings.png) + +## MultiMesh settings + +Below are descriptions of the options. + +### Target Surface + +The mesh used as the target surface on which to place copies of your +source mesh. + +### Source Mesh + +The mesh you want duplicated on the target surface. + +### Mesh Up Axis + +The axis used as the up axis of the source mesh. + +### Random Rotation + +Randomizing the rotation around the up axis of the source mesh. + +### Random Tilt + +Randomizing the overall rotation of the source mesh. + +### Random Scale + +Randomizing the scale of the source mesh. + +### Scale + +The scale of the source mesh that will be placed over the target surface. + +### Amount + +The amount of mesh instances placed over the target surface. + +Select the target surface. In the tree case, this should be the landscape node. +The source mesh should be the tree node. Adjust the other parameters +according to your preference. Press ``Populate`` and multiple copies of the +source mesh will be placed over the target mesh. If you are satisfied with the +result, you can delete the mesh instance used as the source mesh. + +The end result should look like this: + +![Image](/img/Tutorials/3d/img/multimesh_result.png) + +To change the result, repeat the previous steps with different parameters. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/using_transforms.md b/Redot-Documentation/docs/26.1/Tutorials/3d/using_transforms.md new file mode 100644 index 0000000..e3a0371 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/using_transforms.md @@ -0,0 +1,546 @@ + +### Using 3D transforms + +## Introduction + +If you have never made 3D games before, working with rotations in three dimensions can be confusing at first. +Coming from 2D, the natural way of thinking is along the lines of *"Oh, it's just like rotating in 2D, except now rotations happen in X, Y and Z"*. + +At first, this seems easy. For simple games, this way of thinking may even be enough. Unfortunately, it's often incorrect. + +Angles in three dimensions are most commonly referred to as "Euler Angles". + +![Image](/img/Tutorials/3d/img/transforms_euler.png) + +Euler angles were introduced by mathematician Leonhard Euler in the early 1700s. + +![Image](/img/Tutorials/3d/img/transforms_euler_himself.png) + +This way of representing 3D rotations was groundbreaking at the time, but it has several shortcomings when used in game development (which is to be expected from a guy with a funny +hat). +The idea of this document is to explain why, as well as outlining best practices for dealing with transforms when programming 3D games. + +## Problems of Euler angles + +While it may seem intuitive that each axis has a rotation, the truth is that it's just not practical. + +# Axis order + +The main reason for this is that there isn't a *unique* way to construct an orientation from the angles. There isn't a standard mathematical function that +takes all the angles together and produces an actual 3D rotation. The only way an orientation can be produced from angles is to rotate the object angle +by angle, in an *arbitrary order*. + +This could be done by first rotating in *X*, then *Y* and then in *Z*. Alternatively, you could first rotate in *Y*, then in *Z* and finally in *X*. Anything works, +but depending on the order, the final orientation of the object will *not necessarily be the same*. Indeed, this means that there are several ways to construct an orientation +from 3 different angles, depending on *the order of the rotations*. + +Following is a visualization of rotation axes (in X, Y, Z order) in a gimbal (from Wikipedia). As you can see, the orientation of each axis depends on the rotation of the previous one: + +![Image](/img/Tutorials/3d/img/transforms_gimbal.gif) + +You may be wondering how this affects you. Let's look at a practical example: + +Imagine you are working on a first-person controller (e.g. an FPS game). Moving the mouse left and right controls your view angle parallel to the ground, while moving it up and down moves the player's view up and down. + +In this case to achieve the desired effect, rotation must be applied first in the *Y* axis ("up" in this case, since Redot uses a "Y-Up" orientation), followed by rotation in the *X* axis. + +![Image](/img/Tutorials/3d/img/transforms_rotate1.gif) + +If we were to apply rotation in the *X* axis first, and then in *Y*, the effect would be undesired: + +![Image](/img/Tutorials/3d/img/transforms_rotate2.gif) + +Depending on the type of game or effect desired, the order in which you want axis rotations to be applied may differ. Therefore, applying rotations in X, Y, and Z is not enough: you also need a *rotation order*. + +# Interpolation + +Another problem with using Euler angles is interpolation. Imagine you want to transition between two different camera or enemy positions (including rotations). One logical way to approach this is to interpolate the angles from one position to the next. One would expect it to look like this: + +![Image](/img/Tutorials/3d/img/transforms_interpolate1.gif) + +But this does not always have the expected effect when using angles: + +![Image](/img/Tutorials/3d/img/transforms_interpolate2.gif) + +The camera actually rotated the opposite direction! + +There are a few reasons this may happen: + +* Rotations don't map linearly to orientation, so interpolating them does not always result in the shortest path (i.e., to go from ``270`` to ``0`` degrees is not the same as going from ``270`` to ``360``, even though the angles are equivalent). +* Gimbal lock is at play (first and last rotated axis align, so a degree of freedom is lost). See [Wikipedia's page on Gimbal Lock](https://en.wikipedia.org/wiki/Gimbal_lock) for a detailed explanation of this problem. + +# Say no to Euler angles + +The result of all this is that you should **not use** the ``rotation`` property of [class_Node3D](class_Node3D) nodes in Redot for games. It's there to be used mainly in the editor, for coherence with the 2D engine, and for simple rotations (generally just one axis, or even two in limited cases). As much as you may be tempted, don't use it. + +Instead, there is a better way to solve your rotation problems. + +## Introducing transforms + +Redot uses the [class_Transform3D](class_Transform3D) datatype for orientations. Each [class_Node3D](class_Node3D) node contains a ``transform`` property which is relative to the parent's transform, if the parent is a Node3D-derived type. + +It is also possible to access the world coordinate transform via the ``global_transform`` property. + +A transform has a [class_Basis](class_Basis) (transform.basis sub-property), which consists of three [class_Vector3](class_Vector3) vectors. These are accessed via the ``transform.basis`` property and can be accessed directly by ``transform.basis.x``, ``transform.basis.y``, and ``transform.basis.z``. Each vector points in the direction its axis has been rotated, so they effectively describe the node's total rotation. The scale (as long as it's uniform) can also be inferred from the length of the axes. A *basis* can also be interpreted as a 3x3 matrix and used as ``transform.basis[x][y]``. + +A default basis (unmodified) is akin to: + + + + + +```gdscript +var basis = Basis() +# Contains the following default values: +basis.x = Vector3(1, 0, 0) # Vector pointing along the X axis +basis.y = Vector3(0, 1, 0) # Vector pointing along the Y axis +basis.z = Vector3(0, 0, 1) # Vector pointing along the Z axis + +``` + + + + + +```csharp +// Due to technical limitations on structs in C# the default +// constructor will contain zero values for all fields. +var defaultBasis = new Basis(); +GD.Print(defaultBasis); // prints: ((0, 0, 0), (0, 0, 0), (0, 0, 0)) + +// Instead we can use the Identity property. +var identityBasis = Basis.Identity; +GD.Print(identityBasis.X); // prints: (1, 0, 0) +GD.Print(identityBasis.Y); // prints: (0, 1, 0) +GD.Print(identityBasis.Z); // prints: (0, 0, 1) + +// The Identity basis is equivalent to: +var basis = new Basis(Vector3.Right, Vector3.Up, Vector3.Back); +GD.Print(basis); // prints: ((1, 0, 0), (0, 1, 0), (0, 0, 1)) + +``` + + + + + +This is also an analog of a 3x3 identity matrix. + +Following the OpenGL convention, ``X`` is the *Right* axis, ``Y`` is the *Up* axis and ``Z`` is the *Forward* axis. + +Together with the *basis*, a transform also has an *origin*. This is a *Vector3* specifying how far away from the actual origin ``(0, 0, 0)`` this transform is. Combining the *basis* with the *origin*, a *transform* efficiently represents a unique translation, rotation, and scale in space. + +![Image](/img/Tutorials/3d/img/transforms_camera.png) + +One way to visualize a transform is to look at an object's 3D gizmo while in "local space" mode. + +![Image](/img/Tutorials/3d/img/transforms_local_space.png) + +The gizmo's arrows show the ``X``, ``Y``, and ``Z`` axes (in red, green, and blue respectively) of the basis, while the gizmo's center is at the object's origin. + +![Image](/img/Tutorials/3d/img/transforms_gizmo.png) + +For more information on the mathematics of vectors and transforms, please read the [doc_vector_math](../math/vector_math.md) tutorials. + +# Manipulating transforms + +Of course, transforms are not as straightforward to manipulate as angles and have problems of their own. + +It is possible to rotate a transform, either by multiplying its basis by another (this is called accumulation), or by using the rotation methods. + + + + + +```gdscript +var axis = Vector3(1, 0, 0) # Or Vector3.RIGHT +var rotation_amount = 0.1 +# Rotate the transform around the X axis by 0.1 radians. +transform.basis = Basis(axis, rotation_amount) * transform.basis +# shortened +transform.basis = transform.basis.rotated(axis, rotation_amount) + +``` + + + + + +```csharp +Transform3D transform = Transform; +Vector3 axis = new Vector3(1, 0, 0); // Or Vector3.Right +float rotationAmount = 0.1f; + +// Rotate the transform around the X axis by 0.1 radians. +transform.Basis = new Basis(axis, rotationAmount) * transform.Basis; +// shortened +transform.Basis = transform.Basis.Rotated(axis, rotationAmount); + +Transform = transform; + +``` + + + + + +A method in Node3D simplifies this: + + + + + +```gdscript +# Rotate the transform around the X axis by 0.1 radians. +rotate(Vector3(1, 0, 0), 0.1) +# shortened +rotate_x(0.1) + +``` + + + + + +```csharp +// Rotate the transform around the X axis by 0.1 radians. +Rotate(new Vector3(1, 0, 0), 0.1f); +// shortened +RotateX(0.1f); + +``` + + + + + +This rotates the node relative to the parent node. + +To rotate relative to object space (the node's own transform), use the following: + + + + + +```gdscript +# Rotate around the object's local X axis by 0.1 radians. +rotate_object_local(Vector3(1, 0, 0), 0.1) + +``` + + + + + +```csharp +// Rotate around the object's local X axis by 0.1 radians. +RotateObjectLocal(new Vector3(1, 0, 0), 0.1f); + +``` + + + + + +The axis should be defined in the local coordinate system of the object. For example, to rotate around the object's local X, Y, or Z axes, use ``Vector3.RIGHT`` for the X-axis, ``Vector3.UP`` for the Y-axis, and ``Vector3.FORWARD`` for the Z-axis. + +# Precision errors + +Doing successive operations on transforms will result in a loss of precision due to floating-point error. This means the scale of each axis may no longer be exactly ``1.0``, and they may not be exactly ``90`` degrees from each other. + +If a transform is rotated every frame, it will eventually start deforming over time. This is unavoidable. + +There are two different ways to handle this. The first is to *orthonormalize* the transform after some time (maybe once per frame if you modify it every frame): + + + + + +```gdscript +transform = transform.orthonormalized() + +``` + + + + + +```csharp +transform = transform.Orthonormalized(); + +``` + + + + + +This will make all axes have ``1.0`` length again and be ``90`` degrees from each other. However, any scale applied to the transform will be lost. + +It is recommended you not scale nodes that are going to be manipulated; scale their children nodes instead (such as MeshInstance3D). If you absolutely must scale the node, then re-apply it at the end: + + + + + +```gdscript +transform = transform.orthonormalized() +transform = transform.scaled(scale) + +``` + + + + + +```csharp +transform = transform.Orthonormalized(); +transform = transform.Scaled(scale); + +``` + + + + + +# Obtaining information + +You might be thinking at this point: **"Ok, but how do I get angles from a transform?"**. The answer again is: you don't. You must do your best to stop thinking in angles. + +Imagine you need to shoot a bullet in the direction your player is facing. Just use the forward axis (commonly ``Z`` or ``-Z``). + + + + + +```gdscript +bullet.transform = transform +bullet.speed = transform.basis.z * BULLET_SPEED + +``` + + + + + +```csharp +bullet.Transform = transform; +bullet.LinearVelocity = transform.Basis.Z * BulletSpeed; + +``` + + + + + +Is the enemy looking at the player? Use the dot product for this (see the [doc_vector_math](../math/vector_math.md) tutorial for an explanation of the dot product): + + + + + +```gdscript +# Get the direction vector from player to enemy +var direction = enemy.transform.origin - player.transform.origin +if direction.dot(enemy.transform.basis.z) > 0: + enemy.im_watching_you(player) + +``` + + + + + +```csharp +// Get the direction vector from player to enemy +Vector3 direction = enemy.Transform.Origin - player.Transform.Origin; +if (direction.Dot(enemy.Transform.Basis.Z) > 0) +{ + enemy.ImWatchingYou(player); +} + +``` + + + + + +Strafe left: + + + + + +```gdscript +# Remember that +X is right +if Input.is_action_pressed("strafe_left"): + translate_object_local(-transform.basis.x) + +``` + + + + + +```csharp +// Remember that +X is right +if (Input.IsActionPressed("strafe_left")) +{ + TranslateObjectLocal(-Transform.Basis.X); +} + +``` + + + + + +Jump: + + + + + +```gdscript +# Keep in mind Y is up-axis +if Input.is_action_just_pressed("jump"): + velocity.y = JUMP_SPEED + +move_and_slide() + +``` + + + + + +```csharp +// Keep in mind Y is up-axis +if (Input.IsActionJustPressed("jump")) + velocity.Y = JumpSpeed; + +MoveAndSlide(); + +``` + + + + + +All common behaviors and logic can be done with just vectors. + +# Setting information + +There are, of course, cases where you want to set information to a transform. Imagine a first person controller or orbiting camera. Those are definitely done using angles, because you *do want* the transforms to happen in a specific order. + +For such cases, keep the angles and rotations *outside* the transform and set them every frame. Don't try to retrieve and reuse them because the transform is not meant to be used this way. + +Example of looking around, FPS style: + + + + + +```gdscript +# accumulators +var rot_x = 0 +var rot_y = 0 + +func _input(event): + if event is InputEventMouseMotion and event.button_mask & 1: + # modify accumulated mouse rotation + rot_x += event.relative.x * LOOKAROUND_SPEED + rot_y += event.relative.y * LOOKAROUND_SPEED + transform.basis = Basis() # reset rotation + rotate_object_local(Vector3(0, 1, 0), rot_x) # first rotate in Y + rotate_object_local(Vector3(1, 0, 0), rot_y) # then rotate in X + +``` + + + + + +```csharp +// accumulators +private float _rotationX = 0f; +private float _rotationY = 0f; + +public override void _Input(InputEvent @event) +{ + if (@event is InputEventMouseMotion mouseMotion) + { + // modify accumulated mouse rotation + _rotationX += mouseMotion.Relative.X * LookAroundSpeed; + _rotationY += mouseMotion.Relative.Y * LookAroundSpeed; + + // reset rotation + Transform3D transform = Transform; + transform.Basis = Basis.Identity; + Transform = transform; + + RotateObjectLocal(Vector3.Up, _rotationX); // first rotate about Y + RotateObjectLocal(Vector3.Right, _rotationY); // then rotate about X + } +} + +``` + + + + + +As you can see, in such cases it's even simpler to keep the rotation outside, then use the transform as the *final* orientation. + +# Interpolating with quaternions + +Interpolating between two transforms can efficiently be done with quaternions. More information about how quaternions work can be found in other places around the Internet. For practical use, it's enough to understand that pretty much their main use is doing a closest path interpolation. As in, if you have two rotations, a quaternion will smoothly allow interpolation between them using the closest axis. + +Converting a rotation to quaternion is straightforward. + + + + + +```gdscript +# Convert basis to quaternion, keep in mind scale is lost +var a = Quaternion(transform.basis) +var b = Quaternion(transform2.basis) +# Interpolate using spherical-linear interpolation (SLERP). +var c = a.slerp(b,0.5) # find halfway point between a and b +# Apply back +transform.basis = Basis(c) + +``` + + + + + +```csharp +// Convert basis to quaternion, keep in mind scale is lost +var a = transform.Basis.GetQuaternion(); +var b = transform2.Basis.GetQuaternion(); +// Interpolate using spherical-linear interpolation (SLERP). +var c = a.Slerp(b, 0.5f); // find halfway point between a and b +// Apply back +transform.Basis = new Basis(c); + +``` + + + + + +The [class_Quaternion](class_Quaternion) type reference has more information on the datatype (it +can also do transform accumulation, transform points, etc., though this is used +less often). If you interpolate or apply operations to quaternions many times, +keep in mind they need to be eventually normalized. Otherwise, they will also +suffer from numerical precision errors. + +Quaternions are useful when doing camera/path/etc. interpolations, as the result will always be correct and smooth. + +## Transforms are your friend + +For most beginners, getting used to working with transforms can take some time. However, once you get used to them, you will appreciate their simplicity and power. + +Don't hesitate to ask for help on this topic in any of Redot's [online communities](https://redotengine.org/community) and, once you become confident enough, please help others! \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/variable_rate_shading.md b/Redot-Documentation/docs/26.1/Tutorials/3d/variable_rate_shading.md new file mode 100644 index 0000000..a25713c --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/variable_rate_shading.md @@ -0,0 +1,208 @@ + +# Variable rate shading + +## What is variable rate shading? + +In modern 3D rendering engines, shaders are much more complex compared to +before. The advent of physically-based rendering, real-time global illumination +and screen-space effects has increased the number of *per-pixel* shading that +must be performed to render each frame. Additionally, screen resolutions also +have increased a lot, with 1440p and 4K now being common target resolutions. +As a result, the total shading cost in scene rendering usually represents +a significant amount of the time taken to render each frame. + +Variable rate shading (VRS) is a method of decreasing this shading cost by +reducing the resolution of *per-pixel* shading (also called *fragment* shading), +while keeping the original resolution for rendering geometry. This means geometry +edges remain as sharp as they would without VRS. VRS can be combined with any +[doc_3d_antialiasing](3d_antialiasing.md) technique (MSAA, FXAA, TAA, SSAA). + +VRS allows specifying the shading quality in a local manner, which makes it +possible to have certain parts of the viewport receive more detailed shading +than others. This is particularly useful in virtual reality (VR) to achieve +*foveated rendering*, where the center of the viewport is more detailed than the +edges. + +Here's a scene rendered with rate shading disabled then enabled, using the +density map linked at the bottom of this page: + +
+ Variable rate shading disabled in textured scene +
+ Variable rate shading disabled in textured scene +
+
+ +
+ Variable rate shading enabled in textured scene (lower quality, but higher performance) +
+ Variable rate shading enabled in textured scene (lower quality, but higher performance) +
+
+ +When used in scenes with low-frequency detail (such as scenes with a +stylized/low-poly aesthetic), it's possible to achieve similar performance gains, +but with less reduction in visual quality: + +
+ Variable rate shading disabled in untextured scene +
+ Variable rate shading disabled in untextured scene +
+
+ +
+ Variable rate shading enabled in untextured scene (lower quality, but higher performance) +
+ Variable rate shading enabled in untextured scene (lower quality, but higher performance) +
+
+ +## Hardware support + +Variable rate shading is only supported on specific GPUs: + +**Desktop:** + +- NVIDIA Turing and newer (including GTX 1600 series) +- AMD RDNA2 and newer (both integrated and dedicated GPUs – including Steam Deck) +- Intel Arc Alchemist and newer **(dedicated GPUs only)** + + - Intel integrated graphics do not support variable rate shading. + +**Mobile SoCs:** + +- Snapdragon 888 and newer +- MediaTek Dimensity 9000 and newer +- ARM Mali-G615 and newer + +As of January 2023, Apple and Raspberry Pi GPUs do not support variable rate shading. + +## Using variable rate shading in Redot + +:::note + +Both Forward+ and Mobile renderers support variable rate +shading. VRS can be used in both pancake (non-XR) and XR display modes. + +The Compatibility renderer does **not** support variable rate shading. +For XR, you can use [foveation level](doc_openxr_settings_foveation_level) +as an alternative. + +::: + +In the advanced Project Settings, the **Rendering > VRS** section offers settings +to control variable rate shading on the root viewport: + +- **Mode:** Controls the variable rate shading mode. **Disabled** disables + variable rate shading. **Texture** uses a manually authored texture to set + shading density (see the property below). **XR** automatically generates a + texture suited for foveated rendering in virtual/augmented reality. +- **Texture:** The texture to use to control shading density on the root + viewport. Only used if **Mode** is **Texture**. + +For custom viewports, the VRS mode and texture must be set manually to the +[class_Viewport](class_Viewport) node. + +:::note + +On unsupported hardware, there is no visual difference when variable rate +shading is enabled. You can check whether hardware supports variable rate +shading by running the editor or project with the ``--verbose`` +[command line argument](../editor/command_line_tutorial.md). + +::: + +### Creating a VRS density map + +If using the **Texture** VRS mode, you *must* set a texture to be used as a +density map. Otherwise, no effect will be visible. + +You can create your own VRS density map manually using an image editor, or +generate it using another method (e.g. on the CPU using the Image class, or on +the GPU using a shader). However, beware of performance implications when +generating a VRS image dynamically. If opting for dynamic generation, make sure +the VRS image generation process is fast enough to avoid outweighing the +performance gains from VRS. + +The texture must follow these rules: + +- The texture *must* use a lossless compression format so that colors can be + matched precisely. +- The following VRS densities are mapped to various colors, with brighter colors + representing a lower level of shading precision: + +| Density | Color | Comment | +| --- | --- | --- | +| 1×1 (highest detail) | ``rgb(0, 0, 0) - #000000`` | | +| 1×2 | ``rgb(0, 85, 0) - #005500`` | | +| 2×1 | ``rgb(85, 0, 0) - #550000`` | | +| 2×2 | ``rgb(85, 85, 0) - #555500`` | | +| 2×4 | ``rgb(85, 170, 0) - #55aa00`` | | +| 4×2 | ``rgb(170, 85, 0) - #aa5500`` | | +| 4×4 | ``rgb(170, 170, 0) - #aaaa00`` | | +| 4×8 | ``rgb(170, 255, 0) - #aaff00`` | Not supported on most hardware. | +| 8×4 | ``rgb(255, 170, 0) - #ffaa00`` | Not supported on most hardware. | +| 8×8 (lowest detail) | ``rgb(255, 255, 0) - #ffff00`` | Not supported on most hardware. | + +For example, this VRS density texture provides the highest shading density in +the center of the viewport, and the lowest shading density in the corners: + +
+ Example VRS density map texture, simulating foveated rendering +
+ Example VRS density map texture, simulating foveated rendering +
+
+ +There are no size or aspect ratio requirements for the VRS density texture. +However, there is no benefit to using a VRS density map that is larger than the +viewport resolution divided by the GPU's *tile size*. The tile size is what +determines the smallest area of pixels where the shading density can be changed +separately from other tiles. On most GPUs, this tile size is 8×8 pixels. You can +view the tile size by running Redot with the ``--verbose`` command line +argument, as it's printed in the VRS debugging information. + +Therefore, sticking to a relatively low resolution such as 256×256 (square) or +480×270 (16:9) is recommended. Depending on your use cases, a square texture may +be more suited compared to a texture that matches the most common viewport +aspect ratio in your project (such as 16:9). + +:::tip + +When using variable rate shading, you can use a negative +[texture mipmap LOD bias](doc_resolution_scaling_mipmap_bias) +to reduce blurriness in areas with reduced shading rate. + +Note that the texture LOD bias is set globally, so this will also affect +areas of the viewport with full shading rate. Don't use values that are too +low, or textures will appear grainy. + +::: + +### Performance comparison + +To give an idea of how much VRS can improve performance in theory, here's a +performance comparison with the textured example scene shown at the top of this +page. The VRS density map example present on this page is used. + +Results were captured on a GeForce RTX 4090 with the NVIDIA 525.60.11 driver. + +| Resolution | VRS disabled | VRS enabled | Performance improvement | +| --- | --- | --- | --- | +| 1920×1080 (Full HD) | 2832 FPS | 3136 FPS | +10.7% | +| 2560×1440 (QHD) | 2008 FPS | 2256 FPS | +12.3% | +| 3840×2160 (4K) | 1236 FPS | 1436 FPS | +16.2% | +| 7680×4320 (8K) | 384 FPS | 473 FPS | +23.1% | + +In terms of performance improvements, variable rate shading is more beneficial +at higher target resolutions. The reduction in visual quality is also less +noticeable at high resolutions. + +:::note + +For non-VR games, you will probably have to use a less aggressive VRS texture +than what was used in this example. As a result, the effective performance +gains will be lower. + +::: diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/visibility_ranges.md b/Redot-Documentation/docs/26.1/Tutorials/3d/visibility_ranges.md new file mode 100644 index 0000000..b6fab6d --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/visibility_ranges.md @@ -0,0 +1,255 @@ + +# Visibility ranges (HLOD) + +Along with [doc_mesh_lod](mesh_lod.md) and [doc_occlusion_culling](occlusion_culling.md), +visibility ranges are another tool to improve performance in large, +complex 3D scenes. + +On this page, you'll learn: + +- What visibility ranges can do and which scenarios they are useful in. +- How to set up visibility ranges (manual LOD) in Redot. +- How to tune visibility ranges for best performance and quality. + +:::info + +If you only need meshes to become less detailed over distance, but don't have +manually authored LOD meshes, consider relying on automatic +[doc_mesh_lod](mesh_lod.md) instead. + +Note that automatic mesh LOD and visibility ranges can be used at the same +time, even on the same mesh. + +::: + +## How it works + +Visibility ranges can be used with any node that inherits from GeometryInstance3D. +This means they can be used not only with MeshInstance3D and MultiMeshInstance3D +for artist-controlled :abbr:`HLOD (Hierarchical Level of Detail)`, but also +GPUParticles3D, CPUParticles3D, Label3D, Sprite3D, AnimatedSprite3D and CSGShape3D. + +Since visibility ranges are configured on a per-node basis, this makes it possible +to use different node types as part of a :abbr:`LOD (Level of Detail)` system. +For example, you could display a MeshInstance3D representing a tree when up close, +and replace it with a Sprite3D impostor in the distance to improve performance. + +The benefit of :abbr:`HLOD (Hierarchical Level of Detail)` over a traditional +:abbr:`LOD (Level of Detail)` system is its hierarchical nature. A single larger +mesh can replace several smaller meshes, so that the number of draw calls can be +reduced at a distance, but culling opportunities can be preserved when up close. +For example, you can have a group of houses that uses individual MeshInstance3D +nodes (one for each house) when up close, but turns into a single MeshInstance3D +that represents a less detailed group of houses (or use a MultiMeshInstance3D). + +Lastly, visibility ranges can also be used to fade certain objects entirely when +the camera gets too close or too far. This can be used for gameplay purposes, +but also to reduce visual clutter. For example, Label3D nodes can be faded using +visibility ranges when they're too far away to be readable or relevant to the +player. + +## Setting up visibility range + +This is a quick-start guide for configuring a basic LOD system. After following +this guide, this LOD system will display a SphereMesh when up close and a +BoxMesh when the camera is far away enough. A small hysteresis margin is also +configured via the **Begin Margin** and **End Margin** properties. This prevents +LODs from popping back and forth too quickly when the camera is moving at the +"edge" of the LOD transition. + +The visibility range properties can be found in the **Visibility Range** section +of the GeometryInstance3D inspector after selecting the MeshInstance3D Node. + +- Add a Node3D node that will be used to group the two MeshInstance3D nodes + together. +- Add a first MeshInstance3D node as a child of the Node3D. Assign a new + SphereMesh to its Mesh property. +- Set the first MeshInstance3D's visibility range **End** to ``10.0`` and **End + Margin** to ``1.0``. +- Add a second MeshInstance3D node as a child of the Node3D. Assign a new + BoxMesh to its Mesh property. +- Set the second MeshInstance3D's visibility range **Begin** to ``10.0`` and + **Begin Margin** to ``1.0``. +- Move the camera away and back towards the object. Notice how the object will + transition from a sphere to a box as the camera moves away. + +## Visibility range properties + +In the inspector of any node that inherits from GeometryInstance3D, you can adjust +the following properties in the GeometryInstance3D's **Visibility Range** section: + +- **Begin:** The instance will be hidden when the camera is closer to the + instance's *origin* than this value (in 3D units). +- **Begin Margin:** The hysteresis or alpha fade transition distance to use for + the close-up transition (in 3D units). The behavior of this property depends + on **Fade Mode**. +- **End:** The instance will be hidden when the camera is further away from the + instance's *origin* than this value (in 3D units). +- **End Margin:** The hysteresis or alpha fade transition distance to use for + the far-away transition (in 3D units). The behavior of this property depends + on **Fade Mode**. +- **Fade Mode:** Controls how the transition between LOD levels should be performed. + See below for details. + +### Fade mode + +:::note + +The fade mode chosen only has a visible impact if either +**Visibility Range > Begin Margin** or **Visibility Range > End Margin** is +greater than ``0.0``. + +::: + +In the inspector's **Visibility Range** section, there are 3 fade modes to +choose from: + +- **Disabled:** Uses hysteresis to switch between LOD levels instantly. This + prevents situations where LOD levels are switched back and forth quickly when + the player moves forward and then backward at the LOD transition point. The + hysteresis distance is determined by **Visibility Range > Begin Margin** and + **Visibility Range > End Margin**. This mode provides the best performance as + it doesn't force rendering to become transparent during the fade transition. +- **Self:** Uses alpha blending to smoothly fade between LOD levels. The node + will fade-out itself when reaching the limits of its own visibility range. The + fade transition distance is determined by **Visibility Range > Begin Margin** + and **Visibility Range > End Margin**. This mode forces transparent rendering + on the object during its fade transition, so it has a performance impact. +- **Dependencies:** Uses alpha blending to smoothly fade between LOD levels. The + node will fade-in its dependencies when reaching the limits of its own + visibility range. The fade transition distance is determined by **Visibility + Range > Begin Margin** and **Visibility Range > End Margin**. This mode forces + transparent rendering on the object during its fade transition, so it has a + performance impact. This mode is intended for hierarchical LOD systems using + [Visibility parent](doc_visibility_ranges_visibility_parent). It acts + the same as **Self** if visibility ranges are used to perform non-hierarchical + LOD. + +### Visibility parent + +The **Visibility Parent** property makes it easier to set up +:abbr:`HLOD (Hierarchical Level of Detail)`. It allows automatically hiding +child nodes if its parent is visible given its current visibility range properties. + +:::note + +The target of **Visibility Parent** *must* inherit from +[class_GeometryInstance3D](class_GeometryInstance3D). + +Despite its name, the **Visibility Parent** property *can* point to a node +that is not a parent of the node in the scene tree. However, it is +impossible to point **Visibility Parent** towards a child node, as this +creates a dependency cycle which is not supported. You will get an error +message in the Output panel if a dependency cycle occurs. + +::: + +Given the following scene tree (where all nodes inherit from GeometryInstance3D): + +``` +┖╴BatchOfHouses + ┠╴House1 + ┠╴House2 + ┠╴House3 + ┖╴House4 + +``` + +In this example, *BatchOfHouses* is a large mesh designed to represent all child +nodes when viewed at a distance. *House1* to *House4* are smaller +MeshInstance3Ds representing individual houses. To configure HLOD in this +example, we only need to configure two things: + +- Set **Visibility Range Begin** to a number greater than `0.0` so that + *BatchOfHouses* only appears when far away enough from the camera. Below this + distance, we want *House1* to *House4* to be displayed instead. +- On *House1* to *House4*, assign the **Visibility Parent** property to *BatchOfHouses*. + +This makes it easier to perform further adjustments, as you don't need to adjust +the **Visibility Range Begin** of *BatchOfHouses* and **Visibility Range End** +of *House1* to *House4*. + +Fade mode is automatically handled by the **Visibility Parent** property, so +that the child nodes only become hidden once the parent node is fully faded out. +This is done to minimize visible pop-in. Depending on your :abbr:`HLOD +(Hierarchical Level of Detail)` setup, you may want to try both the **Self** and +**Dependencies** [fade modes](doc_visibility_ranges_fade_mode). + +:::note + +Nodes hidden via the **Visible** property are essentially removed from the +visibility dependency tree, so dependent instances will not take the hidden +node or its ancestors into account. + +In practice, this means that if the target of the **Visibility Parent** node +is hidden by setting its **Visible** property to ``false``, the node will +not be hidden according to the **Visibility Range Begin** value specified in +the visibility parent. + +::: + +## Configuration tips + +### Use simpler materials at a distance to improve performance + +One way to further improve performance is to use simpler materials for distant +LOD meshes. While using LOD meshes will reduce the number of vertices that need +to be rendered, the per-pixel shading load for materials remains identical. +However, per-pixel shading load is regularly a bottleneck on the GPU in complex +3D scenes. One way to reduce this shading load on the GPU is to use simpler +materials when they don't make much of a visual difference. + +Performance gains when doing so should be carefully measured, as +increasing the number of *unique* materials in a scene has a performance cost on +its own. Still, using simpler materials for distant LOD meshes can still result +in a net performance gain as a result of the fewer per-pixel calculations +required. + +For example, on the materials used by distant LOD meshes, you can disable +expensive material features such as: + +- Normal Map (especially on mobile platforms) +- Rim +- Clearcoat +- Anisotropy +- Height +- Subsurface Scattering +- Back Lighting +- Refraction +- Proximity Fade + +### Use dithering for LOD transitions + +Redot currently only supports alpha-based fading for visibility ranges. You can +however use dithering instead by using several different materials for different +LOD levels. + +There are two advantages to using dithering over alpha blending for LOD transitions: + +- Higher performance, as dithering transparency is faster to render compared to + alpha blending. +- No visual glitches due to + [transparency sorting issues](doc_3d_rendering_limitations_transparency_sorting) + during LOD transitions. + +The downside of dithering is that a "noisy" pattern is visible during LOD fade +transitions. This may not be as noticeable at higher viewport resolutions or +when temporal antialiasing is enabled. + +Also, as distance fade in BaseMaterial3D only supports fading up close *or* +fading when far away, this setup is best used with only two LODs as part of the +setup. + +- Ensure **Begin Margin** and **End Margin** is set to ``0.0`` on both + MeshInstance3D nodes, as hysteresis or alpha fade are not desired here. +- On both MeshInstance3D nodes, *decrease* **Begin** by the desired fade transition + distance and *increase* **End** by the same distance. This is required for the + dithering transition to actually be visible. +- On the MeshInstance3D that is displayed up close, edit its material in the inspector. + Set its **Distance Fade** mode to **Object Dither**. Set **Min Distance** to + the same value as the visibility range **End**. Set **Max Distance** to the + same value *minus* the fade transition distance. +- On the MeshInstance3D that is displayed far away, edit its material in the inspector. + Set its **Distance Fade** mode to **Object Dither**. Set **Min Distance** to + the same value as the visibility range **Begin**. Set **Max Distance** to the + same value *plus* the fade transition distance. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/3d/volumetric_fog.md b/Redot-Documentation/docs/26.1/Tutorials/3d/volumetric_fog.md new file mode 100644 index 0000000..32a4c20 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/3d/volumetric_fog.md @@ -0,0 +1,311 @@ + +# Volumetric fog and fog volumes + +:::note + +Volumetric fog is only supported in the Forward+ renderer, not the Mobile or +Compatibility renderers. + +::: + +As described in [doc_environment_and_post_processing](environment_and_post_processing.md), Redot supports +various visual effects including two types of fog: traditional (non-volumetric) +fog and volumetric fog. Traditional fog affects the entire scene at once and +cannot be customized with [doc_fog_shader](../shaders/shader_reference/fog_shader.md). + +Volumetric fog can be used at the same time as non-volumetric fog if desired. + +On this page, you'll learn: + +- How to set up volumetric fog in Redot. +- What fog volumes are and how they differ from "global" volumetric fog. + +:::info + +You can see how volumetric fog works in action using the +[Volumetric Fog demo project](https://github.com/redot-engine/redot-demo-projects/tree/master/3d/volumetric_fog). + +::: + +Here is a comparison between traditional fog (which does not interact with lighting) +and volumetric fog, which is able to interact with lighting: + +![Image](/img/Tutorials/3d/img/volumetric_fog_comparison.png) + +## Volumetric fog properties + +After enabling volumetric fog in the WorldEnvironment node's Environment +resource, you can edit the following properties: + +- **Density:** The base *exponential* density of the volumetric fog. Set this to + the lowest density you want to have globally. FogVolumes can be used to add to + or subtract from this density in specific areas. A value of ``0.0`` disables + global volumetric fog while allowing FogVolumes to display volumetric fog in + specific areas. Fog rendering is exponential as in real life. +- **Albedo:** The Color of the volumetric fog when interacting with lights. Mist + and fog have an albedo close to white (``Color(1, 1, 1, 1)``) while smoke + has a darker albedo. +- **Emission:** The emitted light from the volumetric fog. Even with emission, + volumetric fog will not cast light onto other surfaces. Emission is useful to + establish an ambient color. As the volumetric fog effect uses + single-scattering only, fog tends to need a little bit of emission to soften + the harsh shadows. +- **Emission Energy:** The brightness of the emitted light from the volumetric + fog. +- **GI Inject:** Scales the strength of Global Illumination used in the + volumetric fog's albedo color. A value of ``0.0`` means that Global + Illumination will not impact the volumetric fog. This has a small performance + cost when set above ``0.0``. +- **Anisotropy:** The direction of scattered light as it goes through the + volumetric fog. A value close to ``1.0`` means almost all light is scattered + forward. A value close to ``0.0`` means light is scattered equally in all + directions. A value close to ``-1.0`` means light is scattered mostly + backward. Fog and mist scatter light slightly forward, while smoke scatters + light equally in all directions. +- **Length:** The distance over which the volumetric fog is computed. Increase + to compute fog over a greater range, decrease to add more detail when a long + range is not needed. For best quality fog, keep this as low as possible. +- **Detail Spread:** The distribution of size down the length of the froxel + buffer. A higher value compresses the froxels closer to the camera and places + more detail closer to the camera. +- **Ambient Inject:** Scales the strength of ambient light used in the + volumetric fog. A value of ``0.0`` means that ambient light will not impact + the volumetric fog. This has a small performance cost when set above ``0.0``. +- **Sky Affect:** Controls how much volumetric fog should be drawn onto the + background sky. If set to ``0.0``, volumetric fog won't affect sky rendering + at all (including FogVolumes). + +Two additional properties are offered in the **Temporal Reprojection** section: + +- **Temporal Reprojection > Enabled:** Enables temporal reprojection in the + volumetric fog. Temporal reprojection blends the current frame's volumetric + fog with the last frame's volumetric fog to smooth out jagged edges. The + performance cost is minimal, however it does lead to moving FogVolumes and + Light3Ds "ghosting" and leaving a trail behind them. When temporal + reprojection is enabled, try to avoid moving FogVolumes or Light3Ds too fast. + Short-lived dynamic lighting effects should have **Volumetric Fog Energy** set + to ``0.0`` to avoid ghosting. +- **Temporal Reprojection > Amount:** The amount by which to blend the last + frame with the current frame. A higher number results in smoother volumetric + fog, but makes "ghosting" much worse. A lower value reduces ghosting but can + result in the per-frame temporal jitter becoming visible. + +:::note + +Unlike non-volumetric fog, volumetric fog has a *finite* range. This means +volumetric fog cannot entirely cover a large world, as it will eventually +stop being rendered in the distance. + +If you wish to hide distant areas from the player, it's recommended to +enable both non-volumetric fog and volumetric fog at the same time, and +adjust their density accordingly. + +::: + +## Light interaction with volumetric fog + +To simulate fog light scattering behavior in real life, all light types will +interact with volumetric fog. How much each light will affect volumetric fog can +be adjusted using the **Volumetric Fog Energy** property on each light. Enabling +shadows on a light will also make those shadows visible on volumetric fog. + +If fog light interaction is not desired for artistic reasons, this can be +globally disabled by setting **Volumetric Fog > Albedo** to a pure black color +in the Environment resource. Fog light interaction can also be disabled for +specific lights by setting its **Volumetric Fog Energy** to ``0``. Doing so will +also improve performance slightly by excluding the light from volumetric fog +computations. + +## Using volumetric fog as a volumetric lighting solution + +While not physically accurate, it is possible to tune volumetric fog's settings +to work as volumetric *lighting* solution. This means that unlit parts of the +environment will not be darkened anymore by fog, but light will still be able to +make fog brighter in specific areas. + +This can be done by setting volumetric fog density to the lowest permitted value +*greater than zero* (``0.0001``), then increasing the **Volumetric Fog Energy** +property on lights to much higher values than the default to compensate. Values +between ``10000`` and ``100000`` usually work well for this. + +![Image](/img/Tutorials/3d/img/volumetric_fog_lighting.png) + +## Balancing performance and quality + +There are a few project settings available to adjust volumetric fog performance +and quality: + +- **Rendering > Environment > Volumetric Fog > Volume Size:** Base size used to + determine size of froxel buffer in the camera X-axis and Y-axis. The final + size is scaled by the aspect ratio of the screen, so actual values may differ + from what is set. Set a larger size for more detailed fog, set a smaller size + for better performance. +- **Rendering > Environment > Volumetric Fog > Volume Depth:** Number of slices + to use along the depth of the froxel buffer for volumetric fog. A lower number + will be more efficient, but may result in artifacts appearing during camera + movement. +- **Rendering > Environment > Volumetric Fog > Use Filter:** Enables filtering + of the volumetric fog effect prior to integration. This substantially blurs + the fog which reduces fine details, but also smooths out harsh edges and + aliasing artifacts. Disable when more detail is required. + +:::note + +Volumetric fog can cause banding to appear on the viewport, especially at +higher density levels. See [doc_3d_rendering_limitations_color_banding](doc_3d_rendering_limitations_color_banding) +for guidance on reducing banding. + +::: + +## Using fog volumes for local volumetric fog + +Sometimes, you want fog to be constrained to specific areas. Conversely, you may +want to have global volumetric fog, but fog should be excluded from certain +areas. Both approaches can be followed using FogVolume nodes. + +Here's a quick start guide to using FogVolumes: + +- Make sure **Volumetric Fog** is enabled in the Environment properties. If + global volumetric fog is undesired, set its **Density** to ``0.0``. +- Create a FogVolume node. +- Assign a new FogMaterial to the FogVolume node's **Material** property. +- In the FogMaterial, set **Density** to a positive value to increase density + within the FogVolume, or a negative value to subtract the density from global + volumetric fog. +- Configure the FogVolume's extents and shape as needed. + +:::note + +Thin fog volumes may appear to flicker when the camera moves or rotates. +This can be alleviated by increasing the +**Rendering > Environment > Volumetric Fog > Volume Depth** project setting +(at a performance cost) or by decreasing **Length** in the Environment +volumetric fog properties (at no performance cost, but at the cost of lower +fog range). Alternatively, the FogVolume can be made thicker and use a lower +density in the **Material**. + +::: + +## FogVolume properties + +- **Extents:** The size of the FogVolume when **Shape** is **Ellipsoid**, + **Cone**, **Cylinder** or **Box**. If **Shape** is **Cone** or **Cylinder**, + the cone/cylinder will be adjusted to fit within the extents. Non-uniform + scaling of cone/cylinder shapes via the **Extents** property is not supported, + but you can scale the FogVolume node instead. +- **Shape:** The shape of the FogVolume. This can be set to **Ellipsoid**, + **Cone**, **Cylinder**, **Box** or **World** (acts as global volumetric fog). +- **Material:** The material used by the FogVolume. Can be either a + built-in FogMaterial or a custom ShaderMaterial ([doc_fog_shader](../shaders/shader_reference/fog_shader.md)). + +After choosing **New FogMaterial** in the **Material** property, you can adjust +the following properties in FogMaterial: + +- **Density:** The density of the FogVolume. Denser objects are more opaque, but + may suffer from under-sampling artifacts that look like stripes. Negative + values can be used to subtract fog from other FogVolumes or global volumetric + fog. +- **Albedo:** The single-scattering Color of the FogVolume. Internally, member + albedo is converted into single-scattering, which is additively blended with + other FogVolumes and global volumetric fog's **Albedo**. +- **Emission:** The Color of the light emitted by the FogVolume. Emitted light + will not cast light or shadows on other objects, but can be useful for + modulating the Color of the FogVolume independently from light sources. +- **Height Falloff:** The rate by which the height-based fog decreases in + density as height increases in world space. A high falloff will result in a + sharp transition, while a low falloff will result in a smoother transition. + A value of ``0.0`` results in uniform-density fog. The height threshold is + determined by the height of the associated FogVolume. +- **Edge Fade:** The hardness of the edges of the FogVolume. A higher value will + result in softer edges, while a lower value will result in harder edges. +- **Density Texture:** The 3D texture that is used to scale the member density + of the FogVolume. This can be used to vary fog density within the FogVolume + with any kind of static pattern. For animated effects, consider using a custom + [fog shader](../shaders/shader_reference/fog_shader.md). + You can import any image as a 3D texture by + [changing its import type in the Import dock](doc_importing_images_changing_import_type). + +### Using 3D noise density textures + +Since Redot 4.1, there is a NoiseTexture3D resource that can be used to +procedurally generate 3D noise. This is well-suited to FogMaterial density +textures, which can result in more detailed fog effects: + +
+ FogMaterial comparison (without and with density texture) +
+ Screenshot taken with **Volume Size** project setting set to 192 to make +high-frequency detail more visible in the fog. +
+
+ +To do so, select the **Density Texture** property and choose **New NoiseTexture3D**. +Edit this NoiseTexture3D by clicking it, then click **Noise** at the bottom of the +NoiseTexture3D properties and choose **New FastNoiseLite**. Adjust the noise texture's +width, height and depth according to your fog volume's dimensions. + +To improve performance, it's recommended to use low texture sizes (64×64×64 or lower), +as high-frequency detail is difficult to notice in a FogVolume. If you wish to represent +more detailed density variations, you will need to increase +**Rendering > Environment > Volumetric Fog > Volume Size** in the project settings, +which has a performance cost. + +:::note + +NoiseTexture3D's **Color Ramp** affects FogMaterial density textures, but +since only the texture's red channel is sampled, only the color ramp's red +channel will affect the resulting density. + +However, using a color ramp will *not* tint the fog volume according to the +texture. You would need to use a custom shader that reads a Texture3D to +achieve this. + +::: + +## Custom FogVolume shaders + +This page only covers the built-in settings offered by FogMaterial. If you need +to customize fog behavior within a FogVolume node (such as creating animated fog), +FogVolume nodes' appearance can be customized using [doc_fog_shader](../shaders/shader_reference/fog_shader.md). + +## Faking volumetric fog using quads + +In some cases, it may be better to use specially configured QuadMeshes as an +alternative to volumetric fog: + +- Quads work with any rendering method, including Mobile and Compatibility. +- Quads do not require temporal reprojection to look smooth, which makes + them suited to fast-moving dynamic effects such as lasers. They can also + represent small details which volumetric fog cannot do efficiently. +- Quads generally have a lower performance cost than volumetric fog. + +This approach has a few downsides though: + +- The fog effect has less realistic falloff, especially if the camera enters the fog. +- Transparency sorting issues may occur when sprites overlap. +- Performance will not necessarily be better than volumetric fog if there are + lots of sprites close to the camera. + +To create a QuadMesh-based fog sprite: + +1. Create a MeshInstance3D node with a QuadMesh resource in the **Mesh** + property. Set the size as desired. +2. Create a new StandardMaterial3D in the mesh's **Material** property. +3. In the StandardMaterial3D, set **Shading > Shading Mode** to **Unshaded**, + **Billboard > Mode** to **Enabled**, enable **Proximity Fade** and set + **Distance Fade** to **Pixel Alpha**. +4. Set the **Albedo > Texture** to the texture below (right-click and choose **Save as…**): + + .. image:: img/volumetric_fog_quad_mesh_texture.webp + +5. *After* setting the albedo texture, go to the Import dock, select the texture + and change its compression mode to **Lossless** to improve quality. + +The fog's color is set using the **Albedo > Color** property; its density is set +using the color's alpha channel. For best results, you will have to adjust +**Proximity Fade > Distance** and **Distance Fade > Max Distance** depending on +the size of your QuadMesh. + +Optionally, billboarding may be left disabled if you place the quad in a way +where all of its corners are in solid geometry. This can be useful for fogging +large planes that the camera cannot enter, such as bottomless pits. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/animation/2d_skeletons.md b/Redot-Documentation/docs/26.1/Tutorials/animation/2d_skeletons.md new file mode 100644 index 0000000..aabb8f0 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/animation/2d_skeletons.md @@ -0,0 +1,232 @@ +:::warning +This page is marked as outdated and may not reflect current Redot behavior. +::: + +# 2D skeletons + +## Introduction + +When working with 3D, skeletal deforms are common for characters and creatures +and most 3D modeling applications support it. For 2D, as this function is not +used as often, it's difficult to find mainstream software aimed for this. + +One option is to create animations in third-party software such as Spine or +Dragonbones. This functionality is also supported built-in. + +Why would you want to do skeletal animations directly in Redot? The answer is +that there are many advantages to it: + +* Better integration with the engine, so less hassle importing and editing from + an external tool. +* Ability to control particle systems, shaders, sounds, call scripts, colors, + transparency, etc. in animations. +* The built-in skeletal system in Redot is very efficient and designed for + performance. + +The following tutorial will, then, explain 2D skeletal deformations. + +## Setup + +:::info + +Before starting, we recommend you to go through the +[doc_cutout_animation](cutout_animation.md) tutorial to gain a general understanding of +animating within Redot. + +::: + +For this tutorial, we will be using a single image to construct our character. +Download it from :download:`gBot_pieces.png <img/gBot_pieces.png>` or save the +image below. + +![Image](/img/Tutorials/animation/img/gBot_pieces.png) + +It is also advised to download the final character image +:download:`gBot_complete.png <img/gBot_complete.png>` to have a good reference +for putting the different pieces together. + +![Image](/img/Tutorials/animation/img/gBot_complete.png) + +## Creating the polygons + +Create a new scene for your model (if it's going to be an animated character, +you may want to use a ``CharacterBody2D``). For ease of use, an empty 2D node is +created as a root for the polygons. + +Begin with a ``Polygon2D`` node. There is no need to place it anywhere in the +scene for now, so simply create it like this: + +![Image](/img/Tutorials/animation/img/skel2d1.png) + +Select it and assign the texture with the character pieces you have downloaded +before: + +![Image](/img/Tutorials/animation/img/skel2d2.png) + +Drawing a polygon directly is not advised. Instead, open the "UV" dialog for the +polygon: + +![Image](/img/Tutorials/animation/img/skel2d3.png) + +Head over to the *Points* mode, select the pencil and draw a polygon around the +desired piece: + +![Image](/img/Tutorials/animation/img/skel2d4.png) + +Duplicate the polygon node and give it a proper name. Then, enter the "UV" +dialog again and replace the old polygon with another one in the new desired +piece. + +When you duplicate nodes and the next piece has a similar shape, you can edit +the previous polygon instead of drawing a new one. + +After moving the polygon, remember to update the UV by selecting +**Edit > Copy Polygon to UV** in the Polygon 2D UV Editor. + +![Image](/img/Tutorials/animation/img/skel2d5.png) + +Keep doing this until you mapped all pieces. + +![Image](/img/Tutorials/animation/img/skel2d6.png) + +You will notice that pieces for nodes appear in the same layout as they do in +the original texture. This is because by default, when you draw a polygon, the +UV and points are the same. + +Rearrange the pieces and build the character. This should be pretty quick. There +is no need to change pivots, so don't bother making sure rotation pivots for +each piece are right; you can leave them be for now. + +![Image](/img/Tutorials/animation/img/skel2d7.png) + +Ah, the visual order of the pieces is not correct yet, as some are covering +wrong pieces. Rearrange the order of the nodes to fix this: + +![Image](/img/Tutorials/animation/img/skel2d8.png) + +And there you go! It was definitely much easier than in the cutout tutorial. + +## Creating the skeleton + +Create a ``Skeleton2D`` node as a child of the root node. This will be the base +of our skeleton: + +![Image](/img/Tutorials/animation/img/skel2d9.png) + +Create a ``Bone2D`` node as a child of the skeleton. Put it on the hip (usually +skeletons start here). The bone will be pointing to the right, but you can +ignore this for now. + +![Image](/img/Tutorials/animation/img/skel2d10.png) + +Keep creating bones in hierarchy and naming them accordingly. + +![Image](/img/Tutorials/animation/img/skel2d11.png) + +At the end of this chain, there will be a *jaw* node. It is, again, very short +and pointing to the right. This is normal for bones without children. The length +of *tip* bones can be changed with a property in the inspector: + +![Image](/img/Tutorials/animation/img/skel2d12.png) + +In this case, we don't need to rotate the bone (coincidentally the jaw points +right in the sprite), but in case you need to, feel free to do it. Again, this +is only really needed for tip bones as nodes with children don't usually need a +length or a specific rotation. + +Keep going and build the whole skeleton: + +![Image](/img/Tutorials/animation/img/skel2d13.png) + +You will notice that all bones raise a warning about a missing rest pose. A rest +pose is the default pose for a skeleton, you can come back to it anytime you want +(which is very handy for animating). To set one click on the *skeleton* node in +the scene tree, then click on the ``Skeleton2D`` button in the toolbar, and select +``Overwrite Rest Pose`` from the dropdown menu. + +![Image](/img/Tutorials/animation/img/skel2d14.webp) + +The warnings will go away. If you modify the skeleton (add/remove bones) you +will need to set the rest pose again. + +## Deforming the polygons + +Select the previously created polygons and assign the skeleton node to their +``Skeleton`` property. This will ensure that they can eventually be deformed by +it. + +![Image](/img/Tutorials/animation/img/skel2d15.png) + +Click the property highlighted above and select the skeleton node: + +![Image](/img/Tutorials/animation/img/skel2d16.png) + +Again, open the UV editor for the polygon and go to the *Bones* section. + +![Image](/img/Tutorials/animation/img/skel2d17.png) + +You will not be able to paint weights yet. For this you need to synchronize the +list of bones from the skeleton with the polygon. This step is done only once +and manually (unless you modify the skeleton by adding/removing/renaming bones). +It ensures that your rigging information is kept in the polygon, even if a +skeleton node is accidentally lost or the skeleton modified. Push the "Sync +Bones to Polygon" button to sync the list. + +![Image](/img/Tutorials/animation/img/skel2d18.png) + +The list of bones will automatically appear. By default, your polygon has no +weight assigned to any of them. Select the bones you want to assign weight to +and paint them: + +![Image](/img/Tutorials/animation/img/skel2d19.png) + +Points in white have a full weight assigned, while points in black are not +influenced by the bone. If the same point is painted white for multiple bones, +the influence will be distributed amongst them (so usually there is not that +much need to use shades in-between unless you want to polish the bending +effect). + +![Image](/img/Tutorials/animation/img/skel2d20.gif) + +After painting the weights, animating the bones (NOT the polygons!) will have +the desired effect of modifying and bending the polygons accordingly. As you +only need to animate bones in this approach, work becomes much easier! + +But it's not all roses. Trying to animate bones that bend the polygon will often +yield unexpected results: + +![Image](/img/Tutorials/animation/img/skel2d21.gif) + +This happens because Redot generates internal triangles that connect the points +when drawing the polygon. They don't always bend the way you would expect. To +solve this, you need to set hints in the geometry to clarify how you expect it +to deform. + +## Internal vertices + +Open the UV menu for each bone again and go to the *Points* section. Add some +internal vertices in the regions where you expect the geometry to bend: + +![Image](/img/Tutorials/animation/img/skel2d22.png) + +Now, go to the *Polygon* section and redraw your own polygons with more detail. +Imagine that, as your polygons bend, you need to make sure they deform the least +possible, so experiment a bit to find the right setup. + +![Image](/img/Tutorials/animation/img/skel2d23.png) + +Once you start drawing, the original polygon will disappear and you will be free +to create your own: + +![Image](/img/Tutorials/animation/img/skel2d24.png) + +This amount of detail is usually fine, though you may want to have more +fine-grained control over where triangles go. Experiment by yourself until you +get the results you like. + +**Note:** Don't forget that your newly added internal vertices also need weight +painting! Go to the *Bones* section again to assign them to the right bones. + +Once you are all set, you will get much better results: + +![Image](/img/Tutorials/animation/img/skel2d25.gif) \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/animation/animation_track_types.md b/Redot-Documentation/docs/26.1/Tutorials/animation/animation_track_types.md new file mode 100644 index 0000000..22cd667 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/animation/animation_track_types.md @@ -0,0 +1,228 @@ + +# Animation Track types + +This page gives an overview of the track types available for Redot's animation +player node on top of the default property tracks. + +:::info + +We assume you already read [doc_introduction_animation](introduction.md), which covers +the basics, including property tracks. + +::: + +![Image](/img/Tutorials/animation/img/track_types.webp) + +## Property Track + +The most basic track type. See [doc_introduction_animation](introduction.md). + +## Position 3D / Rotation 3D / Scale 3D Track + +These 3D transform tracks control the location, rotation, and scale of a 3D object. +They make it easier to animate a 3D object's transform compared to using regular +property tracks. + +It is designed for animations imported from external 3D models and can reduce resource capacity through compression. + +## Blend Shape Track + +A blend shape track is optimized for animating blend shape in [MeshInstance3D ](class_MeshInstance3D). + +It is designed for animations imported from external 3D models and can reduce resource capacity through compression. + +## Call Method Track + +A call method track allow you to call a function at a precise time from within an +animation. For example, you can call ``queue_free()`` to delete a node at the +end of a death animation. + +:::note +The events placed on the call method track are not executed when the animation is previewed in the editor for safety. + +::: + +To create such a track in the editor, click "Add Track -> Call Method Track." Then, a window +opens and lets you select the node to associate with the track. To call one of +the node's methods, right-click the timeline and select "Insert Key". A window +opens with a list of available methods. Double-click one to finish creating the +keyframe. + +![Image](/img/Tutorials/animation/img/node_methods.webp) + +To change the method call or its arguments, click on the key and head to the +inspector dock. There, you can change the method to call. If you expand the +"Args" section, you will see a list of arguments you can edit. + +![Image](/img/Tutorials/animation/img/node_method_args.webp) + +To create such a track through code, pass a dictionary that contains the target method's name +and parameters as the Variant for ``key`` in ``Animation.track_insert_key()``. The keys and +their expected values are as follows: + +| **Key** | **Value** | +| --- | --- | +| ``"method"`` | The name of the method as a ``String`` | +| ``"args"`` | The arguments to pass to the function as an ``Array`` | + + + + + +```gdscript +# Create a call method track. +func create_method_animation_track(): + # Get or create the animation the target method will be called from. + var animation = $AnimationPlayer.get_animation("idle") + # Get or create the target method's animation track. + var track_index = animation.add_track(Animation.TYPE_METHOD) + # Make the arguments for the target method jump(). + var jump_velocity = -400.0 + var multiplier = randf_range(.8, 1.2) + # Get or create a dictionary with the target method's name and arguments. + var method_dictionary = { + "method": "jump", + "args": [jump_velocity, multiplier], + } + + # Set scene-tree path to node with target method. + animation.track_set_path(track_index, ".") + # Add the dictionary as the animation method track's key. + animation.track_insert_key(track_index, 0.6, method_dictionary, 0) + +# The target method that will be called from the animation. +func jump(jump_velocity, multiplier): + velocity.y = jump_velocity * multiplier + +``` + + + + + +```csharp +// Create a call method track. +public void CreateAnimationTrack() +{ + // Get reference to the AnimationPlayer. + var animationPlayer = GetNode("AnimationPlayer"); + // Get or create the animation the target method will be called from. + var animation = animationPlayer.GetAnimation("idle"); + // Get or create the target method's animation track. + var trackIndex = animation.AddTrack(Animation.TrackType.Method); + // Make the arguments for the target method Jump(). + var jumpVelocity = -400.0; + var multiplier = GD.RandRange(.8, 1.2); + // Get or create a dictionary with the target method's name and arguments. + var methodDictionary = new Godot.Collections.Dictionary + { + { "method", MethodName.Jump }, + { "args", new Godot.Collections.Array { jumpVelocity, multiplier } } + }; + + // Set scene-tree path to node with target method. + animation.TrackSetPath(trackIndex, "."); + // Add the dictionary as the animation method track's key. + animation.TrackInsertKey(trackIndex, 0.6, methodDictionary, 0); +} + +// The target method that will be called from the animation. +private void Jump(float jumpVelocity, float multiplier) +{ + Velocity = new Vector2(Velocity.X, jumpVelocity * multiplier); +} + +``` + + + + + +## Bezier Curve Track + +A bezier curve track is similar to a property track, except it allows you to +animate a property's value using a bezier curve. + +:::note +Bezier curve track and property track cannot be blended in :ref:`AnimationPlayer ` and :ref:`AnimationTree `. + +::: + +To create one, click "Add Track -> Bezier Curve Track". As with property tracks, +you need to select a node and a property to animate. To open the bezier curve +editor, click the curve icon to the right of the animation track. + +![Image](/img/Tutorials/animation/img/bezier_curve_icon.webp) + +In the editor, keys are represented by filled diamonds and the outlined +diamonds connected to them by a line control curve's shape. + +:::tip + +For better precision while manually working with curves, you might want to alter +the zoom levels of the editor. The slider on the bottom right of the editor can be used to +zoom in and out on the time axis, you can also do that with `Ctrl + Shift + Mouse wheel`. +Using `Ctrl + Alt + Mouse wheel` will zoom in and out on the Y axis + +::: + +![Image](/img/Tutorials/animation/img/bezier_curves.webp) + +In the right click panel of the editor, you can select the handle mode: + +- Free: Allows you to orient a manipulator in any direction without affecting the + other's position. +- Linear: Does not allow rotation of the manipulator and draws a linear graph. +- Balanced: Makes it so manipulators rotate together, but the distance between + the key and a manipulator is not mirrored. +- Mirrored: Makes the position of one manipulator perfectly mirror the other, + including their distance to the key. + +![Image](/img/Tutorials/animation/img/manipulator_modes.webp) + +## Audio Playback Track + +If you want to create an animation with audio, you need to create an audio +playback track. To create one, your scene must have either an AudioStreamPlayer, +AudioStreamPlayer2D, or AudioStreamPlayer3D node. When creating the track, you +must select one of those nodes. + +To play a sound in your animation, drag and drop an audio file from the file +system dock onto the animation track. You should see the waveform of your audio +file in the track. + +![Image](/img/Tutorials/animation/img/audio_track.webp) + +To remove a sound from the animation, you can right-click it and select "Delete +Key(s)" or click on it and press the `Del` key. + +The blend mode allows you to choose whether or not to adjust the audio volume when blending in the [AnimationTree ](class_AnimationTree). + +![Image](/img/Tutorials/animation/img/blend_mode.webp) + +## Animation Playback Track + +Animation playback tracks allow you to sequence the animations of other +animation player nodes in a scene. For example, you can use it to animate +several characters in a cut-scene. + +To create an animation playback track, select "New Track -> Animation Playback +Track." + +Then, select the animation player you want to associate with the track. + +To add an animation to the track, right-click on it and insert a key. Select the +key you just created to select an animation in the inspector dock. + +![Image](/img/Tutorials/animation/img/animation_player_animation.webp) + +If an animation is already playing and you want to stop it early, you can create +a key and have it set to `[STOP]` in the inspector. + +:::note +If you instanced a scene that contains an animation player into your +scene, you need to enable "Editable Children" in the scene tree to +access its animation player. Also, an animation player cannot +reference itself. + +::: diff --git a/Redot-Documentation/docs/26.1/Tutorials/animation/animation_tree.md b/Redot-Documentation/docs/26.1/Tutorials/animation/animation_tree.md new file mode 100644 index 0000000..90bb923 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/animation/animation_tree.md @@ -0,0 +1,480 @@ + +# Using AnimationTree + +## Introduction + +With [AnimationPlayer ](class_AnimationPlayer), Redot has one of the most flexible animation systems that you can find in any game engine. +The ability to animate almost any property in any node or resource, as well as having dedicated transform, bezier, +function calling, audio and sub-animation tracks, is pretty much unique. + +However, the support for blending those animations via ``AnimationPlayer`` is relatively limited, as only a fixed cross-fade transition time can be set. + +## Creating an AnimationTree + +Before starting, it must be made clear that an ``AnimationTree`` node does not contain its own animations. +Instead, it uses animations contained in an ``AnimationPlayer`` node. This way, you can edit your animations (or import them from a 3D scene) +as usual and then use this extra node to control the playback. + +The most common way to use ``AnimationTree`` is in a 3D scene. When importing your scenes from a 3D exchange format, they will usually come +with animations built-in (either multiple ones or split from a large one on import). +At the end, the imported Redot scene will contain the animations in a ``AnimationPlayer`` node. + +As you rarely use imported scenes directly in Redot (they are either instantiated or inherited from), you can place the ``AnimationTree`` node in your +new scene which contains the imported one. Afterwards, point the ``AnimationTree`` node to the ``AnimationPlayer`` that was created in the imported scene. + +This is how it's done in the [Third Person Shooter demo ](https://github.com/redot-engine/tps-demo), for reference: + +![Image](/img/Tutorials/animation/img/animtree1.png) + +A new scene was created for the player with a ``CharacterBody3D`` as root. Inside this scene, the original ``.dae`` (Collada) file was instantiated +and an ``AnimationTree`` node was created. + +## Creating a tree + +There are three main types of nodes that can be used in ``AnimationTree``: + +1. Animation nodes, which reference an animation from the linked ``AnimationPlayer``. +2. Animation Root nodes, which are used to blend sub-nodes. +3. Animation Blend nodes, which are used within ``AnimationNodeBlendTree`` as single-graph blending via multiple input ports. + +To set a root node in ``AnimationTree``, a few types are available: + +![Image](/img/Tutorials/animation/img/animtree2.png) + +* ``AnimationNodeAnimation``: Selects an animation from the list and plays it. This is the simplest root node, and generally not used directly as root. +* ``AnimationNodeBlendTree``: Contains many *blend* type nodes, such as mix, blend2, blend3, one shot, etc. This is one of the most commonly used roots. +* ``AnimationNodeStateMachine``: Contains multiple root nodes as children in a graph. Each node is used as a *state*, and provides multiple functions to alternate between states. +* ``AnimationNodeBlendSpace2D``: Allows placing root nodes in a 2D blend space. Control the blend position in 2D to mix between multiple animations. +* ``AnimationNodeBlendSpace1D``: Simplified version of the above (1D). + +## Blend tree + +An ``AnimationNodeBlendTree`` can contain both root and regular nodes used for blending. Nodes are added to the graph from a menu: + +![Image](/img/Tutorials/animation/img/animtree3.webp) + +All blend trees contain an ``Output`` node by default, and something has to be connected to it in order for animations to play. + +The easiest way to test this functionality is to connect an ``Animation`` node to it directly: + +![Image](/img/Tutorials/animation/img/animtree4.png) + +This will simply play back the animation. Make sure that the ``AnimationTree`` is active for something to actually happen. + +Following is a short description of available nodes: + +### Blend2 / Blend3 + +These nodes will blend between two or three inputs by a user-specified blend value: + +![Image](/img/Tutorials/animation/img/animtree5.gif) + +For more complex blending, it is advised to use blend spaces instead. + +Blending can also use filters, i.e. you can control individually which tracks go through the blend function. +This is very useful for layering animations on top of each other. + +![Image](/img/Tutorials/animation/img/animtree6.png) + +### OneShot + +This node will execute a sub-animation and return once it finishes. Blend times for fading in and out can be customized, as well as filters. + +![Image](/img/Tutorials/animation/img/animtree6b.gif) + +After setting the request and changing the animation playback, the one-shot node automatically clears the request on the next process frame by setting its ``request`` value to ``AnimationNodeOneShot.ONE_SHOT_REQUEST_NONE``. + + + + + +```gdscript +# Play child animation connected to "shot" port. +animation_tree.set("parameters/OneShot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE) +# Alternative syntax (same result as above). +animation_tree["parameters/OneShot/request"] = AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE + +# Abort child animation connected to "shot" port. +animation_tree.set("parameters/OneShot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_ABORT) +# Alternative syntax (same result as above). +animation_tree["parameters/OneShot/request"] = AnimationNodeOneShot.ONE_SHOT_REQUEST_ABORT + +# Get current state (read-only). +animation_tree.get("parameters/OneShot/active")) +# Alternative syntax (same result as above). +animation_tree["parameters/OneShot/active"] + +``` + + + + + +```csharp +// Play child animation connected to "shot" port. +animationTree.Set("parameters/OneShot/request", (int)AnimationNodeOneShot.OneShotRequest.Fire); + +// Abort child animation connected to "shot" port. +animationTree.Set("parameters/OneShot/request", (int)AnimationNodeOneShot.OneShotRequest.Abort); + +// Get current state (read-only). +animationTree.Get("parameters/OneShot/active"); + +``` + + + + + +### TimeSeek + +This node can be used to cause a seek command to happen to any sub-children of the animation graph. Use this node type to play an ``Animation`` from the start or a certain playback position inside the ``AnimationNodeBlendTree``. + +After setting the time and changing the animation playback, the seek node automatically goes into sleep mode on the next process frame by setting its ``seek_request`` value to ``-1.0``. + + + + + +```gdscript +# Play child animation from the start. +animation_tree.set("parameters/TimeSeek/seek_request", 0.0) +# Alternative syntax (same result as above). +animation_tree["parameters/TimeSeek/seek_request"] = 0.0 + +# Play child animation from 12 second timestamp. +animation_tree.set("parameters/TimeSeek/seek_request", 12.0) +# Alternative syntax (same result as above). +animation_tree["parameters/TimeSeek/seek_request"] = 12.0 + +``` + + + + + +```csharp +// Play child animation from the start. +animationTree.Set("parameters/TimeSeek/seek_request", 0.0); + +// Play child animation from 12 second timestamp. +animationTree.Set("parameters/TimeSeek/seek_request", 12.0); + +``` + + + + + +### TimeScale + +Allows scaling the speed of the animation (or reverse it) connected to the `in` input via the `scale` parameter. Setting the `scale` to 0 will pause the animation. + +### Transition + +Very simple state machine (when you don't want to cope with a ``StateMachine`` node). Animations can be connected to the outputs and transition times can be specified. +After setting the request and changing the animation playback, the transition node automatically clears the request on the next process frame by setting its ``transition_request`` value to an empty string (``""``). + + + + + +```gdscript +# Play child animation connected to "state_2" port. +animation_tree.set("parameters/Transition/transition_request", "state_2") +# Alternative syntax (same result as above). +animation_tree["parameters/Transition/transition_request"] = "state_2" + +# Get current state name (read-only). +animation_tree.get("parameters/Transition/current_state") +# Alternative syntax (same result as above). +animation_tree["parameters/Transition/current_state"] + +# Get current state index (read-only). +animation_tree.get("parameters/Transition/current_index")) +# Alternative syntax (same result as above). +animation_tree["parameters/Transition/current_index"] + +``` + + + + + +```csharp +// Play child animation connected to "state_2" port. +animationTree.Set("parameters/Transition/transition_request", "state_2"); + +// Get current state name (read-only). +animationTree.Get("parameters/Transition/current_state"); + +// Get current state index (read-only). +animationTree.Get("parameters/Transition/current_index"); + +``` + + + + + +### BlendSpace2D + +``BlendSpace2D`` is a node to do advanced blending in two dimensions. Points are added to a two-dimensional space and then a position +can be controlled to determine blending: + +![Image](/img/Tutorials/animation/img/animtree7.gif) + +The ranges in X and Y can be controlled (and labeled for convenience). By default, points can be placed anywhere (right-click on +the coordinate system or use the *add point* button) and triangles will be generated automatically using Delaunay. + +![Image](/img/Tutorials/animation/img/animtree8.gif) + +It is also possible to draw the triangles manually by disabling the *auto triangle* option, though this is rarely necessary: + +![Image](/img/Tutorials/animation/img/animtree9.png) + +Finally, it is possible to change the blend mode. By default, blending happens by interpolating points inside the closest triangle. +When dealing with 2D animations (frame by frame), you may want to switch to *Discrete* mode. +Alternatively, if you want to keep the current play position when switching between discrete animations, there is a *Carry* mode. +This mode can be changed in the *Blend* menu: + +![Image](/img/Tutorials/animation/img/animtree10.png) + +### BlendSpace1D + +This is similar to 2D blend spaces, but in one dimension (so triangles are not needed). + +### StateMachine + +This node acts as a state machine with root nodes as states. Root nodes can be created and connected via lines. States are connected via *Transitions*, +which are connections with special properties. Transitions are uni-directional, but two can be used to connect in both directions. + +![Image](/img/Tutorials/animation/img/animtree11.gif) + +There are many types of transition: + +![Image](/img/Tutorials/animation/img/animtree12.png) + +* *Immediate*: Will switch to the next state immediately. The current state will end and blend into the beginning of the new one. +* *Sync*: Will switch to the next state immediately, but will seek the new state to the playback position of the old state. +* *At End*: Will wait for the current state playback to end, then switch to the beginning of the next state animation. + +Transitions also have a few properties. Click any transition and it will be displayed in the inspector dock: + +![Image](/img/Tutorials/animation/img/animtree13.png) + +* *Switch Mode* is the transition type (see above), it can be modified after creation here. +* *Auto Advance* will turn on the transition automatically when this state is reached. This works best with the *At End* switch mode. +* *Advance Condition* will turn on auto advance when this condition is set. This is a custom text field that can be filled with a variable name. + The variable can be modified from code (more on this later). +* *Xfade Time* is the time to cross-fade between this state and the next. +* *Priority* is used together with the ``travel()`` function from code (more on this later). Lower priority transitions are preferred when travelling through the tree. +* *Disabled* toggles disabling this transition (when disabled, it will not be used during travel or auto advance). + +## For better blending + +In Redot 4.0+, in order for the blending results to be deterministic (reproducible and always consistent), +the blended property values must have a specific initial value. +For example, in the case of two animations to be blended, if one animation has a property track and the other does not, +the blended animation is calculated as if the latter animation had a property track with the initial value. + +When using Position/Rotation/Scale 3D tracks for Skeleton3D bones, the initial value is Bone Rest. +For other properties, the initial value is ``0`` and if the track is present in the ``RESET`` animation, +the value of its first keyframe is used instead. + +For example, the following AnimationPlayer has two animations, but one of them lacks a Property track for Position. + +![Image](/img/Tutorials/animation/img/blending1.webp) + +This means that the animation lacking that will treat those Positions as ``Vector2(0, 0)``. + +![Image](/img/Tutorials/animation/img/blending2.webp) + +This problem can be solved by adding a Property track for Position as an initial value to the ``RESET`` animation. + +![Image](/img/Tutorials/animation/img/blending3.webp) + +![Image](/img/Tutorials/animation/img/blending4.webp) + +:::note +Be aware that the ``RESET`` animation exists to define the default pose when loading an object originally. +It is assumed to have only one frame and is not expected to be played back using the timeline. + +::: + +Also keep in mind that the Rotation 3D tracks and the Property tracks for 2D rotation +with Interpolation Type set to Linear Angle or Cubic Angle will prevent rotation of more than 180 degrees +from the initial value as blended animation. + +This can be useful for Skeleton3Ds to prevent the bones penetrating the body when blending animations. +Therefore, Skeleton3D's Bone Rest values should be as close to the midpoint of the movable range as possible. +**This means that for humanoid models, it is preferable to import them in a T-pose**. + +![Image](/img/Tutorials/animation/img/blending5.webp) + +You can see that the shortest rotation path from Bone Rests is prioritized rather than the shortest rotation path between animations. + +If you need to rotate Skeleton3D itself more than 180 degrees by blend animations for movement, you can use Root Motion. + +## Root motion + +When working with 3D animations, a popular technique is for animators to use the root skeleton bone to give motion to the rest of the skeleton. +This allows animating characters in a way where steps actually match the floor below. It also allows precise interaction with objects during cinematics. + +When playing back the animation in Redot, it is possible to select this bone as the *root motion track*. Doing so will cancel the bone +transformation visually (the animation will stay in place). + +![Image](/img/Tutorials/animation/img/animtree14.png) + +Afterwards, the actual motion can be retrieved via the [AnimationTree ](class_AnimationTree) API as a transform: + + + + + +```gdscript +# Get the motion delta. +animation_tree.get_root_motion_position() +animation_tree.get_root_motion_rotation() +animation_tree.get_root_motion_scale() + +# Get the actual blended value of the animation. +animation_tree.get_root_motion_position_accumulator() +animation_tree.get_root_motion_rotation_accumulator() +animation_tree.get_root_motion_scale_accumulator() + +``` + + + + + +```csharp +// Get the motion delta. +animationTree.GetRootMotionPosition(); +animationTree.GetRootMotionRotation(); +animationTree.GetRootMotionScale(); + +// Get the actual blended value of the animation. +animationTree.GetRootMotionPositionAccumulator(); +animationTree.GetRootMotionRotationAccumulator(); +animationTree.GetRootMotionScaleAccumulator(); + +``` + + + + + +This can be fed to functions such as [CharacterBody3D.move_and_slide ](class_CharacterBody3D_method_move_and_slide) to control the character movement. + +There is also a tool node, ``RootMotionView``, that can be placed in a scene and will act as a custom floor for your +character and animations (this node is disabled by default during the game). + +![Image](/img/Tutorials/animation/img/animtree15.gif) + +## Controlling from code + +After building the tree and previewing it, the only question remaining is "How is all this controlled from code?". + +Keep in mind that the animation nodes are just resources and, as such, they are shared between all instances using them. +Setting values in the nodes directly will affect all instances of the scene that uses this ``AnimationTree``. +This is generally undesirable, but does have some cool use cases, e.g. you can copy and paste parts of your animation tree, +or reuse nodes with a complex layout (such as a state machine or blend space) in different animation trees. + +The actual animation data is contained in the ``AnimationTree`` node and is accessed via properties. +Check the "Parameters" section of the ``AnimationTree`` node to see all the parameters that can be modified in real-time: + +![Image](/img/Tutorials/animation/img/animtree16.png) + +This is handy because it makes it possible to animate them from an ``AnimationPlayer``, or even the ``AnimationTree`` itself, +allowing the realization of very complex animation logic. + +To modify these values from code, the property path must be obtained. This is done easily by hovering the mouse over any of the parameters: + +![Image](/img/Tutorials/animation/img/animtree17.png) + +Which allows setting them or reading them: + + + + + +```gdscript +animation_tree.set("parameters/eye_blend/blend_amount", 1.0) +# Simpler alternative form: +animation_tree["parameters/eye_blend/blend_amount"] = 1.0 + +``` + + + + + +```csharp +animationTree.Set("parameters/eye_blend/blend_amount", 1.0); + +``` + + + + + +## State machine travel + +One of the nice features in Redot's ``StateMachine`` implementation is the ability to travel. The graph can be instructed to go from the +current state to another one, while visiting all the intermediate ones. This is done via the A\* algorithm. +If there is no path of transitions starting at the current state and finishing at the destination state, the graph teleports to the destination state. + +To use the travel ability, you should first retrieve the [AnimationNodeStateMachinePlayback ](class_AnimationNodeStateMachinePlayback) +object from the ``AnimationTree`` node (it is exported as a property). + + + + + +```gdscript +var state_machine = animation_tree["parameters/playback"] + +``` + + + + + +```csharp +AnimationNodeStateMachinePlayback stateMachine = (AnimationNodeStateMachinePlayback)animationTree.Get("parameters/playback"); + +``` + + + + + +Once retrieved, it can be used by calling one of the many functions it offers: + + + + + +```gdscript +state_machine.travel("SomeState") + +``` + + + + + +```csharp +stateMachine.Travel("SomeState"); + +``` + + + + + +The state machine must be running before you can travel. Make sure to either call ``start()`` or choose a node to **Autoplay on Load**. + +![Image](/img/Tutorials/animation/img/animtree18.png) \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/animation/creating_movies.md b/Redot-Documentation/docs/26.1/Tutorials/animation/creating_movies.md new file mode 100644 index 0000000..8287553 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/animation/creating_movies.md @@ -0,0 +1,487 @@ + +# Creating movies + +Redot can record **non-real-time** video and audio from any 2D or 3D project. +This kind of recording is also called *offline rendering*. +There are many scenarios where this is useful: + +- Recording game trailers for promotional use. +- Recording cutscenes that will be [displayed as pre-recorded videos ](playing_videos.md) + in the final game. This allows for using higher quality settings + (at the cost of file size), regardless of the player's hardware. +- Recording procedurally generated animations or motion design. User interaction + remains possible during video recording, and audio can be included as well + (although you won't be able to hear it while the video is recording). +- Comparing the visual output of graphics settings, shaders, or rendering techniques + in an animated scene. + +With Redot's animation features such as the AnimationPlayer node, Tweeners, +particles and shaders, it can effectively be used to create any kind of 2D and +3D animations (and still images). + +If you are already used to Redot's workflow, you may find yourself more +productive by using Redot for video rendering compared to Blender. That said, +renderers designed for non-real-time usage such as Cycles and Eevee can result +in better visuals (at the cost of longer rendering times). + +Compared to real-time video recording, some advantages of non-real-time recording include: + +- Use any graphics settings (including extremely demanding settings) regardless + of your hardware's capabilities. The output video will *always* have perfect + frame pacing; it will never exhibit dropped frames or stuttering. + Faster hardware will allow you to render a given animation in less time, but + the visual output remains identical. +- Render at a higher resolution than the screen resolution, without having to + rely on driver-specific tools such as NVIDIA's Dynamic Super Resolution or + AMD's Virtual Super Resolution. +- Render at a higher framerate than the video's target framerate, then + [post-process to generate high-quality motion blur ](doc_creating_movies_motion_blur). + This also makes effects that converge over several frames (such as temporal antialiasing, + SDFGI and volumetric fog) look better. + +:::warning + +**This feature is not designed for capturing real-time footage during gameplay.** + +Players should use something like [OBS Studio ](https://obsproject.com/) or +[SimpleScreenRecorder ](https://www.maartenbaert.be/simplescreenrecorder/) +to record gameplay videos, as they do a much better job at intercepting the +compositor than Redot can do using Vulkan or OpenGL natively. + +That said, if your game runs at near-real-time speeds when capturing, +you can still use this feature (but it will lack audible sound playback, +as sound is saved directly to the video file). + +::: + +## Enabling Movie Maker mode + +To enable Movie Maker mode, click the "movie reel" button in the top-right +corner of the editor *before* running the project: + +![Image](/img/Tutorials/animation/img/creating_movies_enable_movie_maker_mode.webp) + + Movie Maker mode is disabled, click the "movie reel" icon to enable + +The icon gets a background matching the accent color when Movie Maker mode is +enabled: + +![Image](/img/Tutorials/animation/img/creating_movies_disable_movie_maker_mode.webp) + + Movie Maker mode is enabled, click the "movie reel" icon again to disable + +Movie Maker status is **not** persisted when the editor quits, so you must +re-enable Movie Maker mode again after restarting the editor if needed. + +:::note + +Toggling Movie Maker mode while running the project will not have any +effect until the project is restarted. + +::: + +Before you can record video by running the project, you still need to configure +the output file path. This path can be set for all scenes in the Project Settings: + +![Image](/img/Tutorials/animation/img/creating_movies_project_settings.webp) + + Movie Maker project settings (with Advanced toggle enabled) + +Alternatively, you can set the output file path on a per-scene basis by adding a +String metadata with the name ``movie_file`` to the scene's **root node**. This +is only used when the main scene is set to the scene in question, or when +running the scene directly by pressing `F6` (`Cmd + R` on macOS). + +![Image](/img/Tutorials/animation/img/creating_movies_set_per_scene_metadata.webp) + + Inspector view after creating a ``movie_file`` metadata of type String + +The path specified in the project settings or metadata can be either absolute, +or relative to the project root. + +Once you've configured and enabled Movie Maker mode, it will be automatically used +when running the project from the editor. + +### Command line usage + +Movie Maker can also be enabled from the [command line ](../editor/command_line_tutorial.md): + +``` +Redot --path /path/to/your_project --write-movie output.avi + +``` + +If the output path is relative, then it is **relative to the project folder**, +not the current working directory. In the above example, the file will be +written to ``/path/to/your_project/output.avi``. This behavior is similar to the +``--export-release`` command line argument. + +Since Movie Maker's output resolution is set by the viewport size, you can +adjust the window size on startup to override it if the project uses the +``disabled`` or ``canvas_items`` [stretch mode ](../rendering/multiple_resolutions.md): + +``` +Redot --path /path/to/your_project --write-movie output.avi --resolution 1280x720 + +``` + +Note that the window size is clamped by your display's resolution. See +[doc_creating_movies_recording_at_higher_resolution](doc_creating_movies_recording_at_higher_resolution) if you need to record +a video at a higher resolution than the screen resolution. + +The recording FPS can also be overridden on the command line, +without having to edit the Project Settings: + +``` +Redot --path /path/to/your_project --write-movie output.avi --fixed-fps 30 + +``` + +:::note + +The ``--write-movie`` and ``--fixed-fps`` command line arguments are both available +in exported projects. Movie Maker mode cannot be toggled while the project is running, +but you can use the [OS.execute() ](class_OS_method_execute) method to +run a second instance of the exported project that will record a video file. + +::: + +## Choosing an output format + +Output formats are provided by the [MovieWriter ](class_MovieWriter) class. +Redot has 2 built-in [MovieWriters ](class_MovieWriter), and more can be +implemented by extensions: + +### AVI (recommended) + +AVI container with MJPEG for video and uncompressed audio. Features lossy video +compression, resulting in medium file sizes and fast encoding. The lossy +compression quality can be adjusted by changing +**Editor > Movie Writer > MJPEG Quality**. + +The resulting file can be viewed in most video players, but it must be converted +to another format for viewing on the web or by Redot with the VideoStreamPlayer +node. MJPEG does not support transparency. AVI output is currently limited to a +file of 4 GB in size at most. + +To use AVI, specify a path to an ``.avi`` file to be created in the +**Editor > Movie Writer > Movie File** project setting. + +### PNG + +PNG image sequence for video and WAV for audio. Features lossless video +compression, at the cost of large file sizes and slow encoding. This is designed +to be +[encoded to a video file with an external tool after recording ](doc_creating_movies_converting_avi). + +Transparency is supported, but the root viewport **must** have its +``transparent_bg`` property set to ``true`` for transparency to be visible on +the output image. This can be achieved by enabling the **Rendering > Transparent +Background** advanced project setting. **Display > Window > Size > Transparent** +and **Display > Window > Per Pixel Transparency > Enabled** can optionally be +enabled to allow transparency to be previewed while recording the video, but +they do not have to be enabled for the output image to contain transparency. + +To use PNG, specify a ``.png`` file to be created in the +**Editor > Movie Writer > Movie File** project setting. The generated ``.wav`` +file will have the same name as the ``.png`` file (minus the extension). + +### Custom + +If you need to encode directly to a different format or pipe a stream through +third-party software, you can extend the MovieWriter class to create your own +movie writers. This should typically be done using GDExtension for performance +reasons. + +## Configuration + +In the **Editor > Movie Writer** section of the Project Settings, there are +several options you can configure. Some of them are only visible after enabling +the **Advanced** toggle in the top-right corner of the Project Settings dialog. + +- **Mix Rate Hz:** The audio mix rate to use in the recorded audio when writing + a movie. This can be different from the project's mix rate, but this + value must be divisible by the recorded FPS to prevent audio from + desynchronizing over time. +- **Speaker Mode:** The speaker mode to use in the recorded audio when writing + a movie (stereo, 5.1 surround or 7.1 surround). +- **MJPEG Quality:** The JPEG quality to use when writing a video to an AVI + file, between ``0.01`` and ``1.0`` (inclusive). Higher quality values result + in better-looking output at the cost of larger file sizes. Recommended quality + values are between ``0.75`` and ``0.9``. Even at quality ``1.0``, JPEG + compression remains lossy. This setting does not affect audio quality and is + ignored when writing to a PNG image sequence. +- **Movie File:** The output path for the movie. This can be absolute or + relative to the project root. +- **Disable V-Sync:** If enabled, requests V-Sync to be disabled when writing a + movie. This can speed up video writing if the hardware is fast enough to + render, encode and save the video at a framerate higher than the monitor's + refresh rate. This setting has no effect if the operating system or graphics + driver forces V-Sync with no way for applications to disable it. +- **FPS:** The rendered frames per second in the output movie. Higher values + result in smoother animation, at the cost of longer rendering times and larger + output file sizes. Most video hosting platforms do not support FPS values + higher than 60, but you can use a higher value and use that to generate motion + blur. + +:::note + +When using the ``disabled`` or ``2d`` [stretch modes ](../rendering/multiple_resolutions.md), +the output file's resolution is set by the window size. Make sure to resize +the window *before* the splash screen has ended. For this purpose, it's +recommended to adjust the +**Display > Window > Size > Window Width Override** and +**Window Height Override** advanced project settings. + +See also [doc_creating_movies_recording_at_higher_resolution](doc_creating_movies_recording_at_higher_resolution). + +::: + +## Quitting Movie Maker mode + +To safely quit a project that is using Movie Maker mode, use the X button at the +top of the window, or call ``get_tree().quit()`` in a script. You can also use +the ``--quit-after N`` command line argument where ``N`` is the number of frames +to render before quitting. + +Pressing `F8` (`Cmd + .` on macOS) or pressing `Ctrl + C` on the +terminal running Redot is **not recommended**, as it will result in an +improperly formatted AVI file with no duration information. For PNG image +sequences, PNG images will not be negatively altered, but the associated WAV file +will still lack duration information. + +Some video players may still be able to play the AVI or WAV file with working +video and audio. However, software that makes use of the AVI or WAV file such as +video editors may not be able to open the file. +[Using a video converter program ](doc_creating_movies_converting_avi) +can help in those cases. + +If you're using an AnimationPlayer to control a "main action" in the scene (such +as camera movement), you can enable the **Movie Quit On Finish** property on the +AnimationPlayer node in question. When enabled, this property will make Redot +quit on its own when an animation is done playing *and* the engine is running in +Movie Maker mode. Note that *this property has no effect on looping animations*. +Therefore, you need to make sure that the animation is set as non-looping. + +## Using high-quality graphics settings + +The ``movie`` [feature tag ](../export/feature_tags.md) can be used to override +specific project settings. This is useful to enable high-quality graphics settings +that wouldn't be fast enough to run in real-time speeds on your hardware. +Remember that putting every setting to its maximum value can still slow down +movie saving speed, especially when recording at higher resolutions. Therefore, +it's still recommended to only increase graphics settings if they make a meaningful +difference in the output image. + +This feature tag can also be queried in a script to increase quality settings +that are set in the Environment resource. For example, to further improve SDFGI +detail and reduce light leaking: + + + + + +```gdscript +extends Node3D + +func _ready(): + if OS.has_feature("movie"): + # When recording a movie, improve SDFGI cell density + # without decreasing its maximum distance. + get_viewport().world_3d.environment.sdfgi_min_cell_size *= 0.25 + get_viewport().world_3d.environment.sdfgi_cascades = 8 + +``` + + + + + +```csharp +using Godot; + +public partial class MyNode3D : Node3D +{ + public override void _Ready() + { + if (OS.HasFeature("movie")) + { + // When recording a movie, improve SDFGI cell density + // without decreasing its maximum distance. + GetViewport().World3D.Environment.SdfgiMinCellSize *= 0.25f; + GetViewport().World3D.Environment.SdfgiCascades = 8; + } + } +} + +``` + + + + + +## Rendering at a higher resolution than the screen resolution + +The overall rendering quality can be improved significantly by rendering at high +resolutions such as 4K or 8K. + +:::note + +For 3D rendering, Redot provides a **Rendering > Scaling 3D > Scale** +advanced project setting, which can be set above ``1.0`` to obtain +*supersample antialiasing*. The 3D rendering is then *downsampled* when it's +drawn on the viewport. This provides an expensive but high-quality form of +antialiasing, without increasing the final output resolution. + +Consider using this project setting first, as it avoids slowing down movie +writing speeds and increasing output file size compared to actually +increasing the output resolution. + +::: + +If you wish to render 2D at a higher resolution, or if you actually need the +higher raw pixel output for 3D rendering, you can increase the resolution above +what the screen allows. + +By default, Redot uses the ``disabled`` [stretch modes ](../rendering/multiple_resolutions.md) +in projects. If using ``disabled`` or ``canvas_items`` stretch mode, +the window size dictates the output video resolution. + +On the other hand, if the project is configured to use the ``viewport`` stretch +mode, the viewport resolution dictates the output video resolution. The viewport +resolution is set using the **Display > Window > Size > Viewport Width** and +**Viewport Height** project settings. This can be used to render a video at a +higher resolution than the screen resolution. + +To make the window smaller during recording without affecting the output video +resolution, you can set the **Display > Window > Size > Window Width Override** +and **Window Height Override** advanced project settings to values greater than +``0``. + +To apply a resolution override only when recording a movie, you can override +those settings with the ``movie`` [feature tag ](../export/feature_tags.md). + +## Post-processing steps + +Some common post-processing steps are listed below. + +:::note + +When using several post-processing steps, try to perform all of them in a +single FFmpeg command. This will save encoding time and improve quality by +avoiding multiple lossy encoding steps. + +::: + +### Converting AVI video to MP4 + +While some platforms such as YouTube support uploading the AVI file directly, many +others will require a conversion step beforehand. [HandBrake ](https://handbrake.fr/) +(GUI) and [FFmpeg ](https://ffmpeg.org/) (CLI) are popular open source tools +for this purpose. FFmpeg has a steeper learning curve, but it's more powerful. + +The command below converts an AVI video to an MP4 (H.264) video with a Constant +Rate Factor (CRF) of 15. This results in a relatively large file, but is +well-suited for platforms that will re-encode your videos to reduce their size +(such as most video sharing websites): + +``` +ffmpeg -i input.avi -crf 15 output.mp4 + +``` + +To get a smaller file at the cost of quality, *increase* the CRF value in the +above command. + +To get a file with a better size/quality ratio (at the cost of slower encoding +times), add ``-preset veryslow`` before ``-crf 15`` in the above command. On the +contrary, ``-preset veryfast`` can be used to achieve faster encoding at the +cost of a worse size/quality ratio. + +### Converting PNG image sequence + WAV audio to a video + +If you chose to record a PNG image sequence with a WAV file beside it, +you need to convert it to a video before you can use it elsewhere. + +The filename for the PNG image sequence generated by Redot always contains 8 +digits, starting at 0 with zero-padded numbers. If you specify an output +path ``folder/example.png``, Redot will write ``folder/example00000000.png``, +``folder/example00000001.png``, and so on in that folder. The audio will be saved +at ``folder/example.wav``. + +The FPS is specified using the ``-r`` argument. It should match the FPS +specified during recording. Otherwise, the video will appear to be slowed down +or sped up, and audio will be out of sync with the video. + +``` +ffmpeg -r 60 -i input%08d.png -i input.wav -crf 15 output.mp4 + +``` + +If you recorded a PNG image sequence with transparency enabled, you need to use +a video format that supports storing transparency. MP4/H.264 doesn't support +storing transparency, so you can use WebM/VP9 as an alternative: + +``` +ffmpeg -r 60 -i input%08d.png -i input.wav -c:v libvpx-vp9 -crf 15 -pix_fmt yuva420p output.webm + +``` + +### Cutting video + +You can trim parts of the video you don't want to keep after the video is +recorded. For example, to discard everything before 12.1 seconds and keep +only 5.2 seconds of video after that point: + +``` +ffmpeg -i input.avi -ss 00:00:12.10 -t 00:00:05.20 -crf 15 output.mp4 + +``` + +Cutting videos can also be done with the GUI tool +[LosslessCut ](https://mifi.github.io/lossless-cut/). + +### Resizing video + +The following command resizes a video to be 1080 pixels tall (1080p), +while preserving its existing aspect ratio: + +``` +ffmpeg -i input.avi -vf "scale=-1:1080" -crf 15 output.mp4 + +``` + +### Reducing framerate + +The following command changes a video's framerate to 30 FPS, dropping some of +the original frames if there are more in the input video: + +``` +ffmpeg -i input.avi -r 30 -crf 15 output.mp4 + +``` + +### Generating accumulation motion blur with FFmpeg + +Redot does not have built-in support for motion blur, but it can still be +created in recorded videos. + +If you record the video at a multiple of the original framerate, you can blend +the frames together then reduce the frameate to produce a video with +*accumulation motion blur*. This motion blur can look very good, but it can take +a long time to generate since you have to render many more frames per second (on +top of the time spent on post-processing). + +Example with a 240 FPS source video, generating 4× motion blur and decreasing +its output framerate to 60 FPS: + +``` +ffmpeg -i input.avi -vf "tmix=frames=4, fps=60" -crf 15 output.mp4 + +``` + +This also makes effects that converge over several frames (such as temporal +antialiasing, SDFGI and volumetric fog) converge faster and therefore look +better, since they'll be able to work with more data at a given time. +See [doc_creating_movies_reducing_framerate](doc_creating_movies_reducing_framerate) if you want to get this benefit +without adding motion blur. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/animation/cutout_animation.md b/Redot-Documentation/docs/26.1/Tutorials/animation/cutout_animation.md new file mode 100644 index 0000000..521f93b --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/animation/cutout_animation.md @@ -0,0 +1,366 @@ +:::warning +This page is marked as outdated and may not reflect current Redot behavior. +::: + +# Cutout animation + +### What is it? + +Traditionally, [cutout animation ](https://en.wikipedia.org/wiki/Cutout_animation) +is a type of [stop motion animation ](https://en.wikipedia.org/wiki/Stop_motion) +in which pieces of paper (or other thin material) are cut into special shapes +and arranged in two-dimensional representations of characters and objects. +Characters' bodies are usually made out of several pieces. The pieces are +arranged and photographed once for each frame of the film. The animator moves +and rotates the parts in small increments between each shot to create the +illusion of movement when the images are played back quickly in sequence. + +Simulations of cutout animation can now be created using software as seen in +[South Park ](https://en.wikipedia.org/wiki/South_Park) and `Jake and the Never +Land Pirates . + +In video games, this technique has also become popular. Examples of +this are [Paper Mario ](https://en.wikipedia.org/wiki/Super_Paper_Mario) or +[Rayman Origins ](https://en.wikipedia.org/wiki/Rayman_Origins) . + +### Cutout animation in Redot + +Redot provides tools for working with cutout rigs, and is ideal for the workflow: + +- **The animation system is fully integrated with the engine**: This + means animations can control much more than just motion of objects. Textures, + sprite sizes, pivots, opacity, color modulation, and more, can all be animated + and blended. +- **Combine animation styles**: AnimatedSprite2D allows traditional cel animation + to be used alongside cutout animation. In cel animation different animation + frames use entirely different drawings rather than the same pieces positioned + differently. In an otherwise cutout-based animation, cel animation can be used + selectively for complex parts such as hands, feet, changing facial expressions, + etc. +- **Custom Shaped Elements**: Custom shapes can be created with + [Polygon2D ](class_Polygon2D) + allowing UV animation, deformations, etc. +- **Particle Systems**: A cutout animation rig can be combined with particle + systems. This can be useful for magic effects, jetpacks, etc. +- **Custom Colliders**: Set colliders and influence areas in different + parts of the skeletons, great for bosses and fighting games. +- **Animation Tree**: Allows complex combinations and blending between + several animations, the same way it works in 3D. + +And much more! + +### Making of GBot + +For this tutorial, we will use as demo content the pieces of the +[GBot ](https://www.youtube.com/watch?v=S13FrWuBMx4&list=UUckpus81gNin1aV8WSffRKw) +character, created by Andreas Esau. + +![Image](/img/Tutorials/animation/img/tuto_cutout_walk.gif) + +Get your assets: +[cutout_animation_assets.zip ](https://github.com/redot-engine/redot-docs-site-project-starters/releases/download/latest-4.x/cutout_animation_assets.zip). + +### Setting up the rig + +Create an empty Node2D as root of the scene, we will work under it: + +![Image](/img/Tutorials/animation/img/tuto_cutout1.png) + +The first node of the model is the hip. +Generally, both in 2D and 3D, the hip is the root of the skeleton. This +makes it easier to animate: + +![Image](/img/Tutorials/animation/img/tuto_cutout2.png) + +Next will be the torso. The torso needs to be a child of the hip, so +create a child sprite and load the torso texture, later accommodate it properly: + +![Image](/img/Tutorials/animation/img/tuto_cutout3.png) + +This looks good. Let's see if our hierarchy works as a skeleton by +rotating the torso. We can do this be pressing `E` to enter rotate mode, +and dragging with the left mouse button. To exit rotate mode hit `ESC`. + +![Image](/img/Tutorials/animation/img/tutovec_torso1.gif) + +The rotation pivot is wrong and needs to be adjusted. + +This small cross in the middle of the [Sprite2D ](class_Sprite2D) is +the rotation pivot: + +![Image](/img/Tutorials/animation/img/tuto_cutout4.png) + +### Adjusting the pivot + +The pivot can be adjusted by changing the *offset* property in the +Sprite2D: + +![Image](/img/Tutorials/animation/img/tuto_cutout5.png) + +The pivot can also be adjusted *visually*. While hovering over the +desired pivot point, press `V` to move the pivot there for the +selected Sprite2D. There is also a tool in the tool bar that has a +similar function. + +![Image](/img/Tutorials/animation/img/tutovec_torso2.gif) + +Continue adding body pieces, starting with the +right arm. Make sure to put each sprite in its correct place in the hierarchy, +so its rotations and translations are relative to its parent: + +![Image](/img/Tutorials/animation/img/tuto_cutout6.png) + +With the left arm there's a problem. In 2D, child nodes appear in front of +their parents: + +![Image](/img/Tutorials/animation/img/tuto_cutout7.png) + +We want the left arm to appear *behind* +the hip and the torso. We could move the left arm nodes behind the hip (above +the hip node in the scene hierarchy), but then the left arm is no longer in its +proper place in the hierarchy. This means it wouldn't be affected by the movement +of the torso. We'll fix this problem with ``RemoteTransform2D`` nodes. + +:::note +You can also fix depth ordering problems by adjusting the Z property +of any node inheriting from Node2D. + +::: + +### RemoteTransform2D node + +The [RemoteTransform2D ](class_RemoteTransform2D) node transforms nodes +somewhere else in the hierarchy. This node applies its own transform (including +any transformation it inherits from its parents) to the remote node it targets. + +This allows us to correct the visibility order of our elements, independently of +the locations of those parts in the cutout hierarchy. + +Create a ``RemoteTransform2D`` node as a child of the torso. Call it ``remote_arm_l``. +Create another RemoteTransform2D node inside the first and call it ``remote_hand_l``. +Use the ``Remote Path`` property of the two new nodes to target the ``arm_l`` and +``hand_l`` sprites respectively: + +![Image](/img/Tutorials/animation/img/tuto_cutout9.png) + +Moving the ``RemoteTransform2D`` nodes now moves the sprites. So we can create +animations by adjusting the ``RemoteTransform2D`` transforms: + +![Image](/img/Tutorials/animation/img/tutovec_torso4.gif) + +### Completing the skeleton + +Complete the skeleton by following the same steps for the rest of the +parts. The resulting scene should look similar to this: + +![Image](/img/Tutorials/animation/img/tuto_cutout10.png) + +The resulting rig will be easy to animate. By selecting the nodes and +rotating them you can animate forward kinematics (FK) efficiently. + +For simple objects and rigs this is fine, but there are limitations: + +- Selecting sprites in the main viewport can become difficult in complex rigs. + The scene tree ends up being used to select parts instead, which can be slower. +- Inverse Kinematics (IK) is useful for animating extremities like hands and + feet, and can't be used with our rig in its current state. + +To solve these problems we'll use Redot's skeletons. + +### Skeletons + +In Redot there is a helper to create "bones" between nodes. The bone-linked +nodes are called skeletons. + +As an example, let's turn the right arm into a skeleton. To create +a skeleton, a chain of nodes must be selected from top to bottom: + +![Image](/img/Tutorials/animation/img/tuto_cutout11.png) + +Then, click on the Skeleton menu and select ``Make Bones``. + +![Image](/img/Tutorials/animation/img/tuto_cutout12.png) + +This will add bones covering the arm, but the result may be surprising. + +![Image](/img/Tutorials/animation/img/tuto_cutout13.png) + +Why does the hand lack a bone? In Redot, a bone connects a +node with its parent. And there's currently no child of the hand node. +With this knowledge let's try again. + +The first step is creating an endpoint node. Any kind of node will do, +but [Marker2D ](class_Marker2D) is preferred because it's +visible in the editor. The endpoint node will ensure that the last bone +has orientation. + +![Image](/img/Tutorials/animation/img/tuto_cutout14.png) + +Now select the whole chain, from the endpoint to the arm and create +bones: + +![Image](/img/Tutorials/animation/img/tuto_cutout15.png) + +The result resembles a skeleton a lot more, and now the arm and forearm +can be selected and animated. + +Create endpoints for all important extremities. Generate bones for all +articulable parts of the cutout, with the hip as the ultimate connection +between all of them. + +You may notice that an extra bone is created when connecting the hip and torso. +Redot has connected the hip node to the scene root with a bone, and we don't +want that. To fix this, select the root and hip node, open the Skeleton menu, +click ``clear bones``. + +![Image](/img/Tutorials/animation/img/tuto_cutout15_2.png) + +Your final skeleton should look something like this: + +![Image](/img/Tutorials/animation/img/tuto_cutout16.png) + +You might have noticed a second set of endpoints in the hands. This will make +sense soon. + +Now that the whole figure is rigged, the next step is setting up the IK +chains. IK chains allow for more natural control of extremities. + +### IK chains + +IK stands for inverse kinematics. It's a convenient technique for animating the +position of hands, feet and other extremities of rigs like the one we've made. +Imagine you want to pose a character's foot in a specific position on the ground. +Without IK chains, each motion of the foot would require rotating and positioning +several other bones (the shin and the thigh at least). This would be quite +complex and lead to imprecise results. IK allows us to move the foot directly +while the shin and thigh self-adjust. + +:::note + +**IK chains in Redot currently work in the editor only**, not +at runtime. They are intended to ease the process of setting keyframes, and are +not currently useful for techniques like procedural animation. + +::: + +To create an IK chain, select a chain of bones from endpoint to +the base for the chain. For example, to create an IK chain for the right +leg, select the following: + +![Image](/img/Tutorials/animation/img/tuto_cutout17.png) + +Then enable this chain for IK. Go to Edit > Make IK Chain. + +![Image](/img/Tutorials/animation/img/tuto_cutout18.png) + +As a result, the base of the chain will turn *Yellow*. + +![Image](/img/Tutorials/animation/img/tuto_cutout19.png) + +Once the IK chain is set up, grab any child or grand-child of the base of the +chain (e.g. a foot), and move it. You'll see the rest of the chain adjust as you +adjust its position. + +![Image](/img/Tutorials/animation/img/tutovec_torso5.gif) + +### Animation tips + +The following section will be a collection of tips for creating animation for +your cutout rigs. For more information on how the animation system in Redot +works, see [doc_introduction_animation](introduction.md). + +## Setting keyframes and excluding properties + +Special contextual elements appear in the top toolbar when the animation editor +window is open: + +![Image](/img/Tutorials/animation/img/tuto_cutout20.png) + +The key button inserts location, rotation, and scale keyframes for the +selected objects or bones at the current playhead position. + +The "loc", "rot", and "scl" toggle buttons to the left of the key button modify +its function, allowing you to specify which of the three properties keyframes +will be created for. + +Here's an illustration of how this can be useful: Imagine you have a node which +already has two keyframes animating its scale only. You want to add an +overlapping rotation movement to the same node. The rotation movement should +begin and end at different times from the scale change that's already set up. +You can use the toggle buttons to have only rotation information added when you +add a new keyframe. This way, you can avoid adding unwanted scale keyframes +which would disrupt the existing scale animation. + +### Creating a rest pose + +Think of a rest pose as a default pose that your cutout rig should be set to +when no other pose is active in your game. Create a rest pose as follows: + +1. Make sure the rig parts are positioned in what looks like a "resting" +arrangement. + +2. Create a new animation, rename it "rest". + +3. Select all nodes in your rig (box selection should work fine). + +4. Make sure the "loc", "rot", and "scl" toggle buttons are all active in the +toolbar. + +5. Press the key button. Keys will be inserted for all selected parts storing +their current arrangement. This pose can now be recalled when necessary in +your game by playing the "rest" animation you've created. + +![Image](/img/Tutorials/animation/img/tuto_cutout21.png) + +### Modifying rotation only + +When animating a cutout rig, often it's only the rotation of the nodes that +needs to change. +Location and scale are rarely used. + +So when inserting keys, you might find it convenient to have only the "rot" +toggle active most of the time: + +![Image](/img/Tutorials/animation/img/tuto_cutout22.png) + +This will avoid the creation of unwanted animation tracks for position +and scale. + +### Keyframing IK chains + +When editing IK chains, it's not necessary to select the whole chain to +add keyframes. Selecting the endpoint of the chain and inserting a +keyframe will automatically insert keyframes for all other parts of the chain too. + +### Visually move a sprite behind its parent + +Sometimes it is necessary to have a node change its visual depth relative to +its parent node during an animation. Think of a character facing the camera, +who pulls something out from behind his back and holds it out in front of him. +During this animation the whole arm and the object in his hand would need to +change their visual depth relative to the body of the character. + +To help with this there's a keyframable "Behind Parent" property on all +Node2D-inheriting nodes. When planning your rig, think about the movements it +will need to perform and give some thought to how you'll use "Behind Parent" +and/or RemoteTransform2D nodes. They provide overlapping functionality. + +![Image](/img/Tutorials/animation/img/tuto_cutout23.png) + +### Setting easing curves for multiple keys + +To apply the same easing curve to multiple keyframes at once: + +1. Select the relevant keys. +2. Click on the pencil icon in the bottom right of the animation panel. This + will open the transition editor. +3. In the transition editor, click on the desired curve to apply it. + +![Image](/img/Tutorials/animation/img/tuto_cutout24.png) + +### 2D Skeletal deform + +Skeletal deform can be used to augment a cutout rig, allowing single pieces to +deform organically (e.g. antennae that wobble as an insect character walks). + +This process is described in a [separate tutorial ](2d_skeletons.md). \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/animation/index.md b/Redot-Documentation/docs/26.1/Tutorials/animation/index.md new file mode 100644 index 0000000..db3c29c --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/animation/index.md @@ -0,0 +1,14 @@ +# Animation + +This section contains tutorials and documentation about animation in Redot Engine. + +## Articles + +- [2D skeletons](2d_skeletons) +- [Animation Track types](animation_track_types) +- [Using AnimationTree](animation_tree) +- [Creating movies](creating_movies) +- [Cutout animation](cutout_animation) +- [Introduction to the animation features](introduction) +- [Playing videos](playing_videos) + diff --git a/Redot-Documentation/docs/26.1/Tutorials/animation/introduction.md b/Redot-Documentation/docs/26.1/Tutorials/animation/introduction.md new file mode 100644 index 0000000..bacc915 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/animation/introduction.md @@ -0,0 +1,357 @@ + +# Introduction to the animation features + +The [class_AnimationPlayer](class_AnimationPlayer) node allows you to create anything +from simple to complex animations. + +In this guide you learn to: + +- Work with the Animation Panel +- Animate any property of any node +- Create a simple animation + +In Redot, you can animate anything available in the Inspector, such as +Node transforms, sprites, UI elements, particles, visibility and color +of materials, and so on. You can also modify values of script variables +and even call functions. + +## Create an AnimationPlayer node + +To use the animation tools we first have to create an +[class_AnimationPlayer](class_AnimationPlayer) node. + +The AnimationPlayer node type is the data container for your animations. +One AnimationPlayer node can hold multiple animations, which can +automatically transition to one another. + +![Image](/img/Tutorials/animation/img/animation_create_animationplayer.webp) + + The AnimationPlayer node + +After you create an AnimationPlayer node, click on it to +open the Animation Panel at the bottom of the viewport. + +![Image](/img/Tutorials/animation/img/animation_animation_panel.webp) + + The animation panel position + +The animation panel consists of four parts: + +![Image](/img/Tutorials/animation/img/animation_animation_panel_overview.webp) + + The animation panel + +- Animation controls (i.e. add, load, save, and delete animations) +- The tracks listing +- The timeline with keyframes +- The timeline and track controls, where you can zoom the timeline and + edit tracks, for example. + +## Computer animation relies on keyframes + +A keyframe defines the value of a property at a point in time. + +Diamond shapes represent keyframes in the timeline. A line between two +keyframes indicates that the value doesn't change between them. + +![Image](/img/Tutorials/animation/img/animation_keyframes.webp) + + Keyframes in Redot + +You set values of a node's properties and create animation keyframes for them. +When the animation runs, the engine will interpolate the values between the +keyframes, resulting in them gradually changing over time. + +![Image](/img/Tutorials/animation/img/animation_illustration.webp) + + Two keyframes are all it takes to obtain a smooth motion + +The timeline defines how long the animation will take. You can insert keyframes +at various points, and change their timing. + +![Image](/img/Tutorials/animation/img/animation_timeline.webp) + + The timeline in the animation panel + +Each line in the Animation Panel is an animation track that references a +Normal or Transform property of a node. Each track stores a path to +a node and its affected property. For example, the position track +in the illustration refers to the ``position`` property of the Sprite2D +node. + +![Image](/img/Tutorials/animation/img/animation_normal_track.webp) + + Example of Normal animation tracks + +:::tip + +If you animate the wrong property, you can edit a track's path at any time +by double-clicking on it and typing the new path. Play the animation using the +"Play from beginning" button |Play from beginning| (or pressing +`Shift + D` on keyboard) to see the changes instantly. + +::: + +## Tutorial: Creating a simple animation + +### Scene setup + +For this tutorial, we'll create a Sprite node with an AnimationPlayer as +its child. We will animate the sprite to move between two points on the screen. + +![Image](/img/Tutorials/animation/img/animation_animation_player_tree.webp) + + Our scene setup + +:::warning + +AnimationPlayer inherits from Node instead of Node2D or Node3D, which means +that the child nodes will not inherit the transform from the parent nodes +due to a bare Node being present in the hierarchy. + +Therefore, it is not recommended to add nodes that have a 2D/3D transform +as a child of an AnimationPlayer node. + +::: + +The sprite holds an image texture. For this tutorial, select the Sprite2D node, +click Texture in the Inspector, and then click Load. Select the default Redot +icon for the sprite's texture. + +### Adding an animation + +Select the AnimationPlayer node and click the "Animation" button in the +animation editor. From the list, select "New" (|Add Animation|) to add a new +animation. Enter a name for the animation in the dialog box. + +![Image](/img/Tutorials/animation/img/animation_create_new_animation.webp) + + Add a new animation + +### Managing animation libraries + +For reusability, the animation is registered in a list in the animation library resource. If you add an animation to AnimationPlayer without specifying any particular settings, the animation will be registered in the [Global] animation library that AnimationPlayer has by default. + +![Image](/img/Tutorials/animation/img/animation_library.webp) + + Manage animations + +If there are multiple animation libraries and you try to add an animation, a dialog box will appear with options. + +![Image](/img/Tutorials/animation/img/animation_library_dialog.webp) + + Add a new animation with library option + +### Adding a track + +To add a new track for our sprite, select it and take a look at the +toolbar: + +![Image](/img/Tutorials/animation/img/animation_convenience_buttons.webp) + + Convenience buttons + +These switches and buttons allow you to add keyframes for the selected +node's location, rotation, and scale. Since we are only animating the sprite's +position, make sure that only the location switch is selected. The selected +switches are blue. + +Click on the key button to create the first keyframe. Since we don't have a +track set up for the Position property yet, Redot will offer to +create it for us. Click **Create**. + +Redot will create a new track and insert our first keyframe at the beginning of +the timeline: + +![Image](/img/Tutorials/animation/img/animation_track.webp) + + The sprite track + +### The second keyframe + +We need to set our sprite's end location and how long it will take for it to get there. + +Let's say we want it to take two seconds to move between the points. By +default, the animation is set to last only one second, so change the animation +length to 2 in the controls on the right side of the animation panel's timeline +header. + +![Image](/img/Tutorials/animation/img/animation_set_length.webp) + + Animation length + +Now, move the sprite right, to its final position. You can use the *Move tool* in the +toolbar or set the *Position*'s X value in the *Inspector*. + +Click on the timeline header near the two-second mark in the animation panel +and then click the key button in the toolbar to create the second keyframe. + +### Run the animation + +Click on the "Play from beginning" (|Play from beginning|) button. + +Yay! Our animation runs: + +![Image](/img/Tutorials/animation/img/animation_simple.gif) + + The animation + +### Autoplay on load + +You can make it so an animation plays automatically when the AnimationPlayer nodes +scene starts, or joins another scene. To do this click the "Autoplay on load" +button in the animation editor, it's right next to the edit button. + +![Image](/img/Tutorials/animation/img/autoplay_on_load.webp) + +The icon for it will also appear in front of the name of the animation, so you can +easily identify which one is the autoplay animation. + +### Back and forth + +Redot has an interesting feature that we can use in animations. When Animation +Looping is set but there's no keyframe specified at the end of the animation, +the first keyframe is also the last. + +This means we can extend the animation length to four seconds now, and Redot +will also calculate the frames from the last keyframe to the first, moving +our sprite back and forth. + +![Image](/img/Tutorials/animation/img/animation_loop.webp) + + Animation loop + +You can change this behavior by changing the track's loop mode. This is covered +in the next chapter. + +### Track settings + +Each property track has a settings panel at the end, where you can set its update +mode, track interpolation, and loop mode. + +![Image](/img/Tutorials/animation/img/animation_track_settings.webp) + + Track settings + +The update mode of a track tells Redot when to update the property +values. This can be: + +- **Continuous:** Update the property on each frame +- **Discrete:** Only update the property on keyframes +- **Capture:** if the first keyframe's time is greater than ``0.0``, the + current value of the property will be remembered and + will be blended with the first animation key. For example, you + could use the Capture mode to move a node that's located anywhere + to a specific location. + +![Image](/img/Tutorials/animation/img/animation_track_rate.webp) + + Track mode + +You will usually use "Continuous" mode. The other types are used to +script complex animations. + +Track interpolation tells Redot how to calculate the frame values between +keyframes. These interpolation modes are supported: + +- Nearest: Set the nearest keyframe value +- Linear: Set the value based on a linear function calculation between + the two keyframes +- Cubic: Set the value based on a cubic function calculation between + the two keyframes +- Linear Angle (Only appears in rotation property): Linear mode with shortest path rotation +- Cubic Angle (Only appears in rotation property): Cubic mode with shortest path rotation + +![Image](/img/Tutorials/animation/img/animation_track_interpolation.webp) + + Track interpolation + +With Cubic interpolation, animation is slower at keyframes and faster between +them, which leads to more natural movement. Cubic interpolation is commonly +used for character animation. Linear interpolation animates changes at a fixed +pace, resulting in a more robotic effect. + +Redot supports two loop modes, which affect the animation when it's set to +loop: + +![Image](/img/Tutorials/animation/img/animation_track_loop_modes.webp) + + Loop modes + +- Clamp loop interpolation: When this is selected, the animation stops + after the last keyframe for this track. When the first keyframe is + reached again, the animation will reset to its values. +- Wrap loop interpolation: When this is selected, Redot calculates the + animation after the last keyframe to reach the values of the first + keyframe again. + +## Keyframes for other properties + +Redot's animation system isn't restricted to position, rotation, and scale. +You can animate any property. + +If you select your sprite while the animation panel is visible, Redot will +display a small keyframe button in the *Inspector* for each of the sprite's +properties. Click on one of these buttons to add a track and keyframe to +the current animation. + +![Image](/img/Tutorials/animation/img/animation_properties_keyframe.webp) + + Keyframes for other properties + +## Edit keyframes + +You can click on a keyframe in the animation timeline to display and +edit its value in the *Inspector*. + +![Image](/img/Tutorials/animation/img/animation_keyframe_editor_key.webp) + + Keyframe editor editing a key + +You can also edit the easing value for a keyframe here by clicking and dragging +its easing curve. This tells Redot how to interpolate the animated property when it +reaches this keyframe. + +You can tweak your animations this way until the movement "looks right." + +## Using RESET tracks + +You can set up a special *RESET* animation to contain the "default pose". +This is used to ensure that the default pose is restored when you save +the scene and open it again in the editor. + +For existing tracks, you can add an animation called "RESET" (case-sensitive), +then add tracks for each property that you want to reset. +The only keyframe should be at time 0, and give it the desired default value +for each track. + +If AnimationPlayer's **Reset On Save** property is set to ``true``, +the scene will be saved with the effects of the reset animation applied +(as if it had been seeked to time ``0.0``). +This only affects the saved file – the property tracks in the editor stay +where they were. + +If you want to reset the tracks in the editor, select the AnimationPlayer node, +open the **Animation** bottom panel then choose **Apply Reset** in the +animation editor's **Edit** dropdown menu. + +When using the keyframe icon next to a property in the inspector the editor will +ask you to automatically create a RESET track. + +:::note +RESET tracks are also used as reference values for blending. See also `For better blending <../animation/animation_tree.html#for-better-blending>`__. + +::: + +## Onion Skinning + +Redot's animation editor allows you use onion skinning while creating an +animation. To turn this feature on click on the onion icon in the top right +of the animation editor. Now there will be transparent red copies of what +is being animated in its previous positions in the animation. + +![Image](/img/Tutorials/animation/img/onion_skin.webp) + +The three dots button next to the onion skinning button opens a dropdown +menu that lets you adjust how it works, including the ability to use +onion skinning for future frames. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/animation/playing_videos.md b/Redot-Documentation/docs/26.1/Tutorials/animation/playing_videos.md new file mode 100644 index 0000000..2fbc60e --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/animation/playing_videos.md @@ -0,0 +1,382 @@ + +# Playing videos + +Redot supports video playback with the [class_VideoStreamPlayer](class_VideoStreamPlayer) node. + +## Supported playback formats + +The only supported format in core is **Ogg Theora** (not to be confused with Ogg +Vorbis audio). It's possible for extensions to bring support for additional +formats, but no such extensions exist yet as of July 2022. + +H.264 and H.265 cannot be supported in core Redot, as they are both encumbered +by software patents. AV1 is royalty-free, but it remains slow to decode on the +CPU and hardware decoding support isn't readily available on all GPUs in use +yet. + +WebM was supported in core in Redot 3.x, but support for it was removed in 4.0 +as it was too buggy and difficult to maintain. + +:::note + +You may find videos with an ``.ogg`` or ``.ogx`` extensions, which are generic +extensions for data within an Ogg container. + +Renaming these file extensions to ``.ogv`` *may* allow the videos to be +imported in Redot. However, not all files with ``.ogg`` or ``.ogx`` +extensions are videos - some of them may only contain audio. + +::: + +## Setting up VideoStreamPlayer + +1. Create a VideoStreamPlayer node using the Create New Node dialog. +2. Select the VideoStreamPlayer node in the scene tree dock, go to the inspector + and load an ``.ogv`` file in the Stream property. + + - If you don't have your video in Ogg Theora format yet, jump to + [doc_playing_videos_recommended_theora_encoding_settings](doc_playing_videos_recommended_theora_encoding_settings). + +3. If you want the video to play as soon as the scene is loaded, check + **Autoplay** in the inspector. If not, leave **Autoplay** disabled and call + ``play()`` on the VideoStreamPlayer node in a script to start playback when + desired. + +### Handling resizing and different aspect ratios + +By default in Redot 4.0, the VideoStreamPlayer will automatically be resized to match +the video's resolution. You can make it follow usual [class_Control](class_Control) sizing +by enabling **Expand** on the VideoStreamPlayer node. + +To adjust how the VideoStreamPlayer node resizes depending on window size, +adjust the anchors using the **Layout** menu at the top of the 2D editor +viewport. However, this setup may not be powerful enough to handle all use +cases, such as playing fullscreen videos without distorting the video (but with +empty space on the edges instead). For more control, you can use an +[class_AspectRatioContainer](class_AspectRatioContainer) node, which is designed to handle this kind of +use case: + +Add an AspectRatioContainer node. Make sure it is not a child of any other +container node. Select the AspectRatioContainer node, then set its **Layout** at +the top of the 2D editor to **Full Rect**. Set **Ratio** in the +AspectRatioContainer node to match your video's aspect ratio. You can use math +formulas in the inspector to help yourself. Remember to make one of the operands +a float. Otherwise, the division's result will always be an integer. + +![Image](/img/Tutorials/animation/img/playing_videos_aspect_ratio_container.png) + + This will evaluate to (approximately) 1.777778 + +Once you've configured the AspectRatioContainer, reparent your VideoStreamPlayer +node to be a child of the AspectRatioContainer node. Make sure **Expand** is +enabled on the VideoStreamPlayer. Your video should now scale automatically +to fit the whole screen while avoiding distortion. + +:::info + +See [doc_multiple_resolutions](../rendering/multiple_resolutions.md) for more tips on supporting multiple +aspect ratios in your project. + +::: + +### Displaying a video on a 3D surface + +Using a VideoStreamPlayer node as a child of a [class_SubViewport](class_SubViewport) node, +it's possible to display any 2D node on a 3D surface. For example, this can be +used to display animated billboards when frame-by-frame animation would require +too much memory. + +This can be done with the following steps: + +1. Create a [class_SubViewport](class_SubViewport) node. Set its size to match your video's size + in pixels. +2. Create a VideoStreamPlayer node *as a child of the SubViewport node* and specify + a video path in it. Make sure **Expand** is disabled, and enable **Autoplay** if needed. +3. Create a MeshInstance3D node with a PlaneMesh or QuadMesh resource in its Mesh property. + Resize the mesh to match the video's aspect ratio (otherwise, it will appear distorted). +4. Create a new StandardMaterial3D resource in the **Material Override** property + in the GeometryInstance3D section. +5. Enable **Local To Scene** in the StandardMaterial3D's Resource section (at the bottom). + This is *required* before you can use a ViewportTexture in its Albedo Texture property. +6. In the StandardMaterial3D, set the **Albedo > Texture** property to **New ViewportTexture**. + Edit the new resource by clicking it, then specify the path to the SubViewport node + in the **Viewport Path** property. +7. Enable **Albedo Texture Force sRGB** in the StandardMaterial3D to prevent colors + from being washed out. +8. If the billboard is supposed to emit its own light, + set **Shading Mode** to **Unshaded** to improve rendering performance. + +See [doc_viewports](../rendering/viewports.md) and the +[GUI in 3D demo ](https://github.com/redot-engine/redot-demo-projects/tree/master/viewport/gui_in_3d) +for more information on setting this up. + +### Looping a video + +For looping a video, the **Loop** property can be enabled. This will seamlessly +restart the video when it reaches its end. + +Note that setting the project setting **Video Delay Compensation** to a non-zero +value might cause your loop to not be seamless, because the synchronization of +audio and video takes place at the start of each loop causing occasional missed +frames. Set **Video Delay Compensation** in your project settings to **0** to +avoid frame drop issues. + +## Video decoding conditions and recommended resolutions + +Video decoding is performed on the CPU, as GPUs don't have hardware acceleration +for decoding Theora videos. Modern desktop CPUs can decode Ogg Theora videos at +1440p @ 60 FPS or more, but low-end mobile CPUs will likely struggle with +high-resolution videos. + +To ensure your videos decode smoothly on varied hardware: + +- When developing games for desktop platforms, it's recommended to encode in + 1080p at most (preferably at 30 FPS). Most people are still using 1080p or + lower resolution displays, so encoding higher-resolution videos may not be + worth the increased file size and CPU requirements. +- When developing games for mobile or web platforms, it's recommended to encode + in 720p at most (preferably at 30 FPS or even lower). The visual difference + between 720p and 1080p videos on a mobile device is usually not that + noticeable. + +## Playback limitations + +There are several limitations with the current implementation of video playback in Redot: + +- Seeking a video to a certain point is not supported. +- Changing playback speed is not supported. VideoStreamPlayer also won't follow + [Engine.time_scale](class_Engine_property_time_scale). +- Streaming a video from a URL is not supported. + +## Recommended Theora encoding settings + +A word of advice is to **avoid relying on built-in Ogg Theora exporters** (most of the time). +There are 2 reasons you may want to favor using an external program to encode your video: + +- Some programs such as Blender can render to Ogg Theora. However, the default + quality presets are usually very low by today's standards. You may be able to + increase the quality options in the software you're using, but you may find + the output quality to remain less than ideal (given the increased file size). + This usually means that the software only supports encoding to constant bit + rate (CBR), instead of variable bit rate (VBR). VBR encoding should be + preferred in most scenarios as it provides a better quality to file size + ratio. +- Some other programs can't render to Ogg Theora at all. + +In this case, you can **render the video to an intermediate high-quality format** +(such as a high-bitrate H.264 video) then re-encode it to Ogg Theora. Ideally, +you should use a lossless or uncompressed format as an intermediate format to +maximize the quality of the output Ogg Theora video, but this can require a lot +of disk space. + +[HandBrake ](https://handbrake.fr/) +(GUI) and [FFmpeg ](https://ffmpeg.org/) (CLI) are popular open source tools +for this purpose. FFmpeg has a steeper learning curve, but it's more powerful. + +Here are example FFmpeg commands to convert an MP4 video to Ogg Theora. Since +FFmpeg supports a lot of input formats, you should be able to use the commands +below with almost any input video format (AVI, MOV, WebM, …). + +:::note + +Make sure your copy of FFmpeg is compiled with libtheora and libvorbis support. +You can check this by running ``ffmpeg`` without any arguments, then looking +at the ``configuration:`` line in the command output. + +::: + +### Balancing quality and file size + +The **video quality** level (``-q:v``) must be between ``1`` and ``10``. Quality +``6`` is a good compromise between quality and file size. If encoding at a high +resolution (such as 1440p or 4K), you will probably want to decrease ``-q:v`` to +``5`` to keep file sizes reasonable. Since pixel density is higher on a 1440p or +4K video, lower quality presets at higher resolutions will look as good or +better compared to low-resolution videos. + +The **audio quality** level (``-q:a``) must be between ``-1`` and ``10``. Quality +``6`` provides a good compromise between quality and file size. In contrast to +video quality, increasing audio quality doesn't increase the output file size +nearly as much. Therefore, if you want the cleanest audio possible, you can +increase this to ``9`` to get *perceptually lossless* audio. This is especially +valuable if your input file already uses lossy audio compression. Higher quality +audio does increase the CPU usage of the decoder, so it might lead to audio +dropouts in case of high system load. See +[this page ](https://wiki.hydrogenaud.io/index.php?title=Recommended_Ogg_Vorbis#Recommended_Encoder_Settings) +for a table listing Ogg Vorbis audio quality presets and their respective +variable bitrates. + +### FFmpeg: Convert while preserving original video resolution + +The following command converts the video while keeping its original resolution. +The video and audio's bitrate will be variable to maximize quality while saving +space in parts of the video/audio that don't require a high bitrate (such as +static scenes). + +``` +ffmpeg -i input.mp4 -q:v 6 -q:a 6 output.ogv + +``` + +### FFmpeg: Resize the video then convert it + +The following command resizes a video to be 720 pixels tall (720p), while +preserving its existing aspect ratio. This helps decrease the file size +significantly if the source is recorded at a higher resolution than 720p: + +``` +ffmpeg -i input.mp4 -vf "scale=-1:720" -q:v 6 -q:a 6 output.ogv + +``` + +## Chroma Key Videos + +Chroma key, commonly known as the "green screen" or "blue screen" effect, allows you to remove a specific color from an image or video and replace it with another background. This effect is widely used in video production to composite different elements together seamlessly. + + .. image:: img/chroma_key_video.webp + +We will achieve the chroma key effect by writing a custom shader in GDScript and using a `VideoStreamPlayer` node to display the video content. + +### Scene Setup + +Ensure that the scene contains a `VideoStreamPlayer` node to play the video and a `Control` node to hold the UI elements for controlling the chroma key effect. + + .. image:: img/chroma_key_scene.webp + +### Writing the Custom Shader + +To implement the chroma key effect, follow these steps: + +1. Select the `VideoStreamPlayer` node in the scene and go to its properties. Under `CanvasItem > Material`, create a new shader named "ChromaKeyShader.gdshader." + +2. In the "ChromaKeyShader.gdshader" file, write the custom shader code as shown below: + +```glsl +shader_type canvas_item; + +// Uniform variables for chroma key effect +uniform vec3 chroma_key_color : source_color = vec3(0.0, 1.0, 0.0); +uniform float pickup_range : hint_range(0.0, 1.0) = 0.1; +uniform float fade_amount : hint_range(0.0, 1.0) = 0.1; + +void fragment() { + // Get the color from the texture at the given UV coordinates + vec4 color = texture(TEXTURE, UV); + + // Calculate the distance between the current color and the chroma key color + float distance = length(color.rgb - chroma_key_color); + + // If the distance is within the pickup range, discard the pixel + // the lesser the distance more likely the colors are + if (distance <= pickup_range) { + discard; + } + + // Calculate the fade factor based on the pickup range and fade amount + float fade_factor = smoothstep(pickup_range, pickup_range + fade_amount, distance); + + // Set the output color with the original RGB values and the calculated fade factor + COLOR = vec4(color.rgb, fade_factor); +} + +``` + +The shader uses the distance calculation to identify pixels close to the chroma key color and discards them, +effectively removing the selected color. Pixels that are slightly further away from the chroma key color are +faded based on the fade_factor, blending them smoothly with the surrounding colors. +This process creates the desired chroma key effect, making it appear as if the background has been replaced with +another image or video. + +The code above represents a simple demonstration of the Chroma Key shader, +and users can customize it according to their specific requirements. + +### UI Controls + +To allow users to manipulate the chroma key effect in real-time, we created sliders in the `Control` node. The `Control` node's script contains the following functions: + + + + + +```gdscript +extends Control + +func _on_color_picker_button_color_changed(color): + # Update the "chroma_key_color" shader parameter of the VideoStreamPlayer's material. + $VideoStreamPlayer.material.set("shader_parameter/chroma_key_color", color) + +func _on_h_slider_value_changed(value): + # Update the "pickup_range" shader parameter of the VideoStreamPlayer's material. + $VideoStreamPlayer.material.set("shader_parameter/pickup_range", value) + +func _on_h_slider_2_value_changed(value): + # Update the "fade_amount" shader parameter of the VideoStreamPlayer's material. + $VideoStreamPlayer.material.set("shader_parameter/fade_amount", value) + +``` + + + + + + func _on_video_stream_player_finished(): + # Restart the video playback when it's finished. + $VideoStreamPlayer.play() + + + +```csharp +using Godot; + +public partial class MyControl : Control +{ + private VideoStreamPlayer _videoStreamPlayer; + + public override void _Ready() + { + _videoStreamPlayer = GetNode("VideoStreamPlayer"); + } + + private void OnColorPickerButtonColorChanged(Color color) + { + // Update the "chroma_key_color" shader parameter of the VideoStreamPlayer's material. + _videoStreamPlayer.Material.Set("shader_parameter/chroma_key_color", color); + } + + private void OnHSliderValueChanged(double value) + { + // Update the "pickup_range" shader parameter of the VideoStreamPlayer's material. + _videoStreamPlayer.Material.Set("shader_parameter/pickup_range", value); + } + + private void OnHSlider2ValueChanged(double value) + { + // Update the "fade_amount" shader parameter of the VideoStreamPlayer's material. + _videoStreamPlayer.Material.Set("shader_parameter/fade_amount", value); + } + + private void OnVideoStreamPlayerFinished() + { + // Restart the video playback when it's finished. + _videoStreamPlayer.Play(); + } +} + +``` + + + +also make sure that the range of the sliders are appropriate, our settings are : + + .. image:: img/slider_range.webp + +### Signal Handling + +Connect the appropriate signal from the UI elements to the `Control` node's script. +you created in the `Control` node's script to control the chroma key effect. +These signal handlers will update the shader's uniform variables +in response to user input. + +Save and run the scene to see the chroma key effect in action! With the provided UI controls, +you can now adjust the chroma key color, pickup range, and fade amount in real-time, achieving the desired +chroma key functionality for your video content. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/escn_exporter/index.md b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/escn_exporter/index.md new file mode 100644 index 0000000..053e9ad --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/escn_exporter/index.md @@ -0,0 +1,4 @@ +# Escn exporter + +This section contains tutorials and documentation about escn exporter in Redot Engine. + diff --git a/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/exporting_3d_scenes.md b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/exporting_3d_scenes.md new file mode 100644 index 0000000..1e81a53 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/exporting_3d_scenes.md @@ -0,0 +1,36 @@ + +# Exporting 3D scenes + +## Overview + +In Redot, it is possible to export 3D scenes as a glTF 2.0 file. You can +export as a glTF binary (``.glb`` file) or glTF embedded with textures +(``gltf`` + ``.bin`` + textures). This allows you to create scenes in Redot, +such as a CSG mesh blockout for a level, export it to clean it up in a +program such as Blender, and then bring it back into Redot. + +:::note + +Only Blender 2.83 and newer can import glTF files exported by Redot. + +::: + +To export a scene in the editor go to **Scene > Export As... > glTF 2.0 Scene...** + +![Image](/img/Tutorials/assets_pipeline/img/gltf_godot_export.png) + +## Limitations + +There are several limitations with glTF export. + +* No support for exporting particles since their implementation varies across engines. +* ShaderMaterials cannot be exported. +* No support for exporting 2D scenes. + +:::info + +3D scenes can be saved at runtime using +[runtime file loading and saving ](doc_runtime_file_loading_and_saving_3d_scenes), +including from an exported project. + +::: diff --git a/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/import_process.md b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/import_process.md new file mode 100644 index 0000000..18eb384 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/import_process.md @@ -0,0 +1,145 @@ + +# Import process + +## Importing assets in Redot + +To import assets in Redot, place your assets (image files, scenes, audio +files, fonts, etc) directly in the project folder. There are 2 ways to achieve this: + +- **For any file type:** Copy files manually with your operating system's file manager. +- **For file types that can be imported by Redot:** + Drag-and-drop files from the operating system's file manager to the editor's FileSystem dock. + This only works with *resource* file types (i.e. file types that Redot can import). + +Redot will automatically import these files internally and keep the imported +resources hidden in a ``res://.Redot/imported/`` folder. + +This means that when trying to access imported assets through code, you +need to use the [Resource Loader](class_ResourceLoader) as it will +automatically take into account where the internal files are saved. If you +try and access an imported asset using the [FileAccess ](class_FileAccess) class, +it will work in the editor, but **it will break in the exported project**. + +However, the [Resource Loader](class_ResourceLoader) cannot access +non-imported files. Only the [FileAccess ](class_FileAccess) class can. + +## Changing import parameters + +:::note + +Import parameters are only present in *non-native* Redot resource types. +This means Redot's own scene and resource file formats (``.tscn``, ``.scn``, +``.tres``, ``.res``) don't have import options you can select in the Import +dock. + +::: + +To change the import parameters of an asset in Redot, select the relevant +resource in the FileSystem dock: + +![Image](/img/Tutorials/assets_pipeline/img/import_process_example.webp) + +After adjusting the parameters, click **Reimport**. Be careful: if you select +another file in the FileSystem dock before clicking **Reimport**, changes will +be discarded. After clicking **Reimport**, the chosen parameters will only be +used for this asset and on future reimports. + +Changing the import parameters of several assets at the same time is also +possible. Select all of them together in the FileSystem dock and the +exposed parameters will apply to all of them when reimporting. + +## Reimporting multiple assets + +While working on a project you may find that several assets need to have +the same parameters changed, such as enabling mipmaps, but you only want +those specific parameters changed. To do this, select every asset you want +to reimport in the file system. In the import tab there will now be a +checkbox to the left of every import parameter. + +![Image](/img/Tutorials/assets_pipeline/img/reimport_multiple.png) + +Select the checkbox of the parameters you want to change on your imported +assets, then change the parameters normally. Finally, click the reimport +button and every selected asset will be reimported with only those +parameters changed. + +## Automatic reimport + +When the MD5 checksum of the source asset changes, Redot will perform an +automatic reimport of it, applying the preset configured for that specific +asset. + +## Files generated + +Importing will add an extra ``<asset>.import`` file next to the source file, +containing the import configuration. + +**Make sure to commit these files to your version control system**, as these +files contain important metadata. + +``` +ls +example.png +example.png.import +project.Redot + +``` + +Additionally, extra assets will be present in the hidden +``res://.Redot/imported/`` folder: + +``` +ls .Redot/imported +example.png-218a8f2b3041327d8a5756f3a245f83b.ctex +example.png-218a8f2b3041327d8a5756f3a245f83b.md5 + +``` + +If any of the files present in this folder is erased (or the whole folder), the +asset or assets will be reimported automatically. As such, committing the +``.Redot/`` folder to the version control system is not recommended. While +committing this folder can shorten reimporting time when checking out on another +computer, it requires considerably more space and bandwidth. + +The default version control metadata that can be generated on project creation +will automatically ignore the ``.Redot/`` folder. + +## Changing import resource type + +Some source assets can be imported as different types of resources. For this, +select the relevant type of resource desired then click **Reimport**: + +![Image](/img/Tutorials/assets_pipeline/img/import_process_changing_import_type.webp) + +Select ``Keep File (exported as is)`` as resource type to skip file import, files +with this resource type will be preserved as is during project export. + +Select ``Skip File (not exported)`` as resource type to skip file import and ignore +file during project export. + +## Changing default import parameters + +Different types of projects might require different defaults. Changing the import +options to a predefined set of options can be achieved by using the +**Preset...** Menu. Besides some resource types offering presets, the default +settings can be saved and cleared too: + +![Image](/img/Tutorials/assets_pipeline/img/import_process_change_preset.webp) + +The default import parameters for a given resource type can be changed +project-wide using the **Import Defaults** tab of the Project Settings dialog: + +![Image](/img/Tutorials/assets_pipeline/img/import_process_import_defaults.webp) + +## Further reading + +This workflow takes a little time to get used to, but it enforces a more correct +way to deal with resources. + +There are many types of assets available for import. Continue reading to +understand how to work with all of them: + +- [doc_importing_images](importing_images.md) +- [doc_importing_audio_samples](importing_audio_samples.md) +- [doc_importing_3d_scenes](importing_3d_scenes/index.md) +- [doc_importing_translations](importing_translations.md) \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_3d_scenes/available_formats.md b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_3d_scenes/available_formats.md new file mode 100644 index 0000000..b88b6fc --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_3d_scenes/available_formats.md @@ -0,0 +1,215 @@ + +# Available 3D formats + +When dealing with 3D assets, Redot has a flexible and configurable importer. + +Redot works with *scenes*. This means that the entire scene being worked on in +your favorite 3D modeling software will be transferred as close as possible. + +Redot supports the following 3D *scene file formats*: + +- glTF 2.0 **(recommended)**. Redot has support for both text (``.gltf``) + and binary (``.glb``) formats. +- ``.blend`` (Blender). This works by calling Blender to export to glTF in a + transparent manner (requires Blender to be installed). +- DAE (COLLADA), an older format that is supported. +- OBJ (Wavefront) format + their MTL material files. This is also + supported, but pretty limited given the format's limitations (no support for + pivots, skeletons, animations, UV2, PBR materials, ...). +- FBX, supported via the [ufbx ](https://github.com/ufbx/ufbx) library. The + previous import workflow used [FBX2glTF ](https://github.com/redot-engine/FBX2glTF) + integration. This requires installing an external program that links against the + proprietary FBX SDK, so we recommend using the default ubfx method or other formats + listed above (if suitable for your workflow). + +Copy the scene file together with the textures and mesh data (if separate) to +the project repository, then Redot will do a full import when focusing the +editor window. + +## Exporting glTF 2.0 files from Blender (recommended) + +There are 3 ways to export glTF files from Blender: + +- As a glTF binary file (``.glb``). +- As a glTF text-based file with embedded binary data (``.gltf`` file) +- As a glTF text-based file with separate binary data and textures (``.gltf`` + file + ``.bin`` file + textures). + +glTF binary files (``.glb``) are the smallest of the three options. They include +the mesh and textures set up in Blender. When brought into Redot the textures +are part of the object's material file. + +glTF embedded files (``.gltf``) function the same way as binary files. They +don't provide extra functionality in Redot, and shouldn't be used since they +have a larger file size. + +There are two reasons to use glTF with the textures separate. One is to have the +scene description in a text based format and the binary data in a separate +binary file. This can be useful for version control if you want to review +changes in a text-based format. The second is you need the texture files +separate from the material file. If you don't need either of those, glTF binary +files are fine. + +The glTF import process first loads the glTF file's data into an in-memory +GLTFState class. This data is then used to generate a Redot scene. +When importing files at runtime, this scene can be directly added to the tree. +The export process is the reverse of this, a Redot scene is converted to a +GLTFState class, then the glTF file is generated from that. + +![Image](/img/Tutorials/assets_pipeline/importing_3d_scenes/img/importing_3d_scenes_available_formats_gltf_runtime.webp) + +When importing glTF files in the editor, there are two more steps. +After generating the Redot scene, the ResourceImporterScene class is used to +apply additional import settings, including settings you set through the +Import dock and the Advanced Import Settings dialog. This is then saved as +a Redot scene file, which is what gets used when you run/export your game. + +![Image](/img/Tutorials/assets_pipeline/importing_3d_scenes/img/importing_3d_scenes_available_formats_gltf_editor.webp) + +:::warning + +If your model contains blend shapes (also known as "shape keys" and "morph +targets"), your glTF export setting **Export Deformation Bones Only** needs +to be configured to **Enabled** under the Animation export configurations. + +Exporting non-deforming bones anyway will lead to incorrect shading. + +::: + +:::note + +Blender versions older than 3.2 do not export emissive textures with the +glTF file. If your model uses one and you're using an older version of +Blender, it must be brought in separately. + +By default, Blender has backface culling disabled on materials and will +export materials to match how they render in Blender. This means that +materials in Redot will have their cull mode set to **Disabled**. This can +decrease performance since backfaces will be rendered, even when they are +being culled by other faces. To resolve this, enable **Backface Culling** in +Blender's Materials tab, then export the scene to glTF again. + +::: + +## Importing ``.blend`` files directly within Redot + +:::note + +This functionality requires Blender 3.0 or later. For best results, we +recommend using Blender 3.5 or later, as it includes many fixes to the glTF +exporter. + +It is **strongly** recommended to use an official Blender release downloaded +from blender.org, as opposed to a Linux distribution package or Flatpak. +This avoids any issues related to packaging, such as different library +versions that can cause incompatibilities or sandboxing restrictions. + +::: + +From Redot 4.0 onwards, the editor can directly import ``.blend`` files by +calling [Blender ](https://www.blender.org/)'s glTF export functionality in a +transparent manner. + +This allows you to iterate on your 3D scenes faster, as you can save the scene +in Blender, alt-tab back to Redot then see your changes immediately. When +working with version control, this is also more efficient as you no longer need +to commit a copy of the exported glTF file to version control. + +To use ``.blend`` import, you must install Blender before opening the Redot +editor (if opening a project that already contains ``.blend`` files). If you +keep Blender installed at its default location, Redot should be able to detect +its path automatically. If this isn't the case, configure the path to the +directory containing the Blender executable in the Editor Settings +(**Filesystem > Import > Blender > Blender 3 Path**). + +If you keep ``.blend`` files within your project folder but don't want them to +be imported by Redot, disable **Filesystem > Import > Blender > Enabled** in the +advanced Project Settings. + +The ``.blend`` import process converts to glTF first, so it still uses +Redot's glTF import code. Therefore, the ``.blend`` import process is the same +as the glTF import process, but with an extra step at the beginning. + +![Image](/img/Tutorials/assets_pipeline/importing_3d_scenes/img/importing_3d_scenes_available_formats_blend.webp) + +:::note + +When working in a team, keep in mind using ``.blend`` files in your project +will require *all* team members to have Blender installed. While Blender is +a free download, this may add friction when working on the project. +``.blend`` import is also not available on the Android and web editors, as +these platforms can't call external programs. + +If this is problematic, consider using glTF scenes exported from Blender +instead. + +::: + +## Exporting DAE files from Blender + +Blender has built-in COLLADA support, but it does not work properly for the +needs of game engines and shouldn't be used as-is. However, scenes exported with +the built-in Collada support may still work for simple scenes without animation. + +For complex scenes or scenes that contain animations, Redot provides a +[Blender plugin ](https://github.com/redot-engine/collada-exporter) +that will correctly export COLLADA scenes for use in Redot. This plugin is +not maintained or supported in Redot 4.x, but may still work depending on your +Redot and Blender versions. + +## Importing OBJ files in Redot + +OBJ is one of the simplest 3D formats out there, so Redot should be able to +import most OBJ files successfully. However, OBJ is also a very limited format: +it doesn't support skinning, animation, UV2 or PBR materials. + +There are 2 ways to use OBJ meshes in Redot: + +- Load them directly in a MeshInstance3D node, or any other property that + expects as mesh (such as GPUParticles3D). This is the default mode. +- Change their import mode to **OBJ as Scene** in the Import dock then restart + the editor. This allows you to use the same import options as glTF or Collada + scenes, such as unwrapping UV2 on import (for [doc_using_lightmap_gi](../../3d/global_illumination/using_lightmap_gi.md)). + +:::note + +Blender 3.4 and later can export RGB vertex colors in OBJ files (this is a +nonstandard extension of the OBJ format). Redot is able to import those +vertex colors since Redot 4.0, but they will not be displayed on the +material unless you enable **Vertex Color > Use As Albedo** on the material. + +Vertex colors from OBJ meshes keep their original color space once imported +(sRGB/linear), but their brightness is clamped to 1.0 (they can't be +overbright). + +::: + +## Importing FBX files in Redot + +By default any FBX file added to a Redot project in Redot 4.3 or later will +use the ufbx import method. Any file that was was added to a project in a +previous version, such as 4.2, will continue to be imported via the FBX2glTF +method unless you go into that files import settings, and change the importer +to ``ufbx``. + +If you keep ``.fbx`` files within your project folder but don't want them to +be imported by Redot, disable **Filesystem > Import > FBX > Enabled** in the +advanced Project Settings. + +If you want to setup the FBX2glTF workflow, which is generally not recommend +unless you have a specific reason to use it, you need to download the [FBX2glTF ](https://github.com/redot-engine/FBX2glTF) +executable, then specify the path to that executable in the editor settings under +**Filesystem > Import > FBX > FBX2glTFPath** + +The FBX2glTF import process converts to glTF first, so it still uses +Redot's glTF import code. Therefore, the FBX import process is the same +as the glTF import process, but with an extra step at the beginning. + +![Image](/img/Tutorials/assets_pipeline/importing_3d_scenes/img/importing_3d_scenes_available_formats_fbx.webp) + +:::info + +The full installation process for using FBX2glTF in Redot is described on the +[FBX import page of the Redot website ](https://redotengine.org/fbx-import). + +::: diff --git a/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_3d_scenes/import_configuration.md b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_3d_scenes/import_configuration.md new file mode 100644 index 0000000..da1532d --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_3d_scenes/import_configuration.md @@ -0,0 +1,516 @@ + +# Import configuration + +Redot provides several ways to customize the imported data, such as the +import dock, the advanced import setting dialog, and inherited scenes. +This can be used to make further changes to the imported scene, such +as adjusting meshes, adding physics information, and adding new nodes. +You can also write a script that runs code at the end of the import +process to perform arbitrary customization. + +Note that, when applicable, modifying the original data should be preferred +to configuring the scene after import. This helps minimize the differences +between the 3D modeling application and the imported scene. See the +[doc_importing_3d_scenes_model_export_considerations](model_export_considerations.md) and +[doc_importing_3d_scenes_node_type_customization](node_type_customization.md) articles +for more information. + +## Import workflows + +Since Redot can only save its own scene format (``.tscn``/``.scn``), Redot +cannot save over the original 3D scene file (which uses a different format). +This is also a safer approach as it avoids making accidental changes to the +source file. + +To allow customizing the scene and its materials, Redot's scene importer allows +for different workflows regarding how data is imported. + +![Image](/img/Tutorials/assets_pipeline/importing_3d_scenes/img/importing_3d_scenes_import_dock.webp) + + Import dock after selecting a 3D scene in the FileSystem dock + +This import process is customizable using 3 separate interfaces, depending on your needs: + +- The **Import** dock, after selecting the 3D scene by clicking it once in the + FileSystem dock. +- The **Advanced Import Settings** dialog, which can be accessed by double-clicking + the 3D scene in the FileSystem dock or by clicking the **Advanced…** button in + the Import dock. This allows you to customize per-object options in Redot. +- [Import hints ](node_type_customization.md), which are special + suffixes added to object names in the 3D modeling software. This allows you to + customize per-object options in the 3D modeling software. + +For basic customization, using the Import dock suffices. However, for more +complex operations such as defining material overrides on a per-material basis, +you'll need to use the Advanced Import Settings dialog, import hints, or possibly both. + +### Using the Import dock + +The following options can be adjusted in the Import dock after selecting a 3D +scene in the FileSystem dock: + +- **Root Type:** The node type to use as a root node. Using node types that + inherit from Node3D is recommended. Otherwise, you'll lose the ability to + position the node directly in the 3D editor. +- **Root Name:** The name of the root node in the imported scene. This is + generally not noticeable when instancing the scene in the editor (or + drag-and-dropping from the FileSystem dock), as the root node is renamed to + match the filename in this case. +- **Apply Root Scale:** If enabled, **Root Scale** will be *applied* on the + meshes and animations directly, while keeping the root node's scale to the + default `(1, 1, 1)`. This means that if you add a child node later on within + the imported scene, it won't be scaled. If disabled, **Root Scale** will + multiply the scale of the root node instead. + +**Meshes** + +- **Ensure Tangents:** If checked, generate vertex tangents using + [Mikktspace ](http://www.mikktspace.com/) if the input meshes don't have + tangent data. When possible, it's recommended to let the 3D modeling software + generate tangents on export instead on relying on this option. Tangents are + required for correct display of normal and height maps, along with any + material/shader features that require tangents. If you don't need material + features that require tangents, disabling this can reduce output file size and + speed up importing if the source 3D file doesn't contain tangents. +- **Generate LODs:** If checked, generates lower detail variants of the + mesh which will be displayed in the distance to improve rendering performance. + Not all meshes benefit from LOD, especially if they are never rendered from + far away. Disabling this can reduce output file size and speed up importing. + See [doc_mesh_lod](../../3d/mesh_lod.md) for more information. +- **Create Shadow Meshes:** If checked, enables the generation of + shadow meshes on import. This optimizes shadow rendering without reducing + quality by welding vertices together when possible. This in turn reduces the + memory bandwidth required to render shadows. Shadow mesh generation currently + doesn't support using a lower detail level than the source mesh (but shadow + rendering will make use of LODs when relevant). +- **Light Baking:** Configures the meshes' + [global illumination mode ](class_GeometryInstance3D_property_gi_mode) + in the 3D scene. If set to **Static Lightmaps**, sets the meshes' GI mode to + **Static** and generates UV2 on import for [lightmap baking ](../../3d/global_illumination/using_lightmap_gi.md). +- **Lightmap Texel Size:** Only visible if **Light Baking** is set to **Static + Lightmaps**. Controls the size of each texel on the baked lightmap. A smaller + value results in more precise lightmaps, at the cost of larger lightmap sizes + and longer bake times. + +**Skins** + +- **Use Named Skins:** If checked, use named [Skins ](class_Skin) for animation. + The [class_MeshInstance3D](class_MeshInstance3D) node contains 3 properties of relevance here: a skeleton + NodePath pointing to the Skeleton3D node (usually ``..``), a mesh, and a skin: + + - The [class_Skeleton3D](class_Skeleton3D) node contains a list of bones with names, their pose and rest, + a name and a parent bone. + - The mesh is all of the raw vertex data needed to display a mesh. In terms of the mesh, + it knows how vertices are weight-painted and uses some internal numbering + often imported from 3D modeling software. + - The skin contains the information necessary to bind this mesh onto this Skeleton3D. + For every one of the internal bone IDs chosen by the 3D modeling software, it contains two things. + Firstly, a Matrix known as the Bind Pose Matrix, Inverse Bind Matrix, or IBM for short. + Secondly, the Skin contains each bone's name (if **Use Named Skins** is enabled), + or the bone's index within the Skeleton3D list (if **Use Named Skins** is disabled). + +Together, this information is enough to tell Redot how to use the bone poses in +the Skeleton3D node to render the mesh from each MeshInstance3D. Note that each +MeshInstance3D may share binds, as is common in models exported from Blender, or +each MeshInstance3D may use a separate Skin object, as is common in models +exported from other tools such as Maya. + +**Animation** + +- **Import:** If checked, import animations from the 3D scene. +- **FPS:** The number of frames per second to use for baking animation curves to + a series of points with linear interpolation. It's recommended to configure + this value to match the value you're using as a baseline in your 3D modeling + software. Higher values result in more precise animation with fast movement + changes, at the cost of higher file sizes and memory usage. Thanks to + interpolation, there is usually not much benefit in going above 30 FPS (as the + animation will still appear smooth at higher rendering framerates). +- **Trimming:** Trim the beginning and end of animations if there are no + keyframe changes. This can reduce output file size and memory usage with + certain 3D scenes, depending on the contents of their animation tracks. +- **Remove Immutable Tracks:** Remove animation tracks that only contain default + values. This can reduce output file size and memory usage with certain 3D + scenes, depending on the contents of their animation tracks. + +**Import Script** + +- **Path:** Path to an import script, which can run code *after* + the import process has completed for custom processing. + See [doc_importing_3d_scenes_import_script](doc_importing_3d_scenes_import_script) for more information. + +**glTF** + +- **Embedded Texture Handling:** Controls how textures embedded within glTF + scenes should be handled. **Discard All Textures** will not import any + textures, which is useful if you wish to manually set up materials in Redot + instead. **Extract Textures** extracts textures to external images, resulting + in smaller file sizes and more control over import options. **Embed as Basis + Universal** and **Embed as Uncompressed** keeps the textures embedded in the + imported scene, with and without VRAM compression respectively. + +**FBX** + +- **Importer** Which import method is used. ubfx handles fbx files as fbx files. + FBX2glTF converts FBX files to glTF on import and requires additional setup. + FBX2glTF is not recommended unless you have a specific rason to use it over + ufbx or working with a different file format. +- **Allow Geometry Helper Nodes** enables or disables geometry helper nodes +- **Embedded Texture Handling:** Controls how textures embedded within fbx + scenes should be handled. **Discard All Textures** will not import any + textures, which is useful if you wish to manually set up materials in Redot + instead. **Extract Textures** extracts textures to external images, resulting + in smaller file sizes and more control over import options. **Embed as Basis + Universal** and **Embed as Uncompressed** keeps the textures embedded in the + imported scene, with and without VRAM compression respectively. + +### Using the Advanced Import Settings dialog + +The first tab you'll see is the **Scene** tab. The options available in the +panel on the right are identical to the Import dock, but you have access to a 3D +preview. The 3D preview can be rotated by holding down the left mouse button +then dragging the mouse. Zoom can be adjusted using the mouse wheel. + +![Image](/img/Tutorials/assets_pipeline/importing_3d_scenes/img/importing_3d_scenes_advanced_import_settings_scene.webp) + + Advanced Import Settings dialog (Scene tab). + Credit: [Modern Arm Chair 01 - Poly Haven ](https://polyhaven.com/a/modern_arm_chair_01) + +**Configuring node import options** + +You can select individual nodes that compose the scene while in the **Scene** +tab using the tree view at the left: + +![Image](/img/Tutorials/assets_pipeline/importing_3d_scenes/img/importing_3d_scenes_advanced_import_settings_node.webp) + + Selecting a node in the Advanced Import Settings dialog (Materials tab) + +This exposes several per-node import options: + +- **Skip Import:** If checked, the node will not be present in the final + imported scene. Enabling this disables all other options. +- **Generate > Physics:** If checked, generates a PhysicsBody3D *parent* node + with collision shapes that are *siblings* to the MeshInstance3D node. +- **Generate > NavMesh:** If checked, generates a NavigationRegion3D *child* + node for [navigation ](../../navigation/navigation_introduction_3d.md). **Mesh + NavMesh** + will keep the original mesh visible, while **NavMesh Only** will only import + the navigation mesh (without a visual representation). **NavMesh Only** is + meant to be used when you've manually authored a simplified mesh for navigation. +- **Generate > Occluder:** If checked, generates an OccluderInstance3D *sibling* + node for [occlusion culling ](../../3d/occlusion_culling.md) using the mesh's + geometry as a basis for the occluder's shape. **Mesh + Occluder** will keep + the original mesh visible, while **Occluder Only** will only import the + occluder (without a visual representation). **Occluder Only** is meant to be + used when you've manually authored a simplified mesh for occlusion culling. + +These options are only visible if some of the above options are enabled: + +- **Physics > Body Type:** Only visible if **Generate > Physics** is enabled. + Controls the PhysicsBody3D that should be created. **Static** creates a + StaticBody3D, **Dynamic** creates a RigidBody3D, **Area** creates an Area3D. +- **Physics > Shape Type:** Only visible if **Generate > Physics** is enabled. + **Trimesh** allows for precise per-triangle collision, but it can only be used + with a **Static** body type. Other types are less precise and may require + manual configuration, but can be used with any body type. For static level + geometry, use **Trimesh**. For dynamic geometry, use primitive shapes if + possible for better performance, or use one of the convex decomposition modes + if the shape is large and complex. +- **Decomposition > Advanced:** Only visible if **Physics > Shape Type** is + **Decompose Convex**. If checked, allows adjusting advanced decomposition + options. If disabled, only a preset **Precision** can be adjusted (which is + usually sufficient). +- **Decomposition > Precision:** Only visible if **Physics > Shape Type** is + **Decompose Convex**. Controls the precision to use for convex decomposition. + Higher values result in more detailed collision, at the cost of slower + generation and increased CPU usage during physics simulation. To improve + performance, it's recommended to keep this value as low as possible for your + use cases. +- **Occluder > Simplification Distance:** Only visible if **Generate > + Occluder** is set to **Mesh + Occluder** or **Occluder Only**. Higher values + result in an occluder mesh with fewer vertices (resulting in decreased CPU + utilization), at the cost of more occlusion culling issues (such as false + positives or false negatives). If you run into objects disappearing when they + shouldn't when the camera is near a certain mesh, try decreasing this value. + +**Configuring mesh and material import options** + +In the Advanced Import Settings dialog, there are 2 ways to select individual +meshes or materials: + +- Switch to the **Meshes** or **Materials** tab in the top-left corner of the dialog. +- Stay in the **Scene** tab, but unfold the options on the tree view on the + left. After choosing a mesh or material, this presents the same information as + the **Meshes** and **Materials** tabs, but in a tree view instead of a list. + +If you select a mesh, different options will appear in the panel on the right: + +![Image](/img/Tutorials/assets_pipeline/importing_3d_scenes/img/importing_3d_scenes_advanced_import_settings_meshes.webp) + + Advanced Import Settings dialog (Meshes tab) + +The options are as follows: + +- **Save to File:** Saves the [class_Mesh](class_Mesh) *resource* to an external file + (this isn't a scene file). You generally don't need to use this for placing + the mesh in a 3D scene – instead, you should instance the 3D scene directly. + However, having direct access to the Mesh resource is useful for specific + nodes, such as [class_MeshInstance3D](class_MeshInstance3D), [class_MultiMeshInstance3D](class_MultiMeshInstance3D), + [class_GPUParticles3D](class_GPUParticles3D) or [class_CPUParticles3D](class_CPUParticles3D). + - You will also need to specify an output file path using the option that + appears after enabling **Save to File**. It's recommended to use the ``.res`` + output file extension for smaller file sizes and faster loading speeds, as + ``.tres`` is inefficient for writing large amounts of data. +- **Generate > Shadow Meshes:** Per-mesh override for the **Meshes > Create + Shadow Meshes** scene-wide import option described in + [doc_importing_3d_scenes_using_the_import_dock](doc_importing_3d_scenes_using_the_import_dock). **Default** will use the + scene-wide import option, while **Enable** or **Disable** can forcibly enable + or disable this behavior on a specific mesh. +- **Generate > Lightmap UV:** Per-mesh override for the **Meshes > Light + Baking** scene-wide import option described in + [doc_importing_3d_scenes_using_the_import_dock](doc_importing_3d_scenes_using_the_import_dock). **Default** will use the + scene-wide import option, while **Enable** or **Disable** can forcibly enable + or disable this behavior on a specific mesh. + - Setting this to **Enable** on a scene with the **Static** light baking mode + is equivalent to configuring this mesh to use **Static Lightmaps**. Setting this + to **Disable** on a scene with the **Static Lightmaps** light baking mode is + equivalent to configuring this mesh to use **Static** instead. +- **Generate > LODs:** Per-mesh override for the **Meshes > Generate LODs** + scene-wide import option described in + [doc_importing_3d_scenes_using_the_import_dock](doc_importing_3d_scenes_using_the_import_dock). **Default** will use the + scene-wide import option, while **Enable** or **Disable** can forcibly enable + or disable this behavior on a specific mesh. +- **LODs > Normal Split Angle:** The minimum angle difference between two + vertices required to preserve a geometry edge in mesh LOD generation. If + running into visual issues with LOD generation, decreasing this value may help + (at the cost of less efficient LOD generation). +- **LODs > Normal Merge Angle:** The minimum angle difference between two + vertices required to preserve a geometry edge in mesh LOD generation. If + running into visual issues with LOD generation, decreasing this value may help + (at the cost of less efficient LOD generation). + +If you select a material, only one option will appear in the panel on the right: + +![Image](/img/Tutorials/assets_pipeline/importing_3d_scenes/img/importing_3d_scenes_advanced_import_settings_materials.webp) + + Advanced Import Settings dialog (Materials tab) + +When **Use External** is checked and an output path is specified, this lets you +use an external material instead of the material that is included in the +original 3D scene file; see the section below. + +### Extracting materials to separate files + +While Redot can import materials authored in 3D modeling software, the default +configuration may not be suitable for your needs. For example: + +- You want to configure material features not supported by your 3D application. +- You want to use a different texture filtering mode, as this option is + configured in the material since Redot 4.0 (and not in the image). +- You want to replace one of the materials with an entirely different material, + such as a custom shader. + +To be able to modify the 3D scene's materials in the Redot editor, you need to +use *external* material resources. + +In the top-left corner of the Advanced Import Settings dialog, choose +**Actions… > Extract Materials**: + +![Image](/img/Tutorials/assets_pipeline/importing_3d_scenes/img/importing_3d_scenes_advanced_import_settings_extract_materials.webp) + + Extracting all built-in materials to external resources in the Advanced Import Settings dialog + +After choosing this option, select a folder to extract material ``.tres`` files +to, then confirm the extraction: + +![Image](/img/Tutorials/assets_pipeline/importing_3d_scenes/img/importing_3d_scenes_advanced_import_settings_extract_materials_confirm.webp) + + Confirming material extraction in the Advanced Import Settings subdialog + +:::note + +After extracting materials, the 3D scene will automatically be configured to +use external material references. As a result, you don't need to manually +enable **Use External** on every material to make the external ``.tres`` +material effective. + +::: + +When **Use External** is enabled, remember that the Advanced Import Settings +dialog will keep displaying the mesh's original materials (the ones designed in +the 3D modeling software). This means your customizations to the materials won't +be visible within this dialog. To preview your modified materials, you need to +place the imported 3D scene in another scene using the editor. + +Redot will not overwrite changes made to extracted materials when the source 3D +scene is reimported. However, if the material name is changed in the source 3D +file, the link between the original material and the extracted material will be +lost. As a result, you'll need to use the Advanced Import Settings dialog to +associate the renamed material to the existing extracted material. + +The above can be done in the dialog's **Materials** tab by selecting the +material, enabling **Save to File**, then specifying the save path using the +**Path** option that appears after enabling **Save to File**. + +### Using import scripts for automation + +A special script to process the whole scene after import can be provided. +This is great for post-processing, changing materials, doing funny stuff with +the geometry, and more. + +Create a script that is not attached to any node by right-clicking in the +FileSystem dock and choosing **New > Script…**. In the script editor, write the +following: + +``` +@tool # Needed so it runs in editor. +extends EditorScenePostImport + +# This sample changes all node names. +# Called right after the scene is imported and gets the root node. +func _post_import(scene): + # Change all node names to "modified_[oldnodename]" + iterate(scene) + return scene # Remember to return the imported scene + +# Recursive function that is called on every node +# (for demonstration purposes; EditorScenePostImport only requires a `_post_import(scene)` function). +func iterate(node): + if node != null: + print_rich("Post-import: [b]%s[/b] -> [b]%s[/b]" % [node.name, "modified_" + node.name]) + node.name = "modified_" + node.name + for child in node.get_children(): + iterate(child) + +``` + +The ``_post_import(scene: Node)`` function takes the imported scene as argument +(the parameter is actually the root node of the scene). The scene that will +finally be used **must** be returned (even if the scene can be entirely different). + +To use your script, locate the script in the import tab's "Path" option under the "Import Script" category. + +### Using animation libraries + +As of Redot 4.0, you can choose to import **only** animations from a glTF file and +nothing else. This is used in some asset pipelines to distribute animations +separately from models. For example, this allows you to use one set of +animations for several characters, without having to duplicate animation data in +every character. + +To do so, select the glTF file in the FileSystem dock, then change the import +mode to Animation Library in the Import dock: + +![Image](/img/Tutorials/assets_pipeline/importing_3d_scenes/img/importing_3d_scenes_changing_import_type.webp) + + Changing the import type to Animation Library in the Import dock + +Click **Reimport** and restart the editor when prompted. After restarting, the +glTF file will be imported as an [class_AnimationLibrary](class_AnimationLibrary) instead of a +[class_PackedScene](class_PackedScene). This animation library can then be referenced in an +[class_AnimationPlayer](class_AnimationPlayer) node. + +The import options that are visible after changing the import mode to Animation +Library act the same as when using the Scene import mode. See +[doc_importing_3d_scenes_using_the_import_dock](doc_importing_3d_scenes_using_the_import_dock) for more information. + +### Filter script + +It is possible to specify a filter script in a special syntax to decide which +tracks from which animations should be kept. + +The filter script is executed against each imported animation. The syntax +consists of two types of statements, the first for choosing which animations to +filter, and the second for filtering individual tracks within the matched +animation. All name patterns are performed using a case-insensitive expression +match, with support for ``?`` and ``*`` wildcards (using +[String.matchn() ](class_String_method_matchn) under the hood). + +The script must start with an animation filter statement (as denoted by the line +beginning with an ``@``). For example, if we would like to apply filters to all +imported animations which have a name ending in ``"_Loop"`` + +``` +@+*_Loop + +``` + +Similarly, additional patterns can be added to the same line, separated by +commas. Here is a modified example to additionally *include* all animations with +names that begin with ``"Arm_Left"``, but also *exclude* all animations which +have names ending in ``"Attack"`` + +``` +@+*_Loop, +Arm_Left*, -*Attack + +``` + +Following the animation selection filter statement, we add track filtering +patterns to indicate which animation tracks should be kept or discarded. If no +track filter patterns are specified, then all tracks within the matched +animations will be discarded! + +It's important to note that track filter statements are applied in order for +each track within the animation, this means that one line may include a track, a +later rule can still discard it. Similarly, a track excluded by an early rule +may then be re-included once again by a filter rule further down in the filter +script. + +For example: include all tracks in animations with names ending in ``"_Loop"``, +but discard any tracks affecting a ``"Skeleton"`` which end in ``"Control"``, +unless they have ``"Arm"`` in their name + +``` +@+*_Loop ++* +-Skeleton:*Control ++*Arm* + +``` + +In the above example, tracks like ``"Skeleton:Leg_Control"`` would be discarded, +while tracks such as ``"Skeleton:Head"`` or ``"Skeleton:Arm_Left_Control"`` +would be retained. + +Any track filter lines that do not begin with a ``+`` or ``-`` are ignored. + +### Storage + +By default, animations are saved as built-in. It is possible to save them to a +file instead. This allows adding custom tracks to the animations and keeping +them after a reimport. + +### Optimizer + +When animations are imported, an optimizer is run, which reduces the size of the +animation considerably. In general, this should always be turned on unless you +suspect that an animation might be broken due to it being enabled. + +### Clips + +It is possible to specify multiple animations from a single timeline as clips. +For this to work, the model must have only one animation that is named +``default``. To create clips, change the clip amount to something greater than +zero. You can then name a clip, specify which frames it starts and stops on, and +choose whether the animation loops or not. + +## Scene inheritance + +In many cases, it may be desired to make manual modifications to the imported +scene. By default, this is not possible because if the source 3D asset changes, +Redot will re-import the *whole* scene. + +However, it is possible to make local modifications by using *scene +inheritance*. If you try to open the imported scene using **Scene > Open +Scene…** or **Scene > Quick Open Scene…**, the following dialog will appear: + +![Image](/img/Tutorials/assets_pipeline/importing_3d_scenes/img/importing_3d_scenes_create_inherited_scene_dialog.webp) + + Dialog when opening an imported 3D scene in the editor + +In inherited scenes, the only limitations for modification are: + +- Nodes from the base scene can't be removed, but additional nodes can be added + anywhere. +- Subresources can't be edited. Instead, you need to save them externally as + described above. + +Other than that, everything is allowed. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_3d_scenes/index.md b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_3d_scenes/index.md new file mode 100644 index 0000000..0db5480 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_3d_scenes/index.md @@ -0,0 +1,11 @@ +# Importing 3d scenes + +This section contains tutorials and documentation about importing 3d scenes in Redot Engine. + +## Articles + +- [Available 3D formats](available_formats) +- [Import configuration](import_configuration) +- [Model export considerations](model_export_considerations) +- [Node type customization using name suffixes](node_type_customization) + diff --git a/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_3d_scenes/model_export_considerations.md b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_3d_scenes/model_export_considerations.md new file mode 100644 index 0000000..00e1c6a --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_3d_scenes/model_export_considerations.md @@ -0,0 +1,83 @@ + +# Model export considerations + +Before exporting a 3D model from a 3D modeling application, such as Blender, +there are some considerations that should be taken into account to ensure that +the model follows the conventions and best practices for Redot. + +## 3D asset direction conventions + +Redot uses a right-handed, Y-is-up coordinate system, with the -Z axis as +the camera's forward direction. This is the same as OpenGL. This implies +that +Z is back, +X is right, and -X is left for a camera. + +The convention for 3D assets is to face the opposite direction as the camera, +so that characters and other assets are facing the camera by default. +This convention is extremely common in 3D modeling applications, and is +[codified in glTF as part of the glTF 2.0 specification ](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#coordinate-system-and-units). +This means that for oriented 3D assets (such as characters), +the +Z axis is the direction of the front, so -Z is the rear, ++X is the left side, and -X is the right side for a 3D asset. +In Blender, this means that +Y is rear and -Y is front for an asset. + +When rotating an oriented 3D asset in Redot, use the ``use_model_front`` +option on the ``look_at`` functions, and use the ``Vector3.MODEL_*`` +constants to perform calculations in the oriented asset's local space. + +For assets without an intrinsic front side or forward direction, such as +a game map or terrain, take note of the cardinal directions instead. +The convention in Redot and the vast majority of other applications is +that +X is east and -X is west. Due to Redot's right-handed Y-is-up +coordinate system, this implies that +Z is south and -Z is north. +In Blender, this means that +Y is north and -Y is south. + +## Exporting textures separately + +While textures can be exported with a model in certain file formats, such as glTF 2.0, you can also export them +separately. Redot uses PBR (physically based rendering) for its materials, so if a texturing program can export PBR +textures, they can work in Redot. This includes the [Substance suite ](https://www.adobe.com/creativecloud/3d-ar.html), +[ArmorPaint (open source) ](https://armorpaint.org/), and [Material Maker (open source) ](https://github.com/RodZill4/material-maker). + +:::info + +For more information on Redot's materials, see [doc_standard_material_3d](../../3d/standard_material_3d.md). + +::: + +## Exporting considerations + +Since GPUs can only render triangles, meshes that contain quads or N-gons have +to be *triangulated* before they can be rendered. Redot can triangulate meshes +on import, but results may be unpredictable or incorrect, especially with +N-gons. Regardless of the target application, triangulating *before* exporting +the scene will lead to more consistent results and should be done whenever +possible. + +To avoid issues with incorrect triangulation after importing in Redot, it is +recommended to make the 3D modeling software triangulate objects on its own. In +Blender, this can be done by adding a Triangulate modifier to your objects and +making sure **Apply Modifiers** is checked in the export dialog. Alternatively, +depending on the exporter, you may be able to find and enable a **Triangulate +Faces** option in the export dialog. + +To avoid issues with 3D selection in the editor, it is recommended to apply the +object transform in the 3D modeling software before exporting the scene. + +:::note + +It is important that the mesh is not deformed by bones when exporting. Make sure +that the skeleton is reset to its T-pose or default rest pose before exporting +with your favorite 3D editor. + +::: + +## Lighting considerations + +While it's possible to import lights from a 3D scene using the glTF, ``.blend`` +or Collada formats, it's generally advised to design the scene's lighting in the +Redot editor after importing the scene. + +This allows you to get a more accurate feel for the final result, as different +engines will render lights in a different manner. This also avoids any issues +with lights appearing excessively strong or faint as a result of the import +process. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_3d_scenes/node_type_customization.md b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_3d_scenes/node_type_customization.md new file mode 100644 index 0000000..2ca863a --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_3d_scenes/node_type_customization.md @@ -0,0 +1,128 @@ + +# Node type customization using name suffixes + +Many times, when editing a scene, there are common tasks that need to be done +after exporting: + +- Adding collision detection to objects. +- Setting objects as navigation meshes. +- Deleting nodes that are not used in the game engine (like specific lights used + for modeling). + +To simplify this workflow, Redot offers several suffixes that can be added to +the names of the objects in your 3D modeling software. When imported, Redot +will detect suffixes in object names and will perform actions automatically. + +:::warning + +All the suffixes described below can be used with ``-``, ``$``, and ``_`` and are +**case-insensitive**. + +::: + +## Remove nodes (-noimp) + +Objects that have the ``-noimp`` suffix will be removed at import-time no matter +what their type is. They will not appear in the imported scene. + +This is equivalent to enabling **Skip Import** for a node in the Advanced Import +Settings dialog. + +## Create collisions (-col, -convcol, -colonly, -convcolonly) + +The option ``-col`` will work only for Mesh objects. If it is detected, a child +static collision node will be added, using the same geometry as the mesh. This +will create a triangle mesh collision shape, which is a slow, but accurate +option for collision detection. This option is usually what you want for level +geometry (but see also ``-colonly`` below). + +The option ``-convcol`` will create a [class_ConvexPolygonShape3D](class_ConvexPolygonShape3D) instead of +a [class_ConcavePolygonShape3D](class_ConcavePolygonShape3D). Unlike triangle meshes which can be concave, +a convex shape can only accurately represent a shape that doesn't have any +concave angles (a pyramid is convex, but a hollow box is concave). Due to this, +convex collision shapes are generally not suited for level geometry. When +representing simple enough meshes, convex collision shapes can result in better +performance compared to a triangle collision shape. This option is ideal for +simple or dynamic objects that require mostly-accurate collision detection. + +However, in both cases, the visual geometry may be too complex or not smooth +enough for collisions. This can create physics glitches and slow down the engine +unnecessarily. + +To solve this, the ``-colonly`` modifier exists. It will remove the mesh upon +importing and will create a [class_StaticBody3D](class_StaticBody3D) collision instead. +This helps the visual mesh and actual collision to be separated. + +The option ``-convcolonly`` works in a similar way, but will create a +[class_ConvexPolygonShape3D](class_ConvexPolygonShape3D) instead using convex decomposition. + +With Collada files, the option ``-colonly`` can also be used with Blender's +empty objects. On import, it will create a [class_StaticBody3D](class_StaticBody3D) with a +collision node as a child. The collision node will have one of a number of +predefined shapes, depending on Blender's empty draw type: + +![Image](/img/Tutorials/assets_pipeline/importing_3d_scenes/img/importing_3d_scenes_blender_empty_draw_types.webp) + + Choosing a draw type for an Empty on creation in Blender + +- Single arrow will create a [class_SeparationRayShape3D](class_SeparationRayShape3D). +- Cube will create a [class_BoxShape3D](class_BoxShape3D). +- Image will create a [class_WorldBoundaryShape3D](class_WorldBoundaryShape3D). +- Sphere (and the others not listed) will create a [class_SphereShape3D](class_SphereShape3D). + +When possible, **try to use a few primitive collision shapes** instead of triangle +mesh or convex shapes. Primitive shapes often have the best performance and +reliability. + +:::note + +For better visibility on Blender's editor, you can set the "X-Ray" option +on collision empties and set some distinct color for them by changing +**Edit > Preferences > Themes > 3D Viewport > Empty**. + +If using Blender 2.79 or older, follow these steps instead: +**User Preferences > Themes > 3D View > Empty**. + +::: + +:::info + +See [doc_collision_shapes_3d](../../physics/collision_shapes_3d.md) for a comprehensive overview of collision +shapes. + +::: + +## Create Occluder (-occ, -occonly) + +If a mesh is imported with the ``-occ`` suffix an [class_occluder3D](class_occluder3D) node +will be created based on the geometry of the mesh, it does not replace the mesh. +A mesh node with the ``-occonly`` suffix will be converted to an +[class_occluder3D](class_occluder3D) on import. + +## Create navigation (-navmesh) + +A mesh node with the ``-navmesh`` suffix will be converted to a navigation mesh. +The original Mesh object will be removed at import-time. + +## Create a VehicleBody (-vehicle) + +A mesh node with the ``-vehicle`` suffix will be imported as a child to a +[class_VehicleBody3D](class_VehicleBody3D) node. + +## Create a VehicleWheel (-wheel) + +A mesh node with the ``-wheel`` suffix will be imported as a child to a +[class_VehicleWheel3D](class_VehicleWheel3D) node. + +## Rigid Body (-rigid) + +A mesh node with the ``-rigid`` suffix will be imported as a [class_RigidBody3D](class_RigidBody3D). + +## Animation loop (-loop, -cycle) + +Animation clips in the source 3D file that start or end with the token ``loop`` or ``cycle`` +will be imported as a Redot [class_Animation](class_Animation) with the loop flag set. +**Unlike the other suffixes described above, this does not require a hyphen.** + +In Blender, this requires using the NLA Editor and naming the Action with the ``loop`` or +``cycle`` prefix or suffix. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_audio_samples.md b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_audio_samples.md new file mode 100644 index 0000000..fe4b93f --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_audio_samples.md @@ -0,0 +1,289 @@ + +# Importing audio samples + +## Supported audio formats + +Redot provides 3 options to import your audio data: WAV, Ogg Vorbis and MP3. + +Each format has different advantages: + +- WAV files use raw data or light compression (IMA-ADPCM or QOA). They are + lightweight to play back on the CPU (hundreds of simultaneous voices in this + format are fine). The downside is that they take up a lot of disk space. +- Ogg Vorbis files use a stronger compression that results in much + smaller file size, but require significantly more processing power to + play back. +- MP3 files use better compression than WAV with IMA-ADPCM or QOA, but worse + than Ogg Vorbis. This means that an MP3 file with roughly equal quality to + Ogg Vorbis will be significantly larger. On the bright side, MP3 requires + less CPU usage to play back compared to Ogg Vorbis. + +:::note + +If you've compiled the Redot editor from source with specific modules disabled, +some formats may not be available. + +::: + +Here is a comparative chart representing the file size of 1 second of audio with +each format: + +| Format | 1 second of audio | +| --- | --- | +| WAV 24-bit, 96 kHz, stereo | 576 KB | +| WAV 16-bit, 44 kHz, mono | 88 KB | +| WAV IMA-ADPCM, 44 kHz, mono | 22 KB | +| WAV QOA, 44 kHz, mono | 17 KB | +| MP3 192 Kb/s, stereo | 24 KB | +| Ogg Vorbis 128 Kb/s, stereo | 16 KB | +| Ogg Vorbis 96 Kb/s, stereo | 12 KB | + +Note that the MP3 and Ogg Vorbis figures can vary depending on the encoding +type. The above figures use :abbr:`CBR (Constant Bit Rate)` encoding for +simplicity, but most Ogg Vorbis and MP3 files you can find online are encoded +with :abbr:`VBR (Variable Bit Rate)` encoding which is more efficient. +VBR encoding makes the effective audio file size depend on how "complex" the +source audio is. + +:::tip + +Consider using WAV for short and repetitive sound effects, and Ogg Vorbis for +music, speech, and long sound effects. MP3 is useful for mobile and web projects +where CPU resources are limited, especially when playing multiple compressed +sounds at the same time (such as long ambient sounds). + +::: + +## Importing audio samples + +Several options are available in the Import dock after selecting a WAV file in +the FileSystem dock: + +![Image](/img/Tutorials/assets_pipeline/img/importing_audio_samples_import_options_wav.webp) + + Import options in the Import dock after selecting a WAV file in the FileSystem dock + +The set of options available after selecting an Ogg Vorbis or MP3 file is different: + +![Image](/img/Tutorials/assets_pipeline/img/importing_audio_samples_import_options_mp3.webp) + + Import options in the Import dock after selecting an MP3 file in the + FileSystem dock. Options are identical for Ogg Vorbis files. + +After importing a sound, you can play it back using the AudioStreamPlayer, +AudioStreamPlayer2D or AudioStreamPlayer3D nodes. See [doc_audio_streams](../audio/audio_streams.md) +for more information. + +## Import options (WAV) + +## Force > 8 Bit + +If enabled, forces the imported audio to use 8-bit quantization if the source +file is 16-bit or higher. + +Enabling this is generally not recommended, as 8-bit quantization decreases +audio quality significantly. If you need smaller file sizes, consider using Ogg +Vorbis or MP3 audio instead. + +## Force > Mono + +If enabled, forces the imported audio to be mono if the source file is stereo. +This decreases the file size by 50% by merging the two channels into one. + +## Force > Max Rate + +If set to a value greater than ``0``, forces the audio's sample rate to be +reduced to a value lower than or equal to the value specified here. + +This can decrease file size noticeably on certain sounds, without impacting +quality depending on the actual sound's contents. See +[doc_importing_audio_samples_best_practices](doc_importing_audio_samples_best_practices) for more information. + +## Edit > Trim + +The source audio file may contain long silences at the beginning and/or the end. +These silences are inserted by :abbr:`DAWs (Digital Audio Workstations)` when +saving to a waveform, which increases their size unnecessarily and add latency +to the moment they are played back. + +Enabling **Trim** will automatically trim the beginning and end of the audio if +it's lower than -50 dB *after* normalization (see **Edit > Normalize** below). A +fade-in/fade-out period of 500 samples is also used during trimming to avoid +audible pops. + +## Edit > Normalize + +If enabled, audio volume will be *normalized* so that its peak volume is equal +to 0 dB. When enabled, normalization will make audio sound louder depending on +its original peak volume. + +## Edit > Loop Mode + +Unlike Ogg Vorbis and MP3, WAV files can contain metadata to indicate whether +they're looping (in addition to loop points). By default, Redot will follow this +metadata, but you can choose to apply a specific loop mode: + +- **Disabled:** Don't loop audio, even if metadata indicates the file should be + played back looping. +- **Forward:** Standard audio looping. +- **Ping-Pong:** Play audio forward until it's done playing, then play it + backward and repeat. This is similar to mirrored texture repeat, but for + audio. +- **Backward:** Play audio in reverse and loop back to the end when done playing. + +When choosing one of the **Forward**, **Ping-Pong** or **Backward** loop modes, +loop points can also be defined to make only a specific part of the sound loop. +**Loop Begin** is set in samples after the beginning of the audio file. **Loop +End** is also set in samples after the beginning of the audio file, but will use +the end of the audio file if set to ``-1``. + +:::warning + +In AudioStreamPlayer, the ``finished`` signal won't be emitted for looping +audio when it reaches the end of the audio file, as the audio will keep +playing indefinitely. + +::: + +## Compress > Mode + +Three compression modes can be chosen from for WAV files: **Disabled** (default), +**RAM (Ima-ADPCM)**, or **QOA (Quite OK Audio)**. **RAM (Ima-ADPCM)** reduces +file size and memory usage a little, at the cost of decreasing quality in an +audible manner. **QOA (Quite OK Audio)** reduces file size a bit more than +**RAM (Ima-ADPCM)** and the quality decrease is much less noticeable, at the +cost of higher CPU usage (still much lower than MP3). + +Ogg Vorbis and MP3 don't decrease quality as much and can provide greater file +size reductions, at the cost of higher CPU usage during playback. This higher +CPU usage is usually not a problem (especially with MP3), unless playing dozens +of compressed sounds at the same time on mobile/web platforms. + +## Import options (Ogg Vorbis and MP3) + +### Loop + +If enabled, the audio will begin playing at the beginning after playback ends by +reaching the end of the audio. + +:::warning + +In AudioStreamPlayer, the ``finished`` signal won't be emitted for looping +audio when it reaches the end of the audio file, as the audio will keep +playing indefinitely. + +::: + +### Loop Offset + +The loop offset determines where audio will start to loop after playback reaches +the end of the audio. This can be used to only loop a part of the audio file, +which is useful for some ambient sounds or music. The value is determined in +seconds relative to the beginning of the audio, so ``0`` will loop the entire +audio file. + +Only has an effect if **Loop** is enabled. + +A more convenient editor for **Loop Offset** is provided in the +[Advanced import settings ](doc_importing_audio_samples_advanced_import_settings) +dialog, as it lets you preview your changes without having to reimport the audio. + +### BPM + +The Beats Per Minute of the audio track. This should match the BPM measure that +was used to compose the track. This is only relevant for music that wishes to +make use of interactive music functionality, not sound +effects. + +A more convenient editor for **BPM** is provided in the +[Advanced import settings ](doc_importing_audio_samples_advanced_import_settings) +dialog, as it lets you preview your changes without having to reimport the audio. + +### Beat Count + +The beat count of the audio track. This is only relevant for music that wishes +to make use of interactive music functionality, not sound +effects. + +A more convenient editor for **Beat Count** is provided in the +[Advanced import settings ](doc_importing_audio_samples_advanced_import_settings) +dialog, as it lets you preview your changes without having to reimport the audio. + +### Bar Beats + +The number of bars within a single beat in the audio track. This is only +relevant for music that wishes to make use of interactive music functionality +, not sound effects. + +A more convenient editor for **Bar Beats** is provided in the +[Advanced import settings ](doc_importing_audio_samples_advanced_import_settings) +dialog, as it lets you preview your changes without having to reimport the audio. + +## Advanced import settings (Ogg Vorbis and MP3) + +If you double-click an Ogg Vorbis or MP3 file in the FileSystem dock (or choose +**Advanced…** in the Import dock), you will see a dialog appear: + +![Image](/img/Tutorials/assets_pipeline/img/importing_audio_samples_advanced_import_settings.webp) + + Advanced dialog when double-clicking an Ogg Vorbis or MP3 file in the FileSystem dock + +This dialog allows you to edit the audio's loop point with a real-time preview, +in addition to the :abbr:`BPM (Beats Per Minute)`, beat count and bar beats. +These 3 settings are currently unused, but they will be used in the future for +interactive music support (which allows smoothly transitioning between different +music tracks). + +:::note + +Unlike WAV files, Ogg Vorbis and MP3 only support a "loop begin" loop point, +not a "loop end" point. Looping can also be only be standard forward +looping, not ping-pong or backward. + +::: + +## Best practices + +### Use appropriate quality settings + +While keeping pristine-quality audio sources is important if you're performing +editing, using the same quality in the exported project is not necessary. For +WAV files, Redot offers several import options to reduce the final file size +without modifying the source file on disk. + +To reduce memory usage and file size, choose an appropriate quantization, +sample rate and number of channels for your audio: + +- There's no *audible* benefit to using 24-bit audio, especially in a game + where several sounds are often playing at the same time (which makes it + harder to appreciate individual sounds). +- Unless you are slowing down the audio at runtime, there's no *audible* + benefit to using a sample rate greater than 48 kHz. If you wish to keep a + source with a higher sample rate for editing, use the **Force > Max Rate** + import option to limit the sample rate of the imported sound (only available + for WAV files). +- Many sound effects can generally be converted to mono as opposed to stereo. + If you wish to keep a source with stereo for editing, use the **Force > Mono** + import option to convert the imported sound to mono (only available for WAV files). +- Voices can generally be converted to mono, but can also have their sample rate + reduced to 22 kHz without a noticeable loss in quality (unless the voice is + very high-pitched). This is because most human voices never go past 11 kHz. + +### Use real-time audio effects to reduce file size + +Redot has an [extensive bus system ](../audio/audio_buses.md) with built-in effects. +This saves SFX artists the need to add reverb to the sound effects, +reducing their size greatly and ensuring correct trimming. + +![Image](/img/Tutorials/assets_pipeline/img/reverb.png) + +As you can see above, sound effects become much larger in file size with reverb +added. + +:::info + +Audio samples can be loaded and saved at runtime using +[runtime file loading and saving ](doc_runtime_file_loading_and_saving_audio_video_files), +including from an exported project. + +::: diff --git a/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_images.md b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_images.md new file mode 100644 index 0000000..09acfcc --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_images.md @@ -0,0 +1,564 @@ + +# Importing images + +## Supported image formats + +Redot can import the following image formats: + +- BMP (``.bmp``) + - No support for 16-bit per pixel images. Only 1-bit, 4-bit, 8-bit, 24-bit, and 32-bit per pixel images are supported. +- DirectDraw Surface (``.dds``) + - If mipmaps are present in the texture, they will be loaded directly. + This can be used to achieve effects using custom mipmaps. +- Khronos Texture (``.ktx``) + - Decoding is done using [libktx ](https://github.com/KhronosGroup/KTX-Software). + Only supports 2D images. Cubemaps, texture arrays and de-padding are not supported. +- OpenEXR (``.exr``) + - Supports HDR (highly recommended for panorama skies). +- Radiance HDR (``.hdr``) + - Supports HDR (highly recommended for panorama skies). +- JPEG (``.jpg``, ``.jpeg``) + - Doesn't support transparency per the format's limitations. +- PNG (``.png``) + - Precision is limited to 8 bits per channel upon importing (no HDR images). +- Truevision Targa (``.tga``) +- SVG (``.svg``) + - SVGs are rasterized using [ThorVG ](https://www.thorvg.org/) + when importing them. [Support is limited ](https://www.thorvg.org/about#:~:text=among%20the%20svg%20tiny%20specs%2C%20yet%20unsupported%20features%20in%20the%20thorvg%20are%20the%20following); + complex vectors may not render correctly. [Text must be converted to paths ](doc_importing_images_svg_text); + otherwise, it won't appear in the rasterized image. + You can check whether ThorVG can render a certain vector correctly using its + [web-based viewer ](https://www.thorvg.org/viewer). + For complex vectors, rendering them to PNGs using [Inkscape ](https://inkscape.org/) + is often a better solution. This can be automated thanks to its + [command-line interface ](https://wiki.inkscape.org/wiki/index.php/Using_the_Command_Line#Export_files). +- WebP (``.webp``) + - WebP files support transparency and can be compressed lossily or losslessly. + The precision is limited to 8 bits per channel. + +:::note + +If you've compiled the Redot editor from source with specific modules disabled, +some formats may not be available. + +::: + +## Importing textures + +The default action in Redot is to import images as textures. Textures are stored +in video memory. Their pixel data can't be accessed directly from the CPU +without converting them back to an [class_Image](class_Image) in a script. This is what +makes drawing them efficient. + +There are over a dozen import options that can be adjusted after selecting an +image in the FileSystem dock: + +![Image](/img/Tutorials/assets_pipeline/img/importing_images_import_dock.webp) + + Import options in the Import dock after selecting an image in the FileSystem dock. + Some of these options are only visible with certain compression modes. + +### Changing import type + +It is possible to choose other types of imported resources in the Import dock: + +- **BitMap:** 1-bit monochrome texture (intended to be used as a click mask in + [class_TextureButton](class_TextureButton) and [class_TouchScreenButton](class_TouchScreenButton)). This resource + type cannot be displayed directly onto 2D or 3D nodes, but the pixel values + can be queried from a script using [get_bit ](class_BitMap_method_get_bit). +- **Cubemap:** Import the texture as a 6-sided cubemap, with interpolation + between the cubemap's sides (seamless cubemaps), which can be sampled in + custom shaders. +- **CubemapArray:** Import the texture as a collection of 6-sided cubemaps, + which can be sampled in custom shaders. This resource type can only be + displayed when using the Forward+ or Mobile renderers, not the Compatibility + renderer. +- **Font Data (Monospace Image Font):** Import the image as a bitmap font where + all characters have the same width. See [doc_gui_using_fonts](../ui/gui_using_fonts.md). +- **Image:** Import the image as-is. This resource type cannot be displayed + directly onto 2D or 3D nodes, but the pixel values can be queried from a + script using [get_pixel](class_Image_method_get_pixel). +- **Texture2D:** Import the image as a 2-dimensional texture, suited for display + on 2D and 3D surfaces. This is the default import mode. +- **Texture2DArray:** Import the image as a collection of 2-dimensional textures. + Texture2DArray is similar to a 3-dimensional texture, but without + interpolation between layers. Built-in 2D and 3D shaders cannot display + texture arrays, so you must create a custom shader in [2D ](../shaders/shader_reference/canvas_item_shader.md) + or [3D ](../shaders/shader_reference/spatial_shader.md) to display a texture from a texture array. +- **Texture3D:** Import the image as a 3-dimensional texture. This is *not* a 2D + texture applied onto a 3D surface. Texture3D is similar to a texture array, but + with interpolation between layers. Texture3D is typically used for + [class_FogMaterial](class_FogMaterial) density maps in [volumetric fog ](../3d/volumetric_fog.md), [particle attractor ](../3d/particles/attractors.md) + vector fields, [class_Environment](class_Environment) 3D LUT color correction, and custom shaders. +- **TextureAtlas:** Import the image as an *atlas* of different textures. Can be + used to reduce memory usage for animated 2D sprites. Only supported in 2D due + to missing support in built-in 3D shaders. + +For **Cubemap**, the expected image order is X+, X-, Y+, Y-, Z+, Z- +(in Redot's coordinate system, so Y+ is "up" and Z- is "forward"). +Here are templates you can use for cubemap images (right-click > **Save Link As…**): + +- :download:`2×3 cubemap template (default layout option) <img/cubemap_template_2x3.webp>` +- :download:`3×2 cubemap template <img/cubemap_template_3x2.webp>` +- :download:`1×6 cubemap template <img/cubemap_template_1x6.webp>` +- :download:`6×1 cubemap template <img/cubemap_template_6x1.webp>` + +### Detect 3D + +The default import options (no mipmaps and **Lossless** compression) are suited +for 2D, but are not ideal for most 3D projects. **Detect 3D** makes Redot aware +of when a texture is used in a 3D scene (such as a texture in a +[class_BaseMaterial3D](class_BaseMaterial3D)). If this happens, several import options are +changed so the texture flags are friendlier to 3D. Mipmaps are enabled and the +compression mode is changed to **VRAM Compressed** unless +[doc_importing_images_detect_3d_compress_to](doc_importing_images_detect_3d_compress_to) is changed. The texture is +also reimported automatically. + +A message is printed to the Output panel when a texture is detected to be used in 3D. + +If you run into quality issues when a texture is detected to be used in 3D (e.g. +for pixel art textures), change the +[doc_importing_images_detect_3d_compress_to](doc_importing_images_detect_3d_compress_to) option before using the +texture in 3D, or change [doc_importing_images_compress_mode](doc_importing_images_compress_mode) to +**Lossless** after using the texture in 3D. This is preferable to disabling +**Detect 3D**, as mipmap generation remains enabled to prevent textures from +looking grainy at a distance. + +## Import options + +:::info + +In Redot 4.0, changing the texture filter and repeat mode is no longer done +in the import options. + +Instead, texture filter and repeat modes are changed in the CanvasItem +properties in 2D (with a project setting acting as a default), and in a +[per-material configuration in 3D ](doc_standard_material_3d_sampling). +In custom shaders, filter and repeat mode is changed on the ``sampler2D`` +uniform using hints described in the [doc_shading_language](../shaders/shader_reference/shading_language.md) +documentation. + +::: + +### Compress > Mode + +Images are one of the largest assets in a game. To handle them efficiently, they +need to be compressed. Redot offers several compression methods, depending on +the use case. + +- **Lossless:** This is the default and most common compression mode for 2D assets. + It shows assets without any kind of artifacting, and disk compression is + decent. It will use considerably more amount of video memory than + VRAM Compression, though. This is also the recommended setting for pixel art. +- **Lossy:** This is a good choice for large 2D assets. It has some artifacts, + but less than VRAM compression and the file size is several times lower + compared to Lossless or VRAM Uncompressed. Video memory usage isn't decreased + by this mode; it's the same as with Lossless or VRAM Uncompressed. +- **VRAM Compressed:** This is the default and most common compression mode for + 3D assets. Size on disk is reduced and video memory usage is also decreased + considerably (usually by a factor between 4 and 6). This mode should be + avoided for 2D as it exhibits noticeable artifacts, especially for + lower-resolution textures. +- **VRAM Uncompressed:** Only useful for formats that can't be compressed, such + as raw floating-point images. +- **Basis Universal:** This alternative VRAM compression mode encodes the + texture to a format that can be transcoded to most GPU-compressed formats at + load-time. This provides very small files that make use of VRAM compression, + at the cost of lower quality compared to VRAM Compressed and slow compression + times. VRAM usage is usually the same as VRAM Compressed. Basis Universal does + not support floating-point image formats (the engine will internally fall back + to VRAM Compressed instead). + +:::note + +Even in 3D, "pixel art" textures should have VRAM compression disabled as it +will negatively affect their appearance, without improving performance +significantly due to their low resolution. + +::: + +In this table, each of the 5 options are described together with their +advantages and disadvantages (|good| = best, |bad| = worst): + +| Compress mode | Lossless | Lossy | VRAM Compressed | VRAM Uncompressed | Basis Universal | +| --- | --- | --- | --- | --- | --- | +| **Description** | Stored as Lossless WebP / PNG | Stored as Lossy WebP | Stored as S3TC, BPTC or ETC2 depending on platform | Stored as raw pixels | Transcoded to VRAM Compressed format | +| **Size on disk** | | regular | Small | | good | Very small | | regular | Small | | bad | Large | | good | Very small | +| **Memory usage** | | bad | Large | | bad | Large | | good | Small | | bad | Large | | good | Small | +| **Performance** | | regular | Normal | | regular | Normal | | good | Fast | | regular | Normal | | good | Fast | +| **Quality loss** | | good | None | | regular | Slight | | bad | Moderate | | good | None | | bad | Moderate | +| **Load time** | | bad | Slow | | bad | Slow | | good | Fast | | regular | Normal | | regular | Normal | + +Estimated memory usage for a single RGBA8 texture with mipmaps enabled: + +| Texture size | Lossless | Lossy | VRAM Compressed | VRAM Uncompressed | Basis Universal | +| --- | --- | --- | --- | --- | --- | +| **128×128** | | good | 85 KiB | | good | 85 KiB | | good | 21 KiB | | good | 85 KiB | | good | 21 KiB | +| **256×256** | | good | 341 KiB | | good | 341 KiB | | good | 85 KiB | | good | 341 KiB | | good | 85 KiB | +| **512×512** | | good | 1.33 MiB | | good | 1.33 MiB | | good | 341 KiB | | good | 1.33 MiB | | good | 341 KiB | +| **1024×1024** | | regular | 5.33 MiB | | regular | 5.33 MiB | | good | 1.33 MiB | | regular | 5.33 MiB | | good | 1.33 MiB | +| **2048×2048** | | bad | 21.33 MiB | | bad | 21.33 MiB | | regular | 5.33 MiB | | bad | 21.33 MiB | | regular | 5.33 MiB | +| **4096×4096** | | bad | 85.33 MiB | | bad | 85.33 MiB | | bad | 21.33 MiB | | bad | 85.33 MiB | | bad | 21.33 MiB | + +:::note + +In the above table, memory usage will be reduced by 25% for images that do +not have an alpha channel (RGB8). Memory usage will be further decreased by +25% for images that have mipmaps disabled. + +::: + +Notice how at larger resolutions, the impact of VRAM compression is much +greater. With a 4:1 compression ratio (6:1 for opaque textures with S3TC), VRAM +compression effectively allows a texture to be twice as large on each axis, +while using the same amount of memory on the GPU. + +VRAM compression also reduces the memory bandwidth required to sample the +texture, which can speed up rendering in memory bandwidth-constrained scenarios +(which are frequent on integrated graphics and mobile). These factors combined +make VRAM compression a must-have for 3D games with high-resolution textures. + +You can preview how much memory a texture takes by double-clicking it in the +FileSystem dock, then looking at the Inspector: + +![Image](/img/Tutorials/assets_pipeline/img/importing_images_inspector_preview.webp) + + Previewing a texture in the Inspector. Credit: [Red Brick 03 - Poly Haven ](https://polyhaven.com/a/red_brick_03) + +### Compress > High Quality + +:::note + +High-quality VRAM texture compression is only supported in the Forward+ and +Mobile renderers. + +When using the Compatibility renderer, this option is always considered +disabled. + +::: + +If enabled, uses BPTC compression on desktop platforms and :abbr:`ASTC (Adaptive +Scalable Texture Compression)` compression on mobile platforms. When using BPTC, +BC7 is used for SDR textures and BC6H is used for HDR textures. + +If disabled (default), uses the faster but lower-quality S3TC compression on +desktop platforms and ETC2 on mobile/web platforms. When using S3TC, DXT1 (BC1) +is used for opaque textures and DXT5 (BC3) is used for transparent or normal map +(:abbr:`RGTC (Red-Green Texture Compression)`) textures. + +BPTC and ASTC support VRAM compression for HDR textures, but S3TC and ETC2 do +not (see **HDR Compression** below). + +### Compress > HDR Compression + +:::note + +This option only has an effect on textures that are imported as HDR formats in Redot +(``.hdr`` and ``.exr`` files). + +::: + +If set to **Disabled**, never uses VRAM compression for HDR textures, regardless +of whether they're opaque or transparent. Instead, the texture is converted to +RGBE9995 (9-bits per channel + 5-bit exponent = 32 bits per pixel) to reduce +memory usage compared to a half-float or single-precision float image format. + +If set to **Opaque Only** (default), only uses VRAM compression for opaque HDR +textures. This is due to a limitation of HDR formats, as there is no +VRAM-compressed HDR format that supports transparency at the same time. + +If set to **Always**, will force VRAM compression even for HDR textures with an +alpha channel. To perform this, the alpha channel is discarded on import. + +### Compress > Normal Map + +When using a texture as normal map, only the red and green channels are +required. Given regular texture compression algorithms produce artifacts that +don't look that nice in normal maps, the :abbr:`RGTC (Red-Green Texture Compression)` +compression format is the best fit for this data. Forcing this option to **Enable** +will make Redot import the image as :abbr:`RGTC (Red-Green Texture Compression)` compressed. +By default, it's set to **Detect**. This means that if the texture is ever detected to +be used as a normal map, it will be changed to **Enable** and reimported automatically. + +Note that :abbr:`RGTC (Red-Green Texture Compression)` compression affects the +resulting normal map image. You will have to adjust custom shaders that use the +normal map's blue channel to take this into account. Built-in material shaders +already ignore the blue channel in a normal map (regardless of the actual normal +map's contents). + +In the example below, the normal map with :abbr:`RGTC (Red-Green Texture Compression)` +compression is able to preserve its detail much better, while +using the same amount of memory as a standard RGBA VRAM-compressed texture: + +![Image](/img/Tutorials/assets_pipeline/img/importing_images_normal_map_rgtc.webp) + + Normal map with standard VRAM compression (left) and with RGTC VRAM compression (right) + +:::note + +Redot requires the normal map to use the X+, Y+ and Z+ coordinates, which is +known as an OpenGL-style normal map. If you've imported a material made to be +used with another engine, it may be DirectX-style. In this case, the normal map +needs to be converted by enabling the **Normal Map Invert Y** import option. + +More information about normal maps (including a coordinate order table for +popular engines) can be found +[here ](http://wiki.polycount.com/wiki/Normal_Map_Technical_Details). + +::: + +### Compress > Channel Pack + +If set to **sRGB Friendly** (default), prevents the RG color format from being +used as it does not support sRGB color. + +If set to **Optimized**, allows the RG color format to be used if the texture +does not use the blue channel. + +A third option **Normal Map (RG Channels)** is *only* available in layered +textures ([class_Cubemap](class_Cubemap), [class_CubemapArray](class_CubemapArray), [class_Texture2DArray](class_Texture2DArray) +and [class_Texture3D](class_Texture3D)). This forces all layers from the texture to be imported +with the RG color format to reduce memory usage, with only the red and green +channels preserved. This only has an effect on textures with the **VRAM Compressed** +or **Basis Universal** compression modes. + +### Mipmaps > Generate + +If enabled, smaller versions of the texture are generated on import. For +example, a 64×64 texture will generate 6 mipmaps (32×32, 16×16, 8×8, 4×4, 2×2, +1×1). This has several benefits: + +- Textures will not become grainy in the distance (in 3D), or if scaled down due + to camera zoom or CanvasItem scale (in 2D). +- Performance will improve if the texture is displayed in the distance, since + sampling smaller versions of the original texture is faster and requires less + memory bandwidth. + +The downside of mipmaps is that they increase memory usage by roughly 33%. + +It's recommended to enable mipmaps in 3D. However, in 2D, this should only be +enabled if your project visibly benefits from having mipmaps enabled. If the +camera never zooms out significantly, there won't be a benefit to enabling +mipmaps but memory usage will increase. + +### Mipmaps > Limit + +:::warning + +**Mipmaps > Limit** is currently not implemented and has no effect when changed. + +::: + +If set to a value greater than ``-1``, limits the maximum number of mipmaps that +can be generated. This can be decreased if you don't want textures to become too +low-resolution at extreme distances, at the cost of some graininess. + +### Roughness > Mode + +The color channel to consider as a roughness map in this texture. Only effective if +**Roughness > Src Normal** is not empty. + +### Roughness > Src Normal + +The path to the texture to consider as a normal map for roughness filtering on +import. Specifying this can help decrease specular aliasing slightly in 3D. + +Roughness filtering on import is only used in 3D rendering, not 2D. + +### Process > Fix Alpha Border + +This puts pixels of the same surrounding color in transition from transparent to +opaque areas. For textures displayed with bilinear filtering, this helps +mitigate the outline effect when exporting images from an image editor. + +![Image](/img/Tutorials/assets_pipeline/img/fixedborder.png) + +It's recommended to leave this enabled (as it is by default), unless this causes +issues for a particular image. + +### Process > Premult Alpha + +An alternative to fixing darkened borders with **Fix Alpha Border** is to use +premultiplied alpha. By enabling this option, the texture will be converted to +this format. A premultiplied alpha texture requires specific materials to be +displayed correctly: + +- In 2D, a [class_CanvasItemMaterial](class_CanvasItemMaterial) will need to be created and + configured to use the **Premul Alpha** blend mode on CanvasItems that use this + texture. +- In 3D, there is no support for premultiplied alpha blend mode yet, so this + option is only suited for 2D. + +### Process > Normal Map Invert Y + +Redot requires the normal map to use the X+, Y+ and Z+ coordinates, which is +known as an OpenGL-style normal map. If you've imported a material made to be +used with another engine, it may be DirectX-style. In this case, the normal map +needs to be converted by enabling the **Normal Map Invert Y** import option. + +More information about normal maps (including a coordinate order table for +popular engines) can be found +[here ](http://wiki.polycount.com/wiki/Normal_Map_Technical_Details). + +### Process > HDR as sRGB + +Some HDR images you can find online may be broken and contain sRGB color data +(instead of linear color data). It is advised not to use those files. If you +absolutely have to, enabling this option on will make them look correct. + +:::warning + +Enabling **HDR as sRGB** on well-formatted HDR images will cause the +resulting image to look too dark, so leave this disabled if unsure. + +::: + +### Process > HDR Clamp Exposure + +Some HDR panorama images you can find online may contain extremely bright +pixels, due to being taken from real life sources without any clipping. + +While these HDR panorama images are accurate to real life, this can cause the +radiance map generated by Redot to contain sparkles when used as a background +sky. This can be seen in material reflections (even on rough materials in +extreme cases). Enabling **HDR Clamp Exposure** can resolve this using a smart +clamping formula that does not introduce *visible* clipping – glow will keep +working when looking at the background sky. + +### Process > Size Limit + +If set to a value greater than ``0``, the size of the texture is limited on +import to a value smaller than or equal to the value specified here. For +non-square textures, the size limit affects the longer dimension, with the +shorter dimension scaled to preserve aspect ratio. Resizing is performed using +cubic interpolation. + +This can be used to reduce memory usage without affecting the source images, or +avoid issues with textures not displaying on mobile/web platforms (as these +usually can't display textures larger than 4096×4096). + +### Detect 3D > Compress To + +This changes the [doc_importing_images_compress_mode](doc_importing_images_compress_mode) option that is used +when a texture is detected as being used in 3D. + +Changing this import option only has an effect if a texture is detected as being +used in 3D. Changing this to **Disabled** then reimporting will not change the +existing compress mode on a texture (if it's detected to be used in 3D), but +choosing **VRAM Compressed** or **Basis Universal** will. + +### SVG > Scale + +*This is only available for SVG images.* + +The scale the SVG should be rendered at, with ``1.0`` being the original design +size. Higher values result in a larger image. Note that unlike font +oversampling, this affects the physical size the SVG is rendered at in 2D. See +also **Editor > Scale With Editor Scale** below. + +### Editor > Scale With Editor Scale + +*This is only available for SVG images.* + +If true, scales the imported image to match the editor's display scale factor. +This should be enabled for editor plugin icons and custom class icons, but +should be left disabled otherwise. + +### Editor > Convert Colors With Editor Theme + +*This is only available for SVG images.* + +If checked, converts the imported image's colors to match the editor's icon and +font color palette. This assumes the image uses the exact same colors as +[Redot's own color palette for editor icons ](../../Contributing/Development/editor/creating_icons.md), with the +source file designed for a dark editor theme. This should be enabled for editor +plugin icons and custom class icons, but should be left disabled otherwise. + +## Importing SVG images with text + +As the SVG library used in Redot doesn't support rasterizing text found in SVG +images, text must be converted to a path first. Otherwise, text won't appear in +the rasterized image. + +There are two ways to achieve this in a non-destructive manner, so you can keep +editing the original text afterwards: + +- Select your text object in Inkscape, then duplicate it in place by pressing + `Ctrl + D` and use **Path > Object to Path**. Hide the original text + object afterwards using the **Layers and Objects** dock. +- Use the Inkscape command line to export an SVG from another SVG file with text + converted to paths: + +``` +inkscape --export-text-to-path --export-filename svg_with_text_converted_to_path.svg svg_with_text.svg + +``` + +## Best practices + +### Supporting high-resolution texture sizes in 2D without artifacts + +To support [multiple resolutions ](../rendering/multiple_resolutions.md) with crisp +visuals at high resolutions, you will need to use high-resolution source images +(suited for the highest resolution you wish to support without blurriness, which +is typically 4K in modern desktop games). + +There are 2 ways to proceed: + +- Use a high base resolution in the project settings (such as 4K), then use the + textures at original scale. This is an easier approach. +- Use a low base resolution in the project settings (such as 1080p), then + downscale textures when using them. This is often more difficult and can make + various calculations in script tedious, so the approach described above is + recommended instead. + +After doing this, you may notice that textures become grainy at lower viewport +resolutions. To resolve this, enable **Mipmaps** on textures used in 2D in the +Import dock. This will increase memory usage. + +Enabling mipmaps can also make textures appear blurrier, but you can choose +to make textures sharper (at the cost of some graininess) by setting +**Rendering > Textures > Default Filters > Texture Mipmap Bias** to a +negative value. + +### Use appropriate texture sizes in 3D + +While there's no "one size fits all" recommendation, here are some general +recommendations for choosing texture sizes in 3D: + +- The size of a texture should be adjusted to have a consistent texel density + compared to surrounding objects. While this cannot be ensured perfectly when + sticking to power-of-two texture sizes, it's usually possible to keep texture + detail fairly consistent throughout a 3D scene. +- The smaller the object appears on screen, the smaller its texture should be. + For example, a tree that only appears in the background doesn't need a texture + resolution as high as other objects the player may be able to walk close to. +- Using power-of-two texture sizes is recommended, but is not required. Textures + don't have to be square – sizes such as 1024×512 are acceptable. +- There are diminishing returns to using large texture sizes, despite the + increased memory usage and loading times. Most modern 3D games not using a + pixel art style stick to 2048×2048 textures on average, with 1024×1024 and + 512×512 for textures spanning smaller surfaces. +- When working with physically-based materials in 3D, you can reduce memory + usage and file size without affecting quality too much by using a lower + resolution for certain texture maps. This works especially well for textures + that only feature low-frequency detail (such as a normal map for a snow + texture). + +If you have control over how the 3D models are created, these tips are also +worth exploring: + +- When working with 3D models that are mostly symmetrical, you may be able to + use mirrored UVs to double the effective texel density. This may look + unnatural when used on human faces though. +- When working with 3D models using a low-poly style and plain colors, you can + rely on vertex colors instead of textures to represent colors on the model's + surfaces. + +:::info + +Images can be loaded and saved at runtime using +[runtime file loading and saving ](doc_runtime_file_loading_and_saving_images), +including from an exported project. + +::: diff --git a/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_translations.md b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_translations.md new file mode 100644 index 0000000..ced7514 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/importing_translations.md @@ -0,0 +1,105 @@ + +# Importing translations + +## Games and internationalization + +The gaming community isn't monolingual or monocultural. It's made up of +many different languages and cultures - just like the Redot community! +If you want to allow players to experience your game in their language, +one of things you'll need to provide is text translations, which Redot +supports via internationalized text. + +In regular desktop or mobile applications, internationalized text is +usually located in resource files (or .po files for GNU stuff). Games, +however, can use several orders of magnitude more text than +applications, so they must support efficient methods for dealing with +loads of multilingual text. + +There are two approaches to generate multilingual language games and +applications. Both are based on a key:value system. The first is to use +one of the languages as the key (usually English), the second is to use a +specific identifier. The first approach is probably easier for +development if a game is released first in English, later in other +languages, but a complete nightmare if working with many languages at +the same time. + +In general, games use the second approach and a unique ID is used for +each string. This allows you to revise the text while it is being +translated to other languages. The unique ID can be a number, a string, +or a string with a number (it's just a unique string anyway). + +:::note +If you need a more powerful file format, Redot also supports +loading translations written in the gettext ``.po`` format. See +[doc_localization_using_gettext](../i18n/localization_using_gettext.md) for details. + +::: + +## Translation format + +To complete the picture and allow efficient support for translations, +Redot has a special importer that can read CSV files. Most spreadsheet +editors can export to this format, so the only requirement is that the files +have a special arrangement. The CSV files **must** be saved with UTF-8 encoding +without a [byte order mark ](https://en.wikipedia.org/wiki/Byte_order_mark). + +CSV files must be formatted as follows: + +| keys | <lang1> | <lang2> | <langN> | +| --- | --- | --- | --- | +| KEY1 | string | string | string | +| KEY2 | string | string | string | +| KEYN | string | string | string | + +The "lang" tags must represent a language, which must be one of the [valid locales ](../i18n/locales.md) supported by the engine, or they must start with an underscore (`_`), +which means the related column is served as comment and won't be imported. +The "KEY" tags must be unique and represent a string universally (they are usually in +uppercase, to differentiate from other strings). These keys will be replaced at +runtime by the matching translated string. Note that the case is important, +"KEY1" and "Key1" will be different keys. +The top-left cell is ignored and can be left empty or having any content. +Here's an example: + +| keys | en | es | ja | +| --- | --- | --- | --- | +| GREET | Hello, friend! | Hola, amigo! | こんにちは | +| ASK | How are you? | Cómo está? | 元気ですか | +| BYE | Goodbye | Adiós | さようなら | +| QUOTE | "Hello" said the man. | "Hola" dijo el hombre. | 「こんにちは」男は言いました | + +The same example is shown below as a comma-separated plain text file, +which should be the result of editing the above in a spreadsheet. +When editing the plain text version, be sure to enclose with double +quotes any message that contains commas, line breaks or double quotes, +so that commas are not parsed as delimiters, line breaks don't create new +entries and double quotes are not parsed as enclosing characters. Be sure +to escape any double quotes a message may contain by preceding them with +another double quote. Alternatively, you can select another delimiter than +comma in the import options. + +```none +keys,en,es,ja +GREET,"Hello, friend!","Hola, amigo!",こんにちは +ASK,How are you?,Cómo está?,元気ですか +BYE,Goodbye,Adiós,さようなら +QUOTE,"""Hello"" said the man.","""Hola"" dijo el hombre.",「こんにちは」男は言いました + +``` + +## CSV importer + +Redot will treat CSV files as translations by default. It will import them +and generate one or more compressed translation resource files next to it. + +Importing will also add the translation to the list of +translations to load when the game runs, specified in project.Redot (or the +project settings). Redot allows loading and removing translations at +runtime as well. + +Select the ``.csv`` file and access the **Import** dock to define import +options. You can toggle the compression of the imported translations, and +select the delimiter to use when parsing the CSV file. + +![Image](/img/Tutorials/assets_pipeline/img/import_csv.webp) + +Be sure to click **Reimport** after any change to these options. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/index.md b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/index.md new file mode 100644 index 0000000..53071de --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/index.md @@ -0,0 +1,18 @@ +# Assets Pipeline + +This section contains tutorials and documentation about assets pipeline in Redot Engine. + +## Articles + +- [Exporting 3D scenes](exporting_3d_scenes) +- [Import process](import_process) +- [Importing audio samples](importing_audio_samples) +- [Importing images](importing_images) +- [Importing translations](importing_translations) +- [Retargeting 3D Skeletons](retargeting_3d_skeletons) + +## Subcategories + +- [Escn exporter](./escn_exporter/index) +- [Importing 3d scenes](./importing_3d_scenes/index) + diff --git a/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/retargeting_3d_skeletons.md b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/retargeting_3d_skeletons.md new file mode 100644 index 0000000..d7e50a3 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/assets_pipeline/retargeting_3d_skeletons.md @@ -0,0 +1,170 @@ + +# Retargeting 3D Skeletons + +## To share animations among multiple Skeletons + +Redot has Position/Rotation/Scale 3D tracks (which this document calls "Transform" tracks) +with Nodepaths to bones for Skeleton bone animation. This means you can't +share animations between multiple Skeletons just by using the same bone +names. + +Redot allows each bone to have a parent-child relationship and can have rotation +and scale as well as position, which means that bones that share a name can still +have different Transform values. + +The Skeleton stores the Transform values necessary for the default pose as Bone Rest. +If Bone Pose is equal to Bone Rest, it means that the Skeleton is in the default pose. + +:::note +Redot 3.x and Redot 4.0+ have different Bone Pose behaviors. +In Redot 3.x, Bone Pose is relative to Bone Rest, but in Redot 4.0+, +it includes Bone Rest. See this [article ](https://godotengine.org/article/animation-data-redesign-40). + +::: + +Skeletal models have different Bone Rests depending on the environment from +which they were exported. For example, the bones of a glTF model output from Blender +have "Edit Bone Orientation" as the Bone Rest rotation. However, there are skeletal +models without any Bone Rest rotations, such as the glTF model output from Maya. + +To share animations in Redot, it is necessary to match Bone Rests as well as Bone Names +to remove unwanted tracks in some cases. In Redot 4.0+, you can do that using the scene +importer. + +## Options for Retargeting + +### Bone Map + +When you select the Skeleton3D node in the advanced scene import menu, a menu will appear +on the right-hand side containing the "Retarget" section. The Retarget section has a single +property ``bone_map``. + +![Image](/img/Tutorials/assets_pipeline/img/retargeting1.webp) + +With the Skeleton node selected, first set up a new [class_bonemap](class_bonemap) and [class_skeletonprofile](class_skeletonprofile). +Redot has a preset called [class_skeletonprofilehumanoid](class_skeletonprofilehumanoid) for humanoid models. +This tutorial proceeds with the assumption that you are using [class_skeletonprofilehumanoid](class_skeletonprofilehumanoid). + +:::note +If you need a profile that is different from :ref:`class_skeletonprofilehumanoid`, you can export +a [class_skeletonprofile](class_skeletonprofile) from the editor by selecting a Skeleton3D and using the **Skeleton3D** menu in the 3D viewport's toolbar. + +::: + +When you use [class_skeletonprofilehumanoid](class_skeletonprofilehumanoid), auto-mapping will be performed when the +[class_skeletonprofile](class_skeletonprofile) is set. If the auto-mapping does not work well, you can map bones manually. + +![Image](/img/Tutorials/assets_pipeline/img/retargeting2.webp) + +Any missing, duplicate or incorrect parent-child relationship mappings will be indicated +by a magenta / red button (depending on the editor setting). It does not block the import process, +but it warns that animations may not be shared correctly. + +:::note +The auto-mapping uses pattern matching for the bone names. So we recommend +to use common English names for bones. + +::: + +After you set up the ``bone_map``, several options are available in the sections below. + +![Image](/img/Tutorials/assets_pipeline/img/retargeting3.webp) + +### Remove Tracks + +If you import resources as an [class_animationlibrary](class_animationlibrary) that will be shared, we recommend to enable these options. +However, if you import resources as scenes, these should be disabled in some cases. +For example, if you import a character with animated accessories, +these options may cause the accessories to not animate. + +#### Except Bone Transform + +Removes any tracks except the bone Transform track from the animations. + +#### Unimportant Positions + +Removes Position tracks other than ``root_bone`` and ``scale_base_bone`` +defined in [class_skeletonprofile](class_skeletonprofile) from the animations. In [class_skeletonprofilehumanoid](class_skeletonprofilehumanoid), +this means that to remove Position tracks other than "Root" and "Hips". +Since Redot 4.0+, animations include Bone Rest in the Transform value. If you disable this option, +the animation may change the body shape unpredictably. + +#### Unmapped Bones + +Removes unmapped bone Transform tracks from the animations. + +### Bone Renamer + +#### Rename Bones + +Rename the mapped bones. + +#### Unique Node + +Makes Skeleton a unique node with the name specified in the ``skeleton_name``. +This allows the animation track paths to be unified independent of the scene hierarchy. + +### Rest Fixer + +Reference poses defined in [class_skeletonprofilehumanoid](class_skeletonprofilehumanoid) have the following rules: + +* The humanoid is T-pose +* The humanoid is facing +Z in the Right-Handed Y-UP Coordinate System +* The humanoid should not have a Transform as Node +* Directs the +Y axis from the parent joint to the child joint +* +X rotation bends the joint like a muscle contracting + +These rules are convenient definitions for blend animation and Inverse Kinematics (IK). +If your model does not match this definition, you need to fix it with these options. + +#### Apply Node Transform + +If the asset is not exported correctly for sharing, the imported Skeleton may have +a Transform as a Node. For example, a glTF exported from Blender with no "Apply Transform" +executed is one such case. It looks like the model matches the definition, +but the internal Transforms are different from the definition. +This option fixes such models by applying Transforms on import. + +:::note +If the imported scene contains objects other than Skeletons, this option may have a negative effect. + +::: + +#### Normalize Position Tracks + +Position track is used mostly for model movement, but sharing the moving animation +between models with different heights may cause the appearance of slipping +due to the difference in stride length. This option normalizes the Position track values +based on the ``scale_base_bone`` height. The ``scale_base_bone`` height is stored +in the Skeleton as the ``motion_scale``, and the normalized Position track values is +multiplied by that value on playback. If this option is disabled, the Position tracks +is not normalized and the Skeleton's ``motion_scale`` is always imported as ``1.0``. + +With [class_skeletonprofilehumanoid](class_skeletonprofilehumanoid), ``scale_base_bone`` is "Hips", therefore the Hips' height is used as the ``motion_scale``. + +#### Overwrite Axis + +Unifies the models' Bone Rests by overwriting it to match the reference poses defined in the [class_skeletonprofile](class_skeletonprofile). + +:::note +This is the most important option for sharing animations in Redot 4.0+, +but be aware that this option can produce horrible results **if the original Bone Rest set externally is important**. +If you want to share animations with keeping the original Bone Rest, +consider to use the [Realtime Retarget Module ](https://github.com/TokageItLab/realtime_retarget). + +::: + +#### Fix Silhouette + +Attempts to make the model's silhouette match that of the reference poses defined in the [class_skeletonprofile](class_skeletonprofile), +such as T-Pose. This cannot fix silhouettes which are too different, and it may not work for fixing bone roll. + +With [class_skeletonprofilehumanoid](class_skeletonprofilehumanoid), this option does not need to be enabled for T-pose models, +but should be enabled for A-pose models. However in that case, the fixed foot results +may be bad depending on the heel height of the model, so it may be necessary to add +the [class_skeletonprofile](class_skeletonprofile) bone names you do not want fixed in the ``filter`` array, as in the below example. + +![Image](/img/Tutorials/assets_pipeline/img/retargeting4.webp) + +Also, for models with bent knees or feet, it may be necessary to adjust the ``scale_base_bone`` height. +For that, you can use ``base_height_adjustment`` option. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/audio/audio_buses.md b/Redot-Documentation/docs/26.1/Tutorials/audio/audio_buses.md new file mode 100644 index 0000000..4cdfbc5 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/audio/audio_buses.md @@ -0,0 +1,128 @@ +:::warning +This page is marked as outdated and may not reflect current Redot behavior. +::: + +# Audio buses + +## Introduction + +Redot's audio processing code has been written with games in mind, with the aim +of achieving an optimal balance between performance and sound quality. + +Redot's audio engine allows any number of audio buses to be created and any +number of effect processors can be added to each bus. Only the hardware of the +device running your game will limit the number of buses and effects that can be +used before performance starts to suffer. + +## Decibel scale + +Redot's sound interface is designed to meet the expectations of sound design +professionals. To this end, it primarily uses the decibel scale. + +For those unfamiliar with it, it can be explained with a few facts: + +- The decibel (dB) scale is a relative scale. It represents the ratio of + sound power by using 20 times the base 10 logarithm of the ratio + (20 × log\ :sub:`10`\ (P/P\ :sub:`0`\ )). +- For every 6 dB, sound amplitude doubles or halves. 12 dB represents a factor + of 4, 18 dB a factor of 8, 20 dB a factor of 10, 40 dB a factor of 100, etc. +- Since the scale is logarithmic, true zero (no audio) can't be represented. +- 0 dB is the maximum amplitude possible in a digital audio system. + This limit is not the human limit, but a limit from the sound hardware. + Audio with amplitudes that are too high to be represented properly below 0 dB + create a kind of distortion called *clipping*. +- To avoid clipping, your sound mix should be arranged so that the output of the + *master bus* (more on that later) never exceeds 0 dB. +- Every 6 dB below the 0 dB limit, sound energy is *halved*. + It means the sound volume at -6 dB is half as loud as 0dB. + -12 dB is half as loud as -6 dB and so on. +- When working with decibels, sound is considered no longer audible + between -60 dB and -80 dB. This makes your working range generally + between -60 dB and 0 dB. + +This can take a bit getting used to, but it's friendlier in the end +and will allow you to communicate better with audio professionals. + +## Audio buses + +Audio buses can be found in the bottom panel of the Redot editor: + +![Image](/img/Tutorials/audio/img/audio_buses1.png) + +An *audio bus* (also called an *audio channel*) can be considered a place that +audio is channeled through on the way to playback through a device's speakers. +Audio data can be *modified* and *re-routed* by an audio bus. An audio bus +has a VU meter (the bars that light up when sound is played) which indicates the +amplitude of the signal passing through. + +The leftmost bus is the *master bus*. This bus outputs the mix to your speakers +so, as mentioned in the *Decibel scale* section above, make sure that your mix +level doesn't reach 0 dB in this bus. The rest of the audio buses can be +flexibly routed. After modifying the sound, they send it to another bus to +the left. The destination bus can be specified for each of the non-master audio +buses. Routing always passes audio from buses on the right to buses further +to the left. This avoids infinite routing loops. + +![Image](/img/Tutorials/audio/img/audio_buses2.png) + +In the above image, the output of *Bus 2* has been routed to the *Master* bus. + +## Playback of audio through a bus + +To test passing audio to a bus, create an AudioStreamPlayer node, load an +AudioStream and select a target bus for playback: + +![Image](/img/Tutorials/audio/img/audio_buses3.png) + +Finally, toggle the **Playing** property to **On** and sound will flow. + +:::info + +You may also be interested in reading about [doc_audio_streams](audio_streams.md) now. + +::: + +## Adding effects + +:::warning + +This feature is not supported on the web platform if the AudioStreamPlayer's +playback mode is set to **Sample**, which is the default. It will only work if the +playback mode is set to **Stream**, at the cost of increased latency if threads +are not enabled. + +See [Audio playback in the Exporting for the Web documentation ](doc_exporting_for_web_audio_playback) +for details. + +::: + +Audio buses can contain all sorts of effects. These effects modify the sound in +one way or another and are applied in order. + +![Image](/img/Tutorials/audio/img/audio_buses4.webp) + +For information on what each effect does, see [doc_audio_effects](audio_effects.md). + +## Automatic bus disabling + +There is no need to disable buses manually when not in use. Redot detects +that the bus has been silent for a few seconds and disables it (including +all effects). + +![Image](/img/Tutorials/audio/img/audio_buses5.png) + + Disabled buses have a blue VU meter instead of a red-green one. + +## Bus rearrangement + +Stream Players use bus names to identify a bus, which allows adding, removing +and moving buses around while the reference to them is kept. However, if a bus +is renamed, the reference will be lost and the Stream Player will output +to Master. This system was chosen because rearranging buses is a more common +process than renaming them. + +## Default bus layout + +The default bus layout is automatically saved to the +``res://default_bus_layout.tres`` file. Custom bus arrangements can be saved +and loaded from disk. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/audio/audio_effects.md b/Redot-Documentation/docs/26.1/Tutorials/audio/audio_effects.md new file mode 100644 index 0000000..1d8002c --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/audio/audio_effects.md @@ -0,0 +1,204 @@ +:::warning +This page is marked as outdated and may not reflect current Redot behavior. +::: + +# Audio effects + +Redot includes several audio effects that can be added to an audio bus to +alter every sound file that goes through that bus. + +![Image](/img/Tutorials/audio/img/audio_buses4.webp) + +Try them all out to get a sense of how they alter sound. Here follows a short +description of the available effects: + +### Amplify + +Amplify changes the volume of the signal. Some care needs to be taken, though: +setting the level too high can make the sound digitally clip, which can produce +unpleasant crackles and pops. + +### BandLimit and BandPass + +These are resonant filters which block frequencies around the *Cutoff* point. +BandPass can be used to simulate sound passing through an old telephone line or +megaphone. Modulating the BandPass frequency can simulate the sound of a wah-wah +guitar pedal, think of the guitar in Jimi Hendrix's *Voodoo Child (Slight +Return)*. + +### Capture + +The Capture effect copies the audio frames of the audio bus that it is on into +an internal buffer. This can be used to capture data from the microphone +or to transmit audio over the network in real-time. + +### Chorus + +As the name of the effect implies, the Chorus effect makes a single audio sample +sound like an entire chorus. It does this by duplicating a signal and very +slightly altering the timing and pitch of each duplicate, and varying that +over time via an LFO (low frequency oscillator). The duplicate(s) are then +mixed back together with the original signal, producing a lush, wide, and +large sound. Although chorus is traditionally used for voices, it can be +desirable with almost any type of sound. + +### Compressor + +A dynamic range compressor automatically attenuates (ducks) the level of the incoming +signal when its amplitude exceeds a certain threshold. The level of attenuation +applied is proportional to how far the incoming audio exceeds the threshold. +The compressor's Ratio parameter controls the degree of attenuation. +One of the main uses of a compressor is to reduce the dynamic range of signals +with very loud and quiet parts. Reducing the dynamic range of a signal +can make it fit more comfortably in a mix. + +The compressor has many uses. For example: + +- It can be used in the Master bus to compress the whole output prior to being hit by a limiter, making the effect of the limiter much more subtle. +- It can be used in voice channels to ensure they sound as even as possible. +- It can be *sidechained* by another sound source. This means it can reduce the sound level + of one signal using the level of another audio bus for threshold detection. + This technique is very common in video game mixing to "duck" the level of + music or sound effects when in-game or multiplayer voices need to be fully audible. +- It can accentuate transients by using a slower attack. + This can make sound effects more punchy. + +:::note + +If your goal is to prevent a signal from exceeding a given amplitude +altogether, rather than to reduce the dynamic range of the signal, +a [limiter ](doc_audio_buses_limiter) is likely a better choice +than a compressor for this purpose. However, applying compression before +a limiter is still good practice. + +::: + +### Delay + +Digital delay essentially duplicates a signal and repeats it at a specified +speed with a volume level that decays for each repeat. Delay is great for +simulating the acoustic space of a canyon or large room, where sound bounces +have a lot of *delay* between their repeats. This is in contrast to reverb, +which has a more natural and blurred sound to it. Using this in conjunction +with reverb can create very natural sounding environments! + +### Distortion + +Makes the sound distorted. Redot offers several types of distortion: + +- *Overdrive* sounds like a guitar distortion pedal or megaphone. Sounds distorted with this sound like they're coming through + a low-quality speaker or device. +- *Tan* sounds like another interesting flavor of overdrive. +- *Bit crushing* clamps the amplitude of the signal, making it sound flat and crunchy. + +All three types of distortion can add higher frequency sounds to an original sound, making it stand out better in a mix. + +### EQ + +EQ is what all other equalizers inherit from. It can be extended with Custom +scripts to create an equalizer with a custom number of bands. + +### EQ6, EQ10, EQ21 + +Redot provides three equalizers with different numbers of bands, which +are represented in the title (6, 10, and 21 bands, respectively). +An equalizer on the Master bus can be useful for cutting low and high +frequencies that the device's speakers can't reproduce well. +For example, phone or tablet speakers usually don't reproduce +low frequency sounds well, and could make a limiter or compressor +attenuate sounds that aren't even audible to the user anyway. + +Note: The equalizer effect can be disabled when headphones are plugged in, giving the user the best of both worlds. + +### Filter + +Filter is what all other filters inherit from and should not be used directly. + +### HardLimiter + +A limiter is similar to a compressor, but it's less flexible and designed to +prevent a signal's amplitude exceeding a given dB threshold. Adding a limiter to the final point of +the Master bus is good practice, as it offers an easy safeguard against clipping. + +### HighPassFilter + +Cuts frequencies below a specific *Cutoff* frequency. +HighPassFilter is used to reduce the bass content of a +signal. + +### HighShelfFilter + +Reduces all frequencies above a specific *Cutoff* frequency. + +### Limiter + +This is the old limiter effect, and it is recommended to use the new HardLimiter +effect instead. + +Here is an example of how this effect works, if the ceiling is set to -12 dB, and the +threshold is 0 dB, all samples going through get reduced by 12dB. This changes the +waveform of the sound and introduces distortion. + +This effect is being kept to preserve compatibility, however it should be considered +deprecated. + +### LowPassFilter + +Cuts frequencies above a specific *Cutoff* frequency and can also resonate +(boost frequencies close to the *Cutoff* frequency). Low pass filters can be +used to simulate "muffled" sound. For instance, underwater sounds, sounds +blocked by walls, or distant sounds. + +### LowShelfFilter + +Reduces all frequencies below a specific *Cutoff* frequency. + +### NotchFilter + +The opposite of the BandPassFilter, it removes a band of sound from the +frequency spectrum at a given *Cutoff* frequency. + +### Panner + +The Panner allows the stereo balance of a signal to be adjusted between +the left and right channels. Headphones are recommended when configuring in this effect. + +### Phaser + +This effect is formed by de-phasing two duplicates of the same sound so +they cancel each other out in an interesting way. Phaser produces a +pleasant whooshing sound that moves back and forth through the audio +spectrum, and can be a great way to create sci-fi effects or Darth +Vader-like voices. + +### PitchShift + +This effect allows the adjustment of the signal's pitch independently of its +speed. All frequencies can be increased/decreased with minimal effect on +transients. PitchShift can be useful to create unusually high or deep voices. +Do note that altering pitch can sound unnatural when pushed outside of a +narrow window. + +### Record + +The Record effect allows the user to record sound from a microphone. + +### Reverb + +Reverb simulates rooms of different sizes. It has adjustable parameters that can +be tweaked to obtain the sound of a specific room. Reverb is commonly outputted +from [Area3Ds ](class_Area3D) +(see [Reverb buses ](doc_audio_streams_reverb_buses)), or to apply +a "chamber" feel to all sounds. + +### SpectrumAnalyzer + +This effect doesn't alter audio, instead, you add this effect to buses you want +a spectrum analysis of. This would typically be used for audio visualization. +Visualizing voices can be a great way to draw attention to them without just +increasing their volume. +A demo project using this can be found [here ](https://github.com/redot-engine/redot-demo-projects/tree/master/audio/spectrum). + +### StereoEnhance + +This effect uses a few algorithms to enhance a signal's stereo width. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/audio/audio_streams.md b/Redot-Documentation/docs/26.1/Tutorials/audio/audio_streams.md new file mode 100644 index 0000000..0d4f903 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/audio/audio_streams.md @@ -0,0 +1,131 @@ +:::warning +This page is marked as outdated and may not reflect current Redot behavior. +::: + +# Audio streams + +## Introduction + +As you might have already read in [doc_audio_buses](audio_buses.md), sound is sent to +each bus via an AudioStreamPlayer node. There are different kinds +of AudioStreamPlayers. Each one loads an AudioStream and plays it back. + +## AudioStream + +An audio stream is an abstract object that emits sound. The sound can come from +many places, but is most commonly loaded from the filesystem. Audio files can be +loaded as AudioStreams and placed inside an AudioStreamPlayer. You can find +information on supported formats and differences in [doc_importing_audio_samples](../assets_pipeline/importing_audio_samples.md). + +There are other types of AudioStreams, such as [AudioStreamRandomizer](class_AudioStreamRandomizer). +This one picks a different audio stream from a list of streams each time it's played +back, and applies random pitch and volume shifting. This can be helpful for adding +variation to sounds that are played back often. + +## AudioStreamPlayer + +![Image](/img/Tutorials/audio/img/audio_stream_player.webp) + +This is the standard, non-positional stream player. It can play to any bus. +In 5.1 sound setups, it can send audio to stereo mix or front speakers. + +Playback Type is an experimental setting, and could change in future versions +of Redot. It exists so Web exports use Web Audio-API based samples instead of +streaming all sounds to the browser, unlike most platforms. This prevents the +audio from being garbled in single-threaded Web exports. By default, only the +Web platform will use samples. Changing this setting is not recommended, unless +you have an explicit reason to. You can change the default playback type +for the web and other platforms in the project settings under **Audio > General** +(advanced settings must be turned on to see the setting). + +## AudioStreamPlayer2D + +![Image](/img/Tutorials/audio/img/audio_stream_2d.webp) + +This is a variant of AudioStreamPlayer, but emits sound in a 2D positional +environment. When close to the left of the screen, the panning will go left. +When close to the right side, it will go right. + +:::note + +Area2Ds can be used to divert sound from any AudioStreamPlayer2Ds they +contain to specific buses. This makes it possible to create buses with +different reverb or sound qualities to handle action happening in a +particular parts of your game world. + +::: + +![Image](/img/Tutorials/audio/img/audio_stream_2d_area.webp) + +## AudioStreamPlayer3D + +![Image](/img/Tutorials/audio/img/audio_stream_3d.webp) + +This is a variant of AudioStreamPlayer, but emits sound in a 3D positional +environment. Depending on the location of the player relative to the screen, +it can position sound in stereo, 5.1 or 7.1 depending on the chosen audio setup. + +Similar to AudioStreamPlayer2D, an Area3D can divert the sound to an audio bus. + +![Image](/img/Tutorials/audio/img/audio_stream_3d_area.webp) + +Unlike for 2D, the 3D version of AudioStreamPlayer has a few more advanced options: + +### Reverb buses + +:::warning + +This feature is not supported on the web platform if the AudioStreamPlayer's +playback mode is set to **Sample**, which is the default. It will only work if the +playback mode is set to **Stream**, at the cost of increased latency if threads +are not enabled. + +See [Audio playback in the Exporting for the Web documentation ](doc_exporting_for_web_audio_playback) +for details. + +::: + +Redot allows for 3D audio streams that enter a specific Area3D node to send dry +and wet audio to separate buses. This is useful when you have several reverb +configurations for different types of rooms. This is done by enabling this type +of reverb in the **Reverb Bus** section of the Area3D's properties: + +![Image](/img/Tutorials/audio/img/audio_stream_reverb_bus.webp) + +At the same time, a special bus layout is created where each Area3D receives the +reverb info from each Area3D. A Reverb effect needs to be created and configured +in each reverb bus to complete the setup for the desired effect: + +![Image](/img/Tutorials/audio/img/audio_stream_reverb_bus2.webp) + +The Area3D's **Reverb Bus** section also has a parameter named **Uniformity**. +Some types of rooms bounce sounds more than others (like a warehouse), so +reverberation can be heard almost uniformly across the room even though the +source may be far away. Playing around with this parameter can simulate +that effect. + +### Doppler + +:::warning + +This feature is not supported on the web platform if the AudioStreamPlayer's +playback mode is set to **Sample**, which is the default. It will only work if the +playback mode is set to **Stream**, at the cost of increased latency if threads +are not enabled. + +See [Audio playback in the Exporting for the Web documentation ](doc_exporting_for_web_audio_playback) +for details. + +::: + +When the relative velocity between an emitter and listener changes, this is +perceived as an increase or decrease in the pitch of the emitted sound. +Redot can track velocity changes in the AudioStreamPlayer3D and Camera nodes. +Both nodes have this property, which must be enabled manually: + +![Image](/img/Tutorials/audio/img/audio_stream_doppler.webp) + +Enable it by setting it depending on how objects will be moved: +use **Idle** for objects moved using ``_process``, or **Physics** +for objects moved using ``_physics_process``. The tracking will +happen automatically. \ No newline at end of file diff --git a/Redot-Documentation/docs/26.1/Tutorials/audio/index.md b/Redot-Documentation/docs/26.1/Tutorials/audio/index.md new file mode 100644 index 0000000..b1f9741 --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/audio/index.md @@ -0,0 +1,13 @@ +# Audio + +This section contains tutorials and documentation about audio in Redot Engine. + +## Articles + +- [Audio buses](audio_buses) +- [Audio effects](audio_effects) +- [Audio streams](audio_streams) +- [Recording with microphone](recording_with_microphone) +- [Sync the gameplay with audio and music](sync_with_audio) +- [Text to speech](text_to_speech) + diff --git a/Redot-Documentation/docs/26.1/Tutorials/audio/recording_with_microphone.md b/Redot-Documentation/docs/26.1/Tutorials/audio/recording_with_microphone.md new file mode 100644 index 0000000..d8a5eea --- /dev/null +++ b/Redot-Documentation/docs/26.1/Tutorials/audio/recording_with_microphone.md @@ -0,0 +1,213 @@ + +:::warning +This page is marked as outdated and may not reflect current Redot behavior. +::: + +# Recording with microphone + +Redot supports in-game audio recording for Windows, macOS, Linux, Android and +iOS. + +A simple demo is included in the official demo projects and will be used as +support for this tutorial: +. + +You will need to enable audio input in the [Audio > Driver > Enable Input](class_ProjectSettings_property_audio/driver/enable_input) project setting, or you'll just get empty audio files. + +## The structure of the demo + +The demo consists of a single scene. This scene includes two major parts: the +GUI and the audio. + +We will focus on the audio part. In this demo, a bus named ``Record`` with the +effect ``Record`` is created to handle the audio recording. +An ``AudioStreamPlayer`` named ``AudioStreamRecord`` is used for recording. + +![Image](/img/Tutorials/audio/img/record_bus.png) + +![Image](/img/Tutorials/audio/img/record_stream_player.png) + + + + + +```gdscript +var effect +var recording + +func _ready(): + # We get the index of the "Record" bus. + var idx = AudioServer.get_bus_index("Record") + # And use it to retrieve its first effect, which has been defined + # as an "AudioEffectRecord" resource. + effect = AudioServer.get_bus_effect(idx, 0) + +``` + + + + + +```csharp +private AudioEffectRecord _effect; +private AudioStreamSample _recording; + +public override void _Ready() +{ + // We get the index of the "Record" bus. + int idx = AudioServer.GetBusIndex("Record"); + // And use it to retrieve its first effect, which has been defined + // as an "AudioEffectRecord" resource. + _effect = (AudioEffectRecord)AudioServer.GetBusEffect(idx, 0); +} + +``` + + + + + +The audio recording is handled by the [class_AudioEffectRecord](class_AudioEffectRecord) resource +which has three methods: +[get_recording() ](class_AudioEffectRecord_method_get_recording), +[is_recording_active() ](class_AudioEffectRecord_method_is_recording_active), +and [set_recording_active() ](class_AudioEffectRecord_method_set_recording_active). + + + + + +```gdscript +func _on_record_button_pressed(): + if effect.is_recording_active(): + recording = effect.get_recording() + $PlayButton.disabled = false + $SaveButton.disabled = false + effect.set_recording_active(false) + $RecordButton.text = "Record" + $Status.text = "" + else: + $PlayButton.disabled = true + $SaveButton.disabled = true + effect.set_recording_active(true) + $RecordButton.text = "Stop" + $Status.text = "Recording..." + +``` + + + + + +```csharp +private void OnRecordButtonPressed() +{ + if (_effect.IsRecordingActive()) + { + _recording = _effect.GetRecording(); + GetNode