{"id":1481,"date":"2024-11-14T10:03:05","date_gmt":"2024-11-14T01:03:05","guid":{"rendered":"https:\/\/heyyocg.link\/?p=1481"},"modified":"2024-11-21T00:24:45","modified_gmt":"2024-11-20T15:24:45","slug":"ue4-26-niagara-advanced-position-based-dynamics-basic","status":"publish","type":"post","link":"https:\/\/heyyocg.link\/en\/ue4-26-niagara-advanced-position-based-dynamics-basic\/","title":{"rendered":"Niagara Advanced Guide &#8211; Position Based Dynamics"},"content":{"rendered":"\n<p>Epic Games distributes a project called \u201cContent Examples,\u201d which is a collection of various sample projects showcasing different features of Unreal Engine. Since UE4.26, there is a map called&nbsp;<em>Niagara Advanced<\/em>.<\/p>\n\n\n\n<p>This map contains a wealth of useful samples, including examples of new Niagara features and practical applications. Upon reviewing the implementations, there\u2019s a lot to learn.<\/p>\n\n\n\n<p>This article will break down these samples into multiple parts for detailed explanations.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img fetchpriority=\"high\" decoding=\"async\" width=\"1024\" height=\"621\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2024\/11\/image-2-1024x621.png\" alt=\"\" class=\"wp-image-1466\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2024\/11\/image-2-1024x621.png 1024w, https:\/\/heyyocg.link\/wp-content\/uploads\/2024\/11\/image-2-300x182.png 300w, https:\/\/heyyocg.link\/wp-content\/uploads\/2024\/11\/image-2-768x466.png 768w, https:\/\/heyyocg.link\/wp-content\/uploads\/2024\/11\/image-2-1536x932.png 1536w, https:\/\/heyyocg.link\/wp-content\/uploads\/2024\/11\/image-2-2048x1242.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>By the way,&nbsp;<em>Content Examples<\/em>&nbsp;can be downloaded from&nbsp;<a href=\"https:\/\/www.fab.com\/listings\/4d251261-d98c-48e2-baee-8f4e47c67091\">this link<\/a>&nbsp;in Fab. Please note that the content may vary depending on the version of UE.<\/p>\n\n\n\n<p>\u203bNote: This blog is written based on UE4.26 Contents Example, so it might differ from the latest version in small details, but the concept itself explained here won&#8217;t change.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">TLDR;)<\/h2>\n\n\n\n<ul>\n<li>Position-Based Dynamics (PBD) is a type of physics simulation that is based on positions (such as collision detection).<\/li>\n\n\n\n<li>By using Position-Based Dynamics, you can achieve collision handling between particles.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p>In this article, I will explain Position-Based Dynamics (PBD). Rather than being a brand-new feature, it makes use of a set of functionalities added since UE4.26, such as Simulation Stage, Particle Attribute Reader, and Neighbor Grid3D. The basic functionalities for PBD are modularized, making them available for use.<\/p>\n\n\n\n<p>So, for those who are not yet familiar with these features, I recommend reading the following articles first:<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-heyyo-cg wp-block-embed-heyyo-cg\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"zBcERgcFiH\"><a href=\"https:\/\/heyyocg.link\/en\/ue4-26-niagara-adavanced-simulation-stage-basic\/\">Niagara Advanced Guide &#8211; Basics of the Simulation Stage<\/a><\/blockquote><iframe class=\"wp-embedded-content\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; clip: rect(1px, 1px, 1px, 1px);\" title=\"&#8220;Niagara Advanced Guide &#8211; Basics of the Simulation Stage&#8221; &#8212; HeyYo CG\" src=\"https:\/\/heyyocg.link\/en\/ue4-26-niagara-adavanced-simulation-stage-basic\/embed\/#?secret=5wzBMbbODw#?secret=zBcERgcFiH\" data-secret=\"zBcERgcFiH\" width=\"500\" height=\"282\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe>\n<\/div><\/figure>\n\n\n\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-heyyo-cg wp-block-embed-heyyo-cg\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"44zopUQGyT\"><a href=\"https:\/\/heyyocg.link\/en\/ue4-26-niagara-advanced-particle-attribute-reader-basic\/\">Niagara Advanced Guide &#8211; Particle Attribute Reader<\/a><\/blockquote><iframe class=\"wp-embedded-content\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; clip: rect(1px, 1px, 1px, 1px);\" title=\"&#8220;Niagara Advanced Guide &#8211; Particle Attribute Reader&#8221; &#8212; HeyYo CG\" src=\"https:\/\/heyyocg.link\/en\/ue4-26-niagara-advanced-particle-attribute-reader-basic\/embed\/#?secret=L5u2CMcwje#?secret=44zopUQGyT\" data-secret=\"44zopUQGyT\" width=\"500\" height=\"282\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe>\n<\/div><\/figure>\n\n\n\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-heyyo-cg wp-block-embed-heyyo-cg\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"QkFjnnYCmt\"><a href=\"https:\/\/heyyocg.link\/en\/ue4-26-niagara-advanced-neighbor-grid-3d-basic\/\">Niagara Advanced Guide &#8211; Neighbor Grid3D<\/a><\/blockquote><iframe class=\"wp-embedded-content\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; clip: rect(1px, 1px, 1px, 1px);\" title=\"&#8220;Niagara Advanced Guide &#8211; Neighbor Grid3D&#8221; &#8212; HeyYo CG\" src=\"https:\/\/heyyocg.link\/en\/ue4-26-niagara-advanced-neighbor-grid-3d-basic\/embed\/#?secret=yw3ajXPB8b#?secret=QkFjnnYCmt\" data-secret=\"QkFjnnYCmt\" width=\"500\" height=\"282\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe>\n<\/div><\/figure>\n\n\n\n<p>This time, I will explain the &#8220;3.6 Position-Based Dynamics&#8221; sample from Niagara Advanced. But before diving into the sample, let&#8217;s first discuss what PBD is.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What is Position-Based Dynamics (PBD)?<\/h2>\n\n\n\n<p>In simple terms, PBD is a <strong>position-based physics simulation<\/strong>.<\/p>\n\n\n\n<p>While standard physics calculations work by applying forces \u2192 accelerations \u2192 velocities \u2192 positions based on physical laws, PBD adjusts positions based on certain constraints (e.g., objects not overlapping), and then calculates velocities from there.<\/p>\n\n\n\n<p>For more details, please refer to articles such as:<br><a href=\"https:\/\/www.4gamer.net\/games\/032\/G003263\/20140904089\/\" data-type=\"URL\" data-id=\"https:\/\/www.4gamer.net\/games\/032\/G003263\/20140904089\/\">\uff3bCEDEC 2014\uff3d\u525b\u4f53\u304b\u3089\u6d41\u4f53\u307e\u3067\uff0c\u30bb\u30ac\u306e\u30d7\u30ed\u30b0\u30e9\u30de\u30fc\u304c\u8a9e\u308b\u300c\u4f4d\u7f6e\u30d9\u30fc\u30b9\u7269\u7406\u30b7\u30df\u30e5\u30ec\u30fc\u30b7\u30e7\u30f3\u300d\u306e\u6700\u524d\u7dda<\/a><\/p>\n\n\n\n<p>In Niagara, Neighbor Grid 3D and Particle Attribute Reader are used to gather position information of nearby particles. By calculating whether particles overlap based on their predefined radius, it is possible to adjust their positions to avoid overlap.<\/p>\n\n\n\n<p>By using the Simulation Stage to repeat this process, we can achieve highly accurate adjustments.<\/p>\n\n\n\n<p>The great thing about this is that <strong>particle-to-particle collisions<\/strong>, which were previously not possible, can now be handled.<\/p>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"480\" height=\"260\" data-id=\"480\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/FeatureSample-Preview-NetMode_-Standalone-64-bit_Windows-2021-02-10-18-35-57.gif\" alt=\"\" class=\"wp-image-480\"\/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"480\" height=\"281\" data-id=\"481\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/FeatureSample_4_26-Preview-NetMode_-Standalone-64-bit_Windows-2021-02-11-15-01-58_1.gif\" alt=\"\" class=\"wp-image-481\"\/><\/figure>\n<figcaption class=\"blocks-gallery-caption wp-element-caption\">\uff08Left\uff09PBD ON\u3000\uff08Right\uff09PBD OFF<\/figcaption><\/figure>\n\n\n\n<p>It\u2019s clear from the difference: In the left image, the particles are drawn toward the center, but they don&#8217;t overlap.<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe title=\"Advanced Niagara Effects | Inside Unreal\" width=\"500\" height=\"281\" src=\"https:\/\/www.youtube.com\/embed\/31GXFW-MgQk?start=85&#038;feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<p>By the way, the &#8220;Popcorn Man&#8221; demo created by Epic also uses PBD technology.<\/p>\n\n\n\n<p>Now, let\u2019s jump into the explanation of the sample.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3.6 Position Based Dynamics Explanation<\/h2>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"480\" height=\"260\" src=\"http:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/FeatureSample-Preview-NetMode_-Standalone-64-bit_Windows-2021-02-10-18-35-57.gif\" alt=\"\" class=\"wp-image-480\"\/><\/figure>\n\n\n\n<p>In this sample, multiple spheres are drawn toward the center but behave in a way that makes them collide with each other. This is made possible by PBD.<\/p>\n\n\n\n<p>Let\u2019s go through the details.<\/p>\n\n\n\n<p>In the <strong>System Update<\/strong> phase, the <strong>Initialize Neighbor Grid<\/strong> module is used to initialize Neighbor Grid3D.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"688\" height=\"630\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_01.png\" alt=\"\" class=\"wp-image-483\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_01.png 688w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_01-300x275.png 300w\" sizes=\"(max-width: 688px) 100vw, 688px\" \/><\/figure>\n\n\n\n<p>A fairly large 4x4x4 grid is set up.<\/p>\n\n\n\n<p>The <strong>Emitter<\/strong> is configured as follows, and the actual PBD calculations are performed in the <strong>PBD Particle Collision<\/strong> module within the <strong>Particle Collisions<\/strong> Simulation Stage.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"276\" height=\"720\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_02.png\" alt=\"\" class=\"wp-image-484\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_02.png 276w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_02-115x300.png 115w\" sizes=\"(max-width: 276px) 100vw, 276px\" \/><\/figure>\n\n\n\n<p>I will explain step by step.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Emitter Update<\/h4>\n\n\n\n<p>First, 10 particles are spawned simultaneously using the <strong>Spawn Burst Instantaneous<\/strong> module.<\/p>\n\n\n\n<p>Then, the <strong>Particle Attribute Reader<\/strong> is initialized. The <strong>Emitter Name<\/strong> is set to the name of the emitter itself, allowing it to read information from other particles.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"696\" height=\"134\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_03.png\" alt=\"\" class=\"wp-image-485\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_03.png 696w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_03-300x58.png 300w\" sizes=\"(max-width: 696px) 100vw, 696px\" \/><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">Particle Spawn<\/h4>\n\n\n\n<p>The <strong>Initialize Particle<\/strong> module sets the particle&#8217;s <strong>Mass<\/strong> randomly. The <strong>Calculate Size and Rotational Inertia by Mass<\/strong> module then sets the mesh&#8217;s scale (and rotational inertia) based on the mass value and various settings. (The size is calculated based on mass and density, so this module adjusts them accordingly.)<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"692\" height=\"482\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_04.png\" alt=\"\" class=\"wp-image-486\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_04.png 692w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_04-300x209.png 300w\" sizes=\"(max-width: 692px) 100vw, 692px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"695\" height=\"252\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_05.png\" alt=\"\" class=\"wp-image-487\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_05.png 695w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_05-300x109.png 300w\" sizes=\"(max-width: 695px) 100vw, 695px\" \/><\/figure>\n\n\n\n<p>This adds some randomness to the mesh size based on the randomly set mass.<\/p>\n\n\n\n<p>The particles are spawned at random positions within a sphere using the <strong>Sphere Location<\/strong> module.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Particle Update<\/h4>\n\n\n\n<p>Three attributes, <strong>CollisionRadius (float)<\/strong>, <strong>Unyielding (bool)<\/strong>, and <strong>Previous Position (Vector)<\/strong>, are initialized. These attributes are used later in the <strong>PBD Particle Collision<\/strong> module.<\/p>\n\n\n\n<ul>\n<li><strong>CollisionRadius<\/strong> is calculated using the <strong>Calculate Particle Radius<\/strong> dynamic input. It\u2019s not too complex; it calculates the radius by mesh dimensions and the particle&#8217;s scale attribute based on the <strong>Method for Calculation Particle Radius<\/strong>.\n<ul>\n<li>This time, since the Method is Minimum Axis and Mesh Dimensions is (2.0, 2.0, 2.0), the value of 2.0 \/ 2 * Scale will be used for the CollisionRadius.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Previous Position<\/strong> is set to the attribute added by the <strong>Solve Forces and Velocity<\/strong> module.<\/li>\n\n\n\n<li><strong>Unyielding<\/strong> is set to <strong>false<\/strong>.<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"694\" height=\"221\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_06.png\" alt=\"\" class=\"wp-image-488\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_06.png 694w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_06-300x96.png 300w\" sizes=\"(max-width: 694px) 100vw, 694px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"301\" height=\"125\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_07.png\" alt=\"\" class=\"wp-image-489\"\/><\/figure>\n\n\n\n<p>The rest of the settings are not very complicated. A movement is created by the Point Attraction Force, which pulls towards the center.<\/p>\n\n\n\n<p>The Collision here is just to enable collisions with other objects, so it is not related to PBD (Position-Based Dynamics).<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"692\" height=\"528\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_08.png\" alt=\"\" class=\"wp-image-490\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_08.png 692w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_08-300x229.png 300w\" sizes=\"(max-width: 692px) 100vw, 692px\" \/><\/figure>\n\n\n\n<p>To briefly explain, since this sample uses GPU Simulation, three methods can be used for collision detection:<\/p>\n\n\n\n<ol>\n<li><strong>GPU Depth Buffer<\/strong>: This uses the Scene Depth information for collision detection.<\/li>\n\n\n\n<li><strong>GPU Distance Fields<\/strong>: This uses the Mesh Distance Fields information for collision detection. Mesh Distance Fields  stores the distance from each voxel (coordinate in the world space) to the closest mesh. This topic can get quite deep, so researching it further might be interesting.<\/li>\n\n\n\n<li><strong>Analytical Planes<\/strong>: This method detects collisions only with user-defined infinite planes.<\/li>\n<\/ol>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"145\" height=\"79\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_09.png\" alt=\"\" class=\"wp-image-491\"\/><\/figure>\n\n\n\n<p>Finally, there&#8217;s the <strong>DynamicMaterialParameter<\/strong> set, but it\u2019s not being used.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Populate Grid<\/h4>\n\n\n\n<p>In this simulation stage, the execution index of each particle is saved to the <strong>Neighbor Grid3D<\/strong>.<\/p>\n\n\n\n<p>The <strong>Populate Neighbor Grid<\/strong> used here is an existing module. <a rel=\"noreferrer noopener\" href=\"https:\/\/heyyocg.link\/ue4-26-niagara-advanced-neighbor-grid-3d-basic\/\" data-type=\"URL\" data-id=\"https:\/\/heyyocg.link\/ue4-26-niagara-advanced-neighbor-grid-3d-basic\/\" target=\"_blank\">In a previous article that explained Neighbor Grid3D<\/a>, these operations were implemented using the Scratch Pad module, but it was already available as a dedicated module.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"694\" height=\"116\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_10.png\" alt=\"\" class=\"wp-image-492\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_10.png 694w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_10-300x50.png 300w\" sizes=\"(max-width: 694px) 100vw, 694px\" \/><\/figure>\n\n\n\n<p>However, since the attributes set by the <strong>Initialize Neighbor Grid3D<\/strong> module are used as input values, if you&#8217;re not using that module, you need to manually provide those values.<\/p>\n\n\n\n<p>Since the internals are the same, I\u2019ll skip the detailed explanation here.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Particle Collisions<\/h4>\n\n\n\n<p>Now we get to the core part.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"690\" height=\"217\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_11.png\" alt=\"\" class=\"wp-image-495\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_11.png 690w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_11-300x94.png 300w\" sizes=\"(max-width: 690px) 100vw, 690px\" \/><\/figure>\n\n\n\n<p>First, the number of iterations is set to 4. In a single pass, it\u2019s possible that, after adjusting the overlap with other particles, a new overlap could occur with a different particle. By repeating the process multiple times, the adjustments become more accurate. Of course, this comes with a trade-off in performance.<\/p>\n\n\n\n<p>As for the PBD process in <strong>PBD Particle Collision<\/strong>, this is also an existing module. However, since it&#8217;s experimental, it won\u2019t show up in search results unless you uncheck the <strong>Library Only<\/strong> option, so be aware of that.<\/p>\n\n\n\n<p>The input values are as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"691\" height=\"385\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_12.png\" alt=\"\" class=\"wp-image-496\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_12.png 691w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_12-300x167.png 300w\" sizes=\"(max-width: 691px) 100vw, 691px\" \/><\/figure>\n\n\n\n<p>The input values are as follows:<\/p>\n\n\n\n<p>We&#8217;ll take a look at the details shortly, but honestly, it still feels like it&#8217;s under development.<\/p>\n\n\n\n<p>In fact, <strong>KinectFriction<\/strong> and <strong>StaticFriction<\/strong> are not functional yet, so changing their values won&#8217;t have any effect.<\/p>\n\n\n\n<p><strong>RelaxationAmount<\/strong>: Increasing this value weakens the PBD effect, making the collisions feel more fluid. A value of 1.0 is usually fine.<\/p>\n\n\n\n<p><strong>Simulate flag<\/strong>: This flag enables or disables PBD.<\/p>\n\n\n\n<p>The other values are predefined, so there\u2019s no need to change them.<\/p>\n\n\n\n<p>Let\u2019s look inside; it\u2019s implemented in HLSL.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"406\" height=\"792\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_14.png\" alt=\"\" class=\"wp-image-497\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_14.png 406w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_14-154x300.png 154w\" sizes=\"(max-width: 406px) 100vw, 406px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"827\" height=\"550\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_13.png\" alt=\"\" class=\"wp-image-498\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_13.png 827w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_13-300x200.png 300w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_13-768x511.png 768w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_13-816x543.png 816w\" sizes=\"(max-width: 827px) 100vw, 827px\" \/><\/figure>\n\n\n\n<p>Ultimately, the values are updated as shown below, but in this sample, only <strong>Position<\/strong> and <strong>Velocity<\/strong> are used.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"709\" height=\"372\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_15.png\" alt=\"\" class=\"wp-image-499\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_15.png 709w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/pbd_basic_15-300x157.png 300w\" sizes=\"(max-width: 709px) 100vw, 709px\" \/><\/figure>\n\n\n\n<p>Before we dive into the HLSL code, let me first explain the premise.<\/p>\n\n\n\n<p><strong>How PBD is Achieved<\/strong><\/p>\n\n\n\n<p>In this method, particles in the 3x3x3=27 cells (including the particle\u2019s own cell) adjacent to the cell it belongs to in the <strong>Neighbor Grid3D<\/strong> are checked for overlap using the pre-set <strong>CollisionRadius<\/strong>. This is done by performing a brute-force search through all the particles.<\/p>\n\n\n\n<p>When an overlap is detected, the length of the overlap is calculated, and a weighted distance is applied based on the masses of the two particles. The position of the particle is then adjusted in the opposite direction, and the velocity is also updated in that direction.<\/p>\n\n\n\n<p>This process is repeated 4 times in a single frame to achieve PBD.<\/p>\n\n\n\n<p><strong>The &#8220;Unyielding&#8221; Flag<\/strong><\/p>\n\n\n\n<p>There\u2019s also a flag called <strong>Unyielding<\/strong>. When this flag is set to true for a particle, if it overlaps with a particle that has this flag set to false, the true particle will not move. Only the false particle will have its position adjusted. (More specifically, you can control how much a particle resists movement by setting the <strong>UnyieldingMassPercentage<\/strong>.)<\/p>\n\n\n\n<p>This allows for different collision behaviors for each particle. However, in this sample, all particles have it set to false, so there\u2019s no difference in behavior between them.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"480\" height=\"281\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/FeatureSample_4_26-Preview-NetMode_-Standalone-64-bit_Windows-2021-02-11-19-23-00.gif\" alt=\"\" class=\"wp-image-500\"\/><figcaption class=\"wp-element-caption\">Red represents <strong>Unyielding=true<\/strong>, and blue represents <strong>false<\/strong>. The <strong>UnyieldingMassPercentage<\/strong> is set to 1.0.<\/figcaption><\/figure>\n\n\n\n<p>With that in mind, let\u2019s take a look inside the HLSL code.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Initialize variables for output\nOutPosition = Position;\nOutVelocity = Velocity;\nCollidesOut = CollidesIn;\nNumberOfNeighbors = 0;\ndisplayCount = 0.0;\n\n\/\/ AveragedCenterLocation = float3 (0.0, 0.0, 0.0);\n\n#if GPU_SIMULATION\n\n\/\/ Define the index offsets to access the 27 adjacent cells, including the current one\nconst int3 IndexOffsets&#91;27] = \n{\n    int3(-1,-1,-1),\n    int3(-1,-1, 0),\n    int3(-1,-1, 1),\n    int3(-1, 0,-1),\n    int3(-1, 0, 0),\n    int3(-1, 0, 1),\n    int3(-1, 1,-1),\n    int3(-1, 1, 0),\n    int3(-1, 1, 1),\n\n    int3(0,-1,-1),\n    int3(0,-1, 0),\n    int3(0,-1, 1),\n    int3(0, 0,-1),\n    int3(0, 0, 0),\n    int3(0, 0, 1),\n    int3(0, 1,-1),\n    int3(0, 1, 0),\n    int3(0, 1, 1),\n\n    int3(1,-1,-1),\n    int3(1,-1, 0),\n    int3(1,-1, 1),\n    int3(1, 0,-1),\n    int3(1, 0, 0),\n    int3(1, 0, 1),\n    int3(1, 1,-1),\n    int3(1, 1, 0),\n    int3(1, 1, 1),\n};\n\n\/\/ Convert position to grid space coordinates\nfloat3 UnitPos;\nmyNeighborGrid.SimulationToUnit(Position, SimulationToUnit, UnitPos);\n\n\/\/ Get the index of the cell the particle belongs to\nint3 Index;\nmyNeighborGrid.UnitToIndex(UnitPos, Index.x, Index.y, Index.z);\n\n\/\/ Define variables for intermediate use\nfloat3 FinalOffsetVector = {0,0,0};\nuint ConstraintCount = 0;\nfloat TotalMassPercentage = 1.0;\n\n\/\/ Get the number of cells in the grid\nint3 NumCells;\nmyNeighborGrid.GetNumCells(NumCells.x, NumCells.y, NumCells.z);\n\n\/\/ Get the maximum number of neighbors per cell\nint MaxNeighborsPerCell;\nmyNeighborGrid.MaxNeighborsPerCell(MaxNeighborsPerCell);\n\n\/\/ Search the 27 adjacent cells\nfor (int xxx = 0; xxx &lt; 27; ++xxx) \n{\n    \/\/ For each cell, search MaxNeighborsPerCell times\n    for (int i = 0; i &lt; MaxNeighborsPerCell; ++i)\n    {\n        \/\/ Get the index of the particle stored in the cell\n        const int3 IndexToUse = Index + IndexOffsets&#91;xxx];\n        int NeighborLinearIndex;\n        myNeighborGrid.NeighborGridIndexToLinear(IndexToUse.x, IndexToUse.y, IndexToUse.z, i, NeighborLinearIndex);\n\n        \/\/ Get the Execution Index of the particle based on NeighborLinearIndex\n        int CurrNeighborIdx;\n        myNeighborGrid.GetParticleNeighbor(NeighborLinearIndex, CurrNeighborIdx);\n\n        \/\/ Get the position of the particle using the Execution Index\n        bool myBool; \n        float3 OtherPos;\n        DirectReads.GetVectorByIndex&lt;Attribute=\"Position\"&gt;(CurrNeighborIdx, myBool, OtherPos);\n        \n        \/\/ Calculate the distance and direction between this particle and the target particle\n        const float3 vectorFromOtherToSelf = Position - OtherPos;\n        const float dist = length(vectorFromOtherToSelf);\n        const float3 CollisionNormal = vectorFromOtherToSelf \/ dist;\n\n        \/\/ Get the CollisionRadius of the target particle\n        float OtherRadius;\n        DirectReads.GetFloatByIndex&lt;Attribute=\"CollisionRadius\"&gt;(CurrNeighborIdx, myBool, OtherRadius);\n\n        \/\/ Calculate if there's an overlap (if Overlap &gt;= 0, they are overlapping)\n        float Overlap = (CollisionRadius + OtherRadius) - dist;\n\n        \/\/ Check for valid indices\n        if (IndexToUse.x &gt;= 0 &amp;&amp; IndexToUse.x &lt; NumCells.x &amp;&amp; \n            IndexToUse.y &gt;= 0 &amp;&amp; IndexToUse.y &lt; NumCells.y &amp;&amp; \n            IndexToUse.z &gt;= 0 &amp;&amp; IndexToUse.z &lt; NumCells.z &amp;&amp; \n            CurrNeighborIdx != InstanceIdx &amp;&amp; CurrNeighborIdx != -1 &amp;&amp; dist &gt; 1e-5)\n        {\n            \/\/ Initialize variables\n            bool otherUnyielding = false;\n            TotalMassPercentage = 1.0;\n\n            \/\/ Process only if there's an overlap\n            if (Overlap &gt; 1e-5)\n            {\n                NumberOfNeighbors += 1;\n                displayCount = NumberOfNeighbors;\n\n                \/\/ Get the Unyielding flag of the target particle\n                bool NeighborUnyieldResults;\n                DirectReads.GetBoolByIndex&lt;Attribute=\"Unyielding\"&gt;(CurrNeighborIdx, myBool, NeighborUnyieldResults);\n\n                CollidesOut = true;\n\n                \/\/ Get the mass of the target particle\n                float OtherMass;\n                DirectReads.GetFloatByIndex&lt;Attribute=\"Mass\"&gt;(CurrNeighborIdx, myBool, OtherMass);\n\n                \/\/ Calculate a weighting factor based on the masses of the two particles (this will be used later for adjusting the movement)\n                TotalMassPercentage = Mass \/ (Mass + OtherMass); \n\n                \/\/ If both particles are unyielding, do nothing\n                if (NeighborUnyieldResults &amp;&amp; Unyielding) { \/\/ both this particle and the other are unyielding\n                    TotalMassPercentage = TotalMassPercentage; \n                }\n                \/\/ If only the target particle is unyielding, adjust the TotalMassPercentage closer to 0\n                else if (NeighborUnyieldResults) { \/\/ this particle yields but the other does not\n                    TotalMassPercentage = lerp(TotalMassPercentage, 0.0, UnyieldingMassPercentage);\n                }\n                \/\/ If this particle is unyielding, adjust the TotalMassPercentage closer to 1\n                else if (Unyielding) { \/\/ this particle is unyielding but the other is\n                    TotalMassPercentage = lerp(TotalMassPercentage, 1.0, UnyieldingMassPercentage);\n                }\n\n                \/\/ Add the weighted overlap distance to the final offset vector\n                FinalOffsetVector += (1.0 - TotalMassPercentage) * Overlap * CollisionNormal; \n                \n                \/\/ This part seems to be calculating momentum, but it\u2019s not used currently. It might be used when friction is implemented.\n                float3 OtherPreviousPos;\n                DirectReads.GetVectorByIndex&lt;Attribute=\"Previous.Position\"&gt;(CurrNeighborIdx, myBool, OtherPreviousPos);\n                const float3 P0Velocity = Position - PreviousPosition;\n                const float3 PNVelocity = OtherPos - OtherPreviousPos;\n                const float3 P0RelativeMomentum = P0Velocity * Mass - PNVelocity * OtherMass;\n                const float3 NormalBounceVelocity = (P0RelativeMomentum - dot(P0RelativeMomentum, CollisionNormal) * CollisionNormal) \/ Mass;\n                const float NormalBounceVelocityMag = length(NormalBounceVelocity);\n                \n                \/\/ Record how many particles were overlapping\n                ConstraintCount += 1;\n            }\n        }      \n    }\n}\n\n\/\/ If there is an overlap and IsMobile (= input value of Simulate) is true\nif (ConstraintCount &gt; 0 &amp;&amp; IsMobile)\n{\n    \/\/ Add the adjusted position vector, divided by the number of constraints and RelaxationAmount (biased average value), to the current position\n    OutPosition += 1.0 * FinalOffsetVector \/ (ConstraintCount * RelaxationAmount);\n\n    \/\/ Add friction support here (friction might be implemented here)\n\n    \/\/ Update velocity based on the adjusted position and the previous position\n    OutVelocity = (OutPosition - PreviousPosition) * InvDt;\n\n    \/\/ If Unyielding is true, set velocity to zero\n    if (Unyielding) {\n        \/\/ May want to make this return to the previous velocity\n        OutVelocity = float3(0, 0, 0);\n    }\n}\n\n#endif\n<\/code><\/pre>\n\n\n\n<p>I\u2019ve included some explanations in the comments for you to be able to understand by reading them. However, since this is still a work in progress, I expect the implementation to change in future versions.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>I honestly think it\u2019s a great evolution that particle-to-particle collisions can now be detected.<\/p>\n\n\n\n<p>As for performance, during the Popcorn Man demo, they mentioned that the processing time for this part was under 1ms, so with proper adjustments, it seems like it could be practical for realtime use.<\/p>\n\n\n\n<p>Regarding PBD, there is another sample available, so I hope to introduce it in a separate article.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Epic Games distributes a project called \u201cContent Examples,\u201d which is a collection of various sample projects showcasing different features of Unreal Engine. Since UE4.26, there is a map called&nbsp;Niagara Advanced. This map contains a wealth of useful samples, including examples of new Niagara features and practical applications. Upon reviewing the implementations, there\u2019s a lot to [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":504,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_lmt_disableupdate":"no","_lmt_disable":"","_mi_skip_tracking":false,"footnotes":"","_locale":"en_US","_original_post":"http:\/\/heyyocg.link\/?p=460"},"categories":[17,31],"tags":[32,15,30],"modified_by":"yohanashima","_links":{"self":[{"href":"https:\/\/heyyocg.link\/wp-json\/wp\/v2\/posts\/1481"}],"collection":[{"href":"https:\/\/heyyocg.link\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/heyyocg.link\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/heyyocg.link\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/heyyocg.link\/wp-json\/wp\/v2\/comments?post=1481"}],"version-history":[{"count":9,"href":"https:\/\/heyyocg.link\/wp-json\/wp\/v2\/posts\/1481\/revisions"}],"predecessor-version":[{"id":1515,"href":"https:\/\/heyyocg.link\/wp-json\/wp\/v2\/posts\/1481\/revisions\/1515"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/heyyocg.link\/wp-json\/wp\/v2\/media\/504"}],"wp:attachment":[{"href":"https:\/\/heyyocg.link\/wp-json\/wp\/v2\/media?parent=1481"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/heyyocg.link\/wp-json\/wp\/v2\/categories?post=1481"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/heyyocg.link\/wp-json\/wp\/v2\/tags?post=1481"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}