{"id":1511,"date":"2024-11-21T00:23:41","date_gmt":"2024-11-20T15:23:41","guid":{"rendered":"https:\/\/heyyocg.link\/?p=1511"},"modified":"2024-11-21T00:23:42","modified_gmt":"2024-11-20T15:23:42","slug":"ue4-26-niagara-advanced-neighbor-grid-3d-basic","status":"publish","type":"post","link":"https:\/\/heyyocg.link\/en\/ue4-26-niagara-advanced-neighbor-grid-3d-basic\/","title":{"rendered":"Niagara Advanced Guide &#8211; Neighbor Grid3D"},"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>With Neighbor Grid3D, you can find the Index or ID of nearby particles.<\/li>\n\n\n\n<li>The basic usage of Neighbor Grid3D is as follows:\n<ul>\n<li>Define a 3D grid.<\/li>\n\n\n\n<li>Write the ID or Index in the cell where your (Particle&#8217;s) position is located.<\/li>\n\n\n\n<li>Retrieve the ID or Index stored in the cell where your particle is located, as well as in the neighboring cells.<\/li>\n\n\n\n<li>Use the Particle Attribute Reader to obtain and utilize information about the particle corresponding to that ID\/Index.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p>This article will explain Neighbor Grid3D. Although the feature itself has been available since version 4.25, many samples were added to the &#8220;Content Examples&#8221; starting from version 4.26.<\/p>\n\n\n\n<p>From those samples, we will focus on and explain the following three: &#8220;3.1 Color Copy by Cell\u300d&#8221;, &#8220;3.2 Dynamic Grid Transform&#8221;, &#8220;3.3 Max Neighbors Per Cell&#8221;.<\/p>\n\n\n\n<p>Additionally, when using Neighbor Grid3D, you will typically use it in conjunction with the Particle Attribute Reader and the Simulation Stage. If you&#8217;re unfamiliar with these, it&#8217;s recommended that you read 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=\"tyzcmaOiri\"><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=NEyH5xN4Vf#?secret=tyzcmaOiri\" data-secret=\"tyzcmaOiri\" 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=\"VZxwmTrvsu\"><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=BReDfRBC6Q#?secret=VZxwmTrvsu\" data-secret=\"VZxwmTrvsu\" width=\"500\" height=\"282\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe>\n<\/div><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">What is Neighbor Grid3D?<\/h2>\n\n\n\n<p>Before diving into the detailed explanation, let&#8217;s briefly summarize the concept.<\/p>\n\n\n\n<p>With this feature, you can define a 3D grid of arbitrary size and store the Particle ID or Execution Index in each cell of the grid.<\/p>\n\n\n\n<p>For example, if you define a 3x3x3 grid, there could be particles with IDs 2, 15, 98 in the cell (1, 0, 0), and particles with IDs 12 and 101 in the cell (2, 2, 2).<\/p>\n\n\n\n<p>This allows you to know the IDs of particles that are stored in the cell where your particle is located or in its neighboring cells (i.e., nearby particles). If you know the IDs, you can use the Particle Attribute Reader to fetch information like color and other attributes of those particles.<\/p>\n\n\n\n<p>Currently, this feature is only supported in GPU simulation.<\/p>\n\n\n\n<p>Now, let\u2019s look at the specific implementation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3.1 Color Copy by Cell<\/h2>\n\n\n\n<figure class=\"wp-block-image size-large\"><img 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-34-48.gif\" alt=\"\" class=\"wp-image-540\"\/><\/figure>\n\n\n\n<p>This sample involves both large and small particles. The large particles are randomly placed with random colors, and the small particles are generated at random positions. They then find the nearest large particle within the same cell (if there are multiple, the closest one) and copy its color.<\/p>\n\n\n\n<p>The light green box visualizes the 2x2x2 Neighbor Grid3D, which is implemented within the same Niagara System. However, this visualization is not directly related to the core explanation, so we will not discuss it here.<\/p>\n\n\n\n<p>Let\u2019s first look at the part where Neighbor Grid3D is defined.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"261\" height=\"239\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_02.png\" alt=\"\" class=\"wp-image-428\"\/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"609\" height=\"416\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_03.png\" alt=\"\" class=\"wp-image-429\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_03.png 609w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_03-300x205.png 300w\" sizes=\"(max-width: 609px) 100vw, 609px\" \/><\/figure>\n\n\n\n<p>It is defined in the System Update, as it will be used by multiple Emitters. Since it is defined in the Update, it gets rebuilt every frame.<\/p>\n\n\n\n<p>The &#8220;Max Neighbors Per Cell&#8221; refers to the number of particles that can be stored in each cell. We\u2019ll look at this in more detail in section 3.3 &#8220;Max Neighbors Per Cell.&#8221;<\/p>\n\n\n\n<p>Before we proceed, let\u2019s first explain the &#8220;Set Resolution Method.&#8221; There are three options available in the dropdown: Independent, Max Axis, and Cell Size. Independent corresponds to Num Cells, Max Axis corresponds to Num Cells Max Axis, and Cell Size corresponds to Cell Size. The other options are disabled based on your selection.<\/p>\n\n\n\n<p>This setting determines the number and size of the cells. Num Cells directly defines the number of cells in each dimension, and the cell size is determined by dividing the World BBox Size by the number of cells. Conversely, if you select Cell Size, the cell size is defined, and the number of cells is determined by dividing the World BBox Size by the cell size. Max Axis appears to correspond to setting the number of cells in the dimension with the largest World BBox Size, which then determines the cell size.<\/p>\n\n\n\n<p>In this example, a 2x2x2 grid with each side length of 75 is defined.<\/p>\n\n\n\n<p>Next, let&#8217;s look at the Emitter for the large particles, which will store information in the Neighbor Grid3D.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"414\" height=\"405\" src=\"http:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/ColorQuery-2021-02-10-18-54-30_1_1.gif\" alt=\"\" class=\"wp-image-541\"\/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"245\" height=\"520\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_05.png\" alt=\"\" class=\"wp-image-433\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_05.png 245w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_05-141x300.png 141w\" sizes=\"(max-width: 245px) 100vw, 245px\" \/><\/figure>\n\n\n\n<p>The particles are placed at random positions inside a box and with random colors using the Box Location module.<\/p>\n\n\n\n<p>The key point here is that the &#8220;Fill Grid&#8221; Simulation Stage is used. The Iteration Source is set to &#8220;Particle,&#8221; so the process runs for every particle. The process itself is implemented with the &#8220;Fill Neighbor Grid 3D&#8221; Scratch Pad module, which writes the Execution Index of the particle into the appropriate cell in the Neighbor Grid3D.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"615\" height=\"206\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_06.png\" alt=\"\" class=\"wp-image-434\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_06.png 615w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_06-300x100.png 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"638\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_07-1024x638.png\" alt=\"\" class=\"wp-image-435\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_07-1024x638.png 1024w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_07-300x187.png 300w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_07-768x478.png 768w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_07-816x508.png 816w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_07.png 1407w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>The processing is primarily done in HLSL, and I will provide a brief explanation with comments on the HLSL code.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Result of whether the particle was successfully added to the grid\r\nAddedToGrid = false;\r\n\r\n\/\/ Ensuring that it's GPU simulation\r\n#if GPU_SIMULATION\r\n\r\n\/\/ Convert the particle's world position to the Neighbor Grid3D coordinate system (explained later)\r\nfloat3 UnitPos;\r\nNeighborGrid.SimulationToUnit(Position, SimulationToUnit, UnitPos);\r\n\r\n\/\/ Get the 3D index of the cell from the UnitPos\r\nint3 Index;\r\nNeighborGrid.UnitToIndex(UnitPos, Index.x, Index.y, Index.z);\r\n\r\n\/\/ Get the number of cells in the Neighbor Grid3D\r\nint3 NumCells;\r\nNeighborGrid.GetNumCells(NumCells.x, NumCells.y, NumCells.z);\r\n\r\n\/\/ Ensure the index is valid\r\nif (Index.x >= 0 &amp;&amp; Index.x &lt; NumCells.x &amp;&amp; \r\n    Index.y >= 0 &amp;&amp; Index.y &lt; NumCells.y &amp;&amp; \r\n    Index.z >= 0 &amp;&amp; Index.z &lt; NumCells.z)\r\n{\r\n    \/\/ Convert the 3D index to a linear index\r\n    int LinearIndex;\r\n    NeighborGrid.IndexToLinear(Index.x, Index.y, Index.z, LinearIndex);\r\n\r\n    \/\/ Get the number of particles in this cell and increment it\r\n    int PreviousNeighborCount;\r\n    NeighborGrid.SetParticleNeighborCount(LinearIndex, 1, PreviousNeighborCount);\r\n\r\n    \/\/ Get the maximum number of particles that can be stored in the cell\r\n    int MaxNeighborsPerCell;\r\n    NeighborGrid.MaxNeighborsPerCell(MaxNeighborsPerCell);\r\n\r\n    \/\/ If the number of particles in the cell is less than the max, store the particle's info\r\n    if (PreviousNeighborCount &lt; MaxNeighborsPerCell)\r\n    {\r\n        AddedToGrid = true;\r\n\r\n        \/\/ Get the index of the particle in the cell's array\r\n        int NeighborGridLinear;\r\n        NeighborGrid.NeighborGridIndexToLinear(Index.x, Index.y, Index.z, PreviousNeighborCount, NeighborGridLinear);\r\n\r\n        \/\/ Save the Execution Index at the specified position\r\n        int IGNORE;\r\n        NeighborGrid.SetParticleNeighbor(NeighborGridLinear, ExecIndex, IGNORE);\r\n    }\t\t\r\n}\r\n#endif\r<\/code><\/pre>\n\n\n\n<p>I think most of it can be understood by reading the comments, but I will provide an explanation for one point, which I indicated as &#8220;to be explained later.&#8221;<\/p>\n\n\n\n<p>The function <code>NeighborGrid.SimulationToUnit(Position, SimulationToUnit, UnitPos)<\/code> converts the world position of the particle into the Neighbor Grid3D coordinate system (with values between 0 and 1).<\/p>\n\n\n\n<p>Here, <code>Position<\/code> is the world position of the particle, and <code>SimulationToUnit<\/code> appears to be a matrix created using the World BBox Size of the Neighbor Grid3D. This is a standard code pattern, so you can likely copy and paste it.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"864\" height=\"767\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_08.png\" alt=\"\" class=\"wp-image-437\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_08.png 864w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_08-300x266.png 300w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_08-768x682.png 768w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_08-816x724.png 816w\" sizes=\"(max-width: 864px) 100vw, 864px\" \/><\/figure>\n\n\n\n<p>This allows us to store the Execution Index of the large particle in the grid.<\/p>\n\n\n\n<p>Next, let&#8217;s look at the Emitter that searches the grid for the nearest large particle and copies its color.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"257\" height=\"565\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_09.png\" alt=\"\" class=\"wp-image-438\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_09.png 257w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_09-136x300.png 136w\" sizes=\"(max-width: 257px) 100vw, 257px\" \/><\/figure>\n\n\n\n<p>Key modules to note are the &#8220;Particle Attribute Reader&#8221; and the &#8220;Query Grid&#8221; Simulation Stage.<\/p>\n\n\n\n<p>For the Particle Attribute Reader, the target particles are the large particles, so the Emitter Name is set to &#8220;Grid_Write.&#8221;<\/p>\n\n\n\n<p>&#8220;Query Grid&#8221; uses the particles as the Iteration Source, and the process involves two Scratch Pad modules.<\/p>\n\n\n\n<p>In &#8220;Find Closest Neighbor,&#8221; the closest large particle in the same cell is found, and its Execution Index is retrieved. Then, using this, the color is copied in the &#8220;Copy Color&#8221; module.<\/p>\n\n\n\n<p>Let\u2019s look at &#8220;Find Closest Neighbor&#8221; in more detail.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"989\" height=\"793\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_10.png\" alt=\"\" class=\"wp-image-439\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_10.png 989w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_10-300x241.png 300w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_10-768x616.png 768w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_10-816x654.png 816w\" sizes=\"(max-width: 989px) 100vw, 989px\" \/><\/figure>\n\n\n\n<p>This too is written primarily in HLSL. At the end, the retrieved Execution Index is written to the Neighbor Index attribute.<\/p>\n\n\n\n<p>Here\u2019s the explanation of the HLSL code, with comments:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ The Execution Index of the closest particle is stored here. If no particle is found in the same cell, it outputs -1.\r\nNeighborIndex = -1;\r\n\r\n#if GPU_SIMULATION\r\n\r\nbool Valid;\r\n\r\nfloat3 UnitPos;\r\nNeighborGrid.SimulationToUnit(Position, SimulationToUnit, UnitPos);\r\n\r\n\/\/ Get the 3D index of the particle's cell\r\nint3 Index;\r\nNeighborGrid.UnitToIndex(UnitPos, Index.x, Index.y, Index.z);\r\n\r\n\/\/ Initialize the closest distance with a very large value\r\nfloat neighbordist = 3.4e+38;\r\n\r\nint MaxNeighborsPerCell;\r\nNeighborGrid.MaxNeighborsPerCell(MaxNeighborsPerCell);\r\n\r\n\/\/ Loop through the maximum number of neighbors in the cell\r\nfor (int i = 0; i &lt; MaxNeighborsPerCell; ++i)\r\n{\r\n    int NeighborLinearIndex;\r\n    NeighborGrid.NeighborGridIndexToLinear(Index.x, Index.y, Index.z, i, NeighborLinearIndex);\r\n\r\n    \/\/ Get the Execution Index of the neighbor particle\r\n    int CurrNeighborIdx;\r\n    NeighborGrid.GetParticleNeighbor(NeighborLinearIndex, CurrNeighborIdx);\r\n\r\n    \/\/ If no neighbor is found (Execution Index is -1), skip\r\n    if (CurrNeighborIdx != -1)\r\n    {\r\n        \/\/ Retrieve the position of the neighbor particle using the Particle Attribute Reader\r\n        float3 NeighborPos;\r\n        AttributeReader.GetVectorByIndex&lt;Attribute=\"Position\">(CurrNeighborIdx, Valid, NeighborPos);\r\n\r\n        \/\/ Compare the distance to find the closest particle\r\n        const float3 delta = Position - NeighborPos;\r\n        const float dist = length(delta);\r\n\r\n        \/\/ If a closer particle is found, update the Execution Index\r\n        if( dist &lt; neighbordist )\r\n        {\r\n            neighbordist = dist;\r\n            NeighborIndex = CurrNeighborIdx;\r\n        }\r\n    }  \r\n}    \r\n\r\n#endif\r<\/code><\/pre>\n\n\n\n<p>Now we have retrieved the Execution Index of the closest large particle in the same cell and stored it in the Neighbor Index attribute. If there are no large particles in the same cell, it holds -1.<\/p>\n\n\n\n<p>Finally, the color copy process is straightforward, using the Particle Attribute Reader to read the color and copy it.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"322\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_11-1024x322.png\" alt=\"\" class=\"wp-image-441\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_11-1024x322.png 1024w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_11-300x94.png 300w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_11-768x241.png 768w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_11-816x256.png 816w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_11.png 1435w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>This concludes the explanation of this sample. Writing to and reading from the grid is the basic usage of Neighbor Grid3D.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3.2 Dynamic Grid Transform<\/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-06.gif\" alt=\"\" class=\"wp-image-542\"\/><\/figure>\n\n\n\n<p>In this sample, a light green grid visualizing the Neighbor Grid3D continuously rotates, and the red particles arranged on the grid expand slightly and turn green when they enter the Neighbor Grid3D. This demo demonstrates this behavior.<\/p>\n\n\n\n<p>Essentially, this is a sample showing that you can rotate the Neighbor Grid3D, and I will quickly explain how it works.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"211\" height=\"228\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_13.png\" alt=\"\" class=\"wp-image-445\"\/><\/figure>\n\n\n\n<p>This time, we initialize the Neighbor Grid3D within the System Update, but unlike the previous example, we use the \u201cInitialize Neighbor Grid\u201d node. By using this node, we can set the position and rotation of the grid.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"646\" height=\"740\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_14.png\" alt=\"\" class=\"wp-image-446\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_14.png 646w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_14-262x300.png 262w\" sizes=\"(max-width: 646px) 100vw, 646px\" \/><\/figure>\n\n\n\n<p>You can adjust the size with Grid Extents and set the pivot position with Local Pivot. I experimented a bit, but it seems like only the Independent option works for the Resolution Method. In other words, by setting the Num Cells, you decide the number of cells in the grid.<\/p>\n\n\n\n<p>With Transformed Inputs, you can set the position with Offset and rotation with Rotation. Here, it is set to rotate according to the Age on a specific axis, which allows for continuous rotation.<\/p>\n\n\n\n<p>Another advantage of using Initialize Neighbor Grid is that it writes various grid-related information to Attributes.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"469\" height=\"374\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_15.png\" alt=\"\" class=\"wp-image-447\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_15.png 469w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_15-300x239.png 300w\" sizes=\"(max-width: 469px) 100vw, 469px\" \/><\/figure>\n\n\n\n<p>In fact, by using the WorldToGridUnit inside, we determine whether a particle is inside the Neighbor Grid3D or not.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"322\" height=\"450\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_16.png\" alt=\"\" class=\"wp-image-448\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_16.png 322w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_16-215x300.png 215w\" sizes=\"(max-width: 322px) 100vw, 322px\" \/><\/figure>\n\n\n\n<p>The emitter places the red particles on the grid. The placement is handled with the \u201cSpawn Particles in Grid\u201d and \u201cGrid Location\u201d nodes.<\/p>\n\n\n\n<p>The expansion and color change to green when inside the Neighbor Grid3D is achieved using the DynamicMaterialParameter in the material.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"516\" height=\"165\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_17.png\" alt=\"\" class=\"wp-image-449\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_17.png 516w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_17-300x96.png 300w\" sizes=\"(max-width: 516px) 100vw, 516px\" \/><\/figure>\n\n\n\n<p>We use \u201cTransform Position by Matrix\u201d to convert the particle&#8217;s position to a normalized coordinate within the Neighbor Grid3D using WorldToGridUnit. This means that if the particle is inside the grid, its value will range from 0 to 1 in each dimension. These values are then input into the DynamicMaterialParameter\u2019s RGB.<\/p>\n\n\n\n<p>Although I will skip the details, in the material, it checks if the RGB values from the DynamicMaterialParameter fall within the range of 0 to 1, and controls the color and size accordingly.<\/p>\n\n\n\n<p>This concludes the explanation of this sample, but as a bonus, the material that visualizes the Neighbor Grid3D uses the unique \u201cSplineThicken\u201d node, so it might be interesting to check it out.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"390\" height=\"344\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_18.png\" alt=\"\" class=\"wp-image-450\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_18.png 390w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_18-300x265.png 300w\" sizes=\"(max-width: 390px) 100vw, 390px\" \/><figcaption class=\"wp-element-caption\">While experimenting, I came up with something interesting.<\/figcaption><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\">3.3 Max Neighbors Per Cell<\/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-24.gif\" alt=\"\" class=\"wp-image-543\"\/><\/figure>\n\n\n\n<p>This sample demonstrates the behavior when Max Neighbors Per Cell is set, and shows that the execution order of the Simulation Stage&#8217;s Execution Index is random (changing every frame).<\/p>\n\n\n\n<p>The content involves storing particles in the Neighbor Grid3D, and if the storage is successful, the particle turns white, while if it fails, it turns black. When Max Neighbors Per Cell is set to 1, only one particle can be stored in each cell, so storage fails in most cases (only the particle processed first in each cell succeeds), resulting in mostly black particles. However, when Max Neighbors Per Cell is set to 25, all particles can be stored successfully, and they turn white.<\/p>\n\n\n\n<p>By observing the GIF image, you can see that the particles turning white are swapped out every frame. This is due to the fact that the order of the particles being processed each frame is different, as mentioned earlier. When using the Execution Index in the Simulation Stage, it\u2019s important to keep this in mind.<\/p>\n\n\n\n<p>Now, let\u2019s look at the details.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"611\" height=\"488\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_20.png\" alt=\"\" class=\"wp-image-452\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_20.png 611w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_20-300x240.png 300w\" sizes=\"(max-width: 611px) 100vw, 611px\" \/><\/figure>\n\n\n\n<p>In this case, we are creating a Neighbor Grid3D as a User Parameter. After placing it in the level, you can set parameters like Max Neighbors Per Cell for each instance. Here, 100 is set arbitrarily.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"243\" height=\"668\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_21.png\" alt=\"\" class=\"wp-image-453\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_21.png 243w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_21-109x300.png 109w\" sizes=\"(max-width: 243px) 100vw, 243px\" \/><\/figure>\n\n\n\n<p>Next, for the emitter handling the process, not many modules are used.<\/p>\n\n\n\n<p>This is because this Niagara System is also used in the &#8220;3.4 Color Propagation&#8221; sample. It is managed with the \u201cShowGridParticles\u201d User Parameter flag to switch between the two.<\/p>\n\n\n\n<p>In this sample, the functionality removed from the image is not used.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"601\" height=\"234\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_22.png\" alt=\"\" class=\"wp-image-454\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_22.png 601w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_22-300x117.png 300w\" sizes=\"(max-width: 601px) 100vw, 601px\" \/><\/figure>\n\n\n\n<p>In the \u201cSet Color\u201d section, processing is divided based on the flag from ShowGridParticles. This sample corresponds to the \u201ctrue\u201d case, where the AddedToGrid attribute is used to color the particle white if true and black if false.<\/p>\n\n\n\n<p>As the name suggests, AddedToGrid is a flag indicating whether the particle was successfully stored in the Neighbor Grid3D.<\/p>\n\n\n\n<p>This flag is set within the Fill Grid simulation stage.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"596\" height=\"63\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_23.png\" alt=\"\" class=\"wp-image-455\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_23.png 596w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_23-300x32.png 300w\" sizes=\"(max-width: 596px) 100vw, 596px\" \/><\/figure>\n\n\n\n<p>It simply binds the output attribute of Fill Neighbor Grid 3D. Since Fill Neighbor Grid 3D is a Scratch Pad module, let\u2019s take a look inside.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"682\" src=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_24-1024x682.png\" alt=\"\" class=\"wp-image-456\" srcset=\"https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_24-1024x682.png 1024w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_24-300x200.png 300w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_24-768x511.png 768w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_24-816x543.png 816w, https:\/\/heyyocg.link\/wp-content\/uploads\/2021\/02\/neighbor_grid_3d_basic_24.png 1062w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>We\u2019ve seen this before. Yes, it\u2019s exactly the same as Fill Neighbor Grid 3D in \u201c3.1 Color Copy by Cell.\u201d In the \u201c3.1 Color Copy by Cell\u201d example, there was a filter processing based on the value of Max Neighbors Per Cell.<\/p>\n\n\n\n<p>Conversely, this means that it\u2019s only being filtered inside HLSL, so if you try to save more particles than the limit, it seems like you could do that, but performance might degrade.<\/p>\n\n\n\n<p>Anyway, this concludes the explanation of this sample. The \u201c3.4 Color Propagation\u201d part would be explained in a separate article.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>The functionality to search for particles near you has a wide range of applications, and it\u2019s also used in Position Based Dynamics, which I will explain next.<\/p>\n\n\n\n<p>At its core, it involves reading and writing to a grid, and the processing is mostly boilerplate code, so it might eventually become a default module. However, knowing how it behaves internally can be very useful, so I hope this article was helpful.<\/p>\n\n\n\n<p>Enjoy your CG life!<\/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":425,"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=422"},"categories":[17,31],"tags":[32,15,30],"modified_by":"yohanashima","_links":{"self":[{"href":"https:\/\/heyyocg.link\/wp-json\/wp\/v2\/posts\/1511"}],"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=1511"}],"version-history":[{"count":3,"href":"https:\/\/heyyocg.link\/wp-json\/wp\/v2\/posts\/1511\/revisions"}],"predecessor-version":[{"id":1514,"href":"https:\/\/heyyocg.link\/wp-json\/wp\/v2\/posts\/1511\/revisions\/1514"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/heyyocg.link\/wp-json\/wp\/v2\/media\/425"}],"wp:attachment":[{"href":"https:\/\/heyyocg.link\/wp-json\/wp\/v2\/media?parent=1511"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/heyyocg.link\/wp-json\/wp\/v2\/categories?post=1511"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/heyyocg.link\/wp-json\/wp\/v2\/tags?post=1511"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}