<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6481831191748590297</id><updated>2026-10-02T12:45:51.203+08:00</updated><category term="引擎研發"/><category term="技術分享"/><category term="日記"/><category term="感想"/><category term="經歷"/><title type='text'>SmallBurger</title><subtitle type='html'>SmallBurger focuses on mobile graphics technology and GPU-driven rendering solutions.&#xa;We explore practical techniques that make real-time graphics faster, lighter, and more scalable on mobile hardware — from rendering architecture and compute workflows to procedural effects.&#xa;&#xa;Our goal is simple: deliver visual quality and performance that mobile developers once thought impossible.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>260</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-6842997562985036926</id><published>2026-08-12T10:59:32.521+08:00</published><updated>2026-10-02T12:45:51.203+08:00</updated><title type='text'>Why Are Traditional Terrains Performance Killers on Mobile? Z-Terrain (Zero Deform Ultimate Mobile Landscape) Development Insights and Solutions</title><content type='html'>&lt;p&gt;For a long time, implementing expansive, open-world landscapes on mobile devices has been an extremely tricky task. Because running Unity&#39;s native Terrain system directly on mobile phones incurs completely unacceptable performance overhead, the industry&#39;s conventional compromise is to use plugins like Mesh Terrain Editor Free or Terrain To Mesh to convert the terrain into Meshes for rendering.&lt;/p&gt;

&lt;p&gt;However, this long-standing conventional pipeline hides three &quot;fatal pain points&quot; that cause the most headaches for R&amp;amp;D teams:&lt;/p&gt;

&lt;h2&gt;Pain Point 1: Unnecessary Waste of Geometry in Flat Areas (Vertex Pressure)&lt;/h2&gt;

&lt;p&gt;When traditional plugins convert native terrain to a Mesh, they typically can only use a naive, uniform subdivision algorithm. This leads to a very absurd phenomenon: a perfectly flat beach or grassy plain is assigned the exact same density of triangles and vertices as a steep cliff.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiD3-sHPJTiIPJ4Q9nx-IjcTDnit91DPh55lsAiRHhEFojeL3rk50rnpv77LmOSG3qhyUM1KAkzfw1xInaBXp1pPyn0REGcoa_VhJOCKqGgUH3BUGv5C7Ko5g-tDjcw-1VLoVsCR5RM83Wtwqe8s1Q4LLHji0aWEoL768Bn2VNxp7TbWEaAeDrDhXDvO28G&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;419&quot; data-original-width=&quot;640&quot; height=&quot;420&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiD3-sHPJTiIPJ4Q9nx-IjcTDnit91DPh55lsAiRHhEFojeL3rk50rnpv77LmOSG3qhyUM1KAkzfw1xInaBXp1pPyn0REGcoa_VhJOCKqGgUH3BUGv5C7Ko5g-tDjcw-1VLoVsCR5RM83Wtwqe8s1Q4LLHji0aWEoL768Bn2VNxp7TbWEaAeDrDhXDvO28G=w640-h420&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;These densely packed vertices, which offer zero visual contribution, needlessly waste the computational resources of the Vertex Shader on mobile GPUs, resulting in a severe geometric performance burden.&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;&lt;span&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/span&gt;Pain Point 2: Water Bodies and River &quot;Relocation Hell&quot; (Extremely High Late-Stage Maintenance Costs)&lt;/h2&gt;

&lt;p&gt;The traditional Terrain-to-Mesh pipeline is a &quot;destructive, irreversible&quot; one-way process. In large-scale scene development, terrain rarely exists in isolation; it is often deeply integrated with water systems like lakes, rivers, and waterfalls.&lt;/p&gt;

&lt;p&gt;When a Level Designer (LD) or Director casually says late in development: &quot;We need to expand the village here, move this river 50 meters to the left, and make the lake a bit larger,&quot; the nightmare for artists officially begins: They have to &#39;level the previous holes and dig new ones,&#39; and then re-export the Mesh.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhKcT8gtd42ElCB2eTyhYk_tyjflH2oSjIzzp8flQTpya7V-xOjipdnkLiN1AUbS12T_rGP-x1AIuatYE77awbq0J0rhwUlALiOBY-OB-L8S3bML3FjJxLC63cMFQmyqiyvJAjmTYOdzuhH7yk-nCmR9Vw2AtsqY438nu2eTkMuCIof-xp38e6P2zrrOHvZ&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;419&quot; data-original-width=&quot;640&quot; height=&quot;420&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhKcT8gtd42ElCB2eTyhYk_tyjflH2oSjIzzp8flQTpya7V-xOjipdnkLiN1AUbS12T_rGP-x1AIuatYE77awbq0J0rhwUlALiOBY-OB-L8S3bML3FjJxLC63cMFQmyqiyvJAjmTYOdzuhH7yk-nCmR9Vw2AtsqY438nu2eTkMuCIof-xp38e6P2zrrOHvZ=w640-h420&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Excavation and lining are required to construct a lake.&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;Pain Point 3: Transition Blending Between Cliffs (Scene Objects) and Terrain&lt;/h2&gt;

&lt;p&gt;Because traditional Terrain inherently suffers from vertical UV stretching issues, the system does not automatically add vertices (Vertices) on steep cliffs with extreme height differences to distribute the resolution, leading to severely distorted textures.&lt;/p&gt;

&lt;p&gt;To solve this visual disaster, artists typically have to &quot;place a large number of rocks, mountains, and other scene objects&quot; in these areas to forcefully cover up the ugly parts. But this introduces a new problem: extremely stiff seams (complete disconnects in lighting and textures) appear at the intersection of the objects and the terrain. To hide this rigid seam, artists are forced to densely pack the surrounding area with grass and small pebbles. For project teams with extreme demands on visual quality and scene cleanliness, this approach is not only extremely tedious but also needlessly stacks a massive amount of Draw Calls and polygon counts.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgP1_kOtUENmjf0mtxA3kDrbc9YmsgkeoTKNixslxAE-HbY_Z0fQ7xft5xRGqBi6pcPwAMk-A-e4dLF8ZZOZggTpeXvrdJpreGjkApH4-A2Tnjhtrs8KAIs5xtIql6-ketIg1A_uOVmiGHtJnebVTOwK5ii7N0wkJobx80iNoDFpxypXEacPXUIQcuyNGqy&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;409&quot; data-original-width=&quot;640&quot; height=&quot;410&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgP1_kOtUENmjf0mtxA3kDrbc9YmsgkeoTKNixslxAE-HbY_Z0fQ7xft5xRGqBi6pcPwAMk-A-e4dLF8ZZOZggTpeXvrdJpreGjkApH4-A2Tnjhtrs8KAIs5xtIql6-ketIg1A_uOVmiGHtJnebVTOwK5ii7N0wkJobx80iNoDFpxypXEacPXUIQcuyNGqy=w640-h410&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #999999;&quot;&gt;There is a noticeable hard edge where the terrain meets the mountain.&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;Pain Point 4: Quantity Limits on Multi-Layer Texture Blending (Splat Mapping)&lt;/h2&gt;

&lt;p&gt;To express surface richness, traditional terrain needs to blend multiple Textures like grass, rocks, and sand. If conventional Texture2D is used directly for multi-layer blending, the Shader will declare failure right at the compilation stage due to hardware limits on Sampler registers and Texture Binding counts (for example, on conventional mid-to-low-end Android OpenGL ES 3.0/3.1 devices, the upper limit is only 16).&lt;/p&gt;

&lt;p&gt;A common compromise solution in the industry is to use Multi-Pass (multi-channel rendering) to split up the layers. However, large terrains already have a very high vertex/polygon count; this approach not only doubles the geometric pressure but also causes severe Overdraw bandwidth overhead on mobile devices by forcing the terrain to be repeatedly drawn across the full screen, with lighting and shadows calculated multiple times.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjQtzqIjwoYCwgnVJVplkRbJZ2sPYF5KETeJ9jkrZ45fv0rXtTvR1TZpGzXZhDoxyI_C9TfI7gbKEs0im6_WUk6juDU3C4fr00e53pEF2eGXWrQeyybDTDaBCraBsQaUCXA9BXjC4FeOu0j6OLgPBNcb4GE8sI4qNhqwTRtv8tPqKcwz5niAKTJxz4BKJU2&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;327&quot; data-original-width=&quot;640&quot; height=&quot;328&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjQtzqIjwoYCwgnVJVplkRbJZ2sPYF5KETeJ9jkrZ45fv0rXtTvR1TZpGzXZhDoxyI_C9TfI7gbKEs0im6_WUk6juDU3C4fr00e53pEF2eGXWrQeyybDTDaBCraBsQaUCXA9BXjC4FeOu0j6OLgPBNcb4GE8sI4qNhqwTRtv8tPqKcwz5niAKTJxz4BKJU2=w640-h328&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;When Unity&#39;s native terrain needs to blend more than 5 layers of textures (in this case, 2 control maps, along with 8 albedo and 8 normal maps), the underlying system is forced to switch to Multi-Pass rendering. Through the Frame Debugger, we can clearly see that the terrain is brutally rendered twice, resulting in a severe waste of performance.&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;

&lt;hr style=&quot;margin: 40px 0px;&quot; /&gt;

&lt;h2&gt;Solving the Landscape Dilemma: Z-Terrain&#39;s Underlying Architecture and Core Philosophy&lt;/h2&gt;

&lt;p&gt;Having developed mobile games for a long time, I noticed that the terrains artists create are actually mostly flat ground, and ultimately, atmospheric richness is achieved by stacking a large number of scene objects. Furthermore, I have personally deeply felt the pain of late-stage artists having to repeatedly level and re-dig terrain when &quot;moving lakes and changing rivers.&quot;&lt;/p&gt;

&lt;p&gt;So I thought: Is it possible to completely solve the terrain by solely &quot;placing scene objects&quot;? Let the terrain itself have absolutely no Deform (geometric bumps or depressions). If I can just handle the &quot;edge transition blending between objects and the ground&quot; perfectly, and allow the ground to &quot;reveal objects situated below the surface (like lake bottoms or underground rivers),&quot; wouldn&#39;t that perfectly achieve the goal of a large open terrain while simultaneously liberating the artists&#39; productivity?&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiQKGSpYlIArIniXIeLQdty_xdzwEGF3SPV-EUeNsftdKeF8j3dCyw21oHS6PSikOUttK6vSxu9i3sq0aVGUhOq-wj3cdwWGF6xafzFlNk-Ks2X7b9y9HSrL-_FtCCGmh1wN_azkve1o3C7qsq6QWt7O-DMxD3aHFf_A6NzGLWvMn6dqHmTR0AWvpRj-GQ3&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;409&quot; data-original-width=&quot;640&quot; height=&quot;410&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiQKGSpYlIArIniXIeLQdty_xdzwEGF3SPV-EUeNsftdKeF8j3dCyw21oHS6PSikOUttK6vSxu9i3sq0aVGUhOq-wj3cdwWGF6xafzFlNk-Ks2X7b9y9HSrL-_FtCCGmh1wN_azkve1o3C7qsq6QWt7O-DMxD3aHFf_A6NzGLWvMn6dqHmTR0AWvpRj-GQ3=w640-h410&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Z-Terrain in-game rendering: The ground is a completely flat mesh with zero deformation (Zero Deform). All landforms (lakes, mountains, and even underground cracks and depressions) are pieced together from independent objects, and perfect edge transition blending and downward surface penetration are achieved through the underlying Shader.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;blockquote style=&quot;border-left: 4px solid rgb(204, 204, 204); color: #555555; padding-left: 15px;&quot;&gt;
&lt;/blockquote&gt;

&lt;h2&gt;Letting the Data Speak: S8 Performance Benchmark (13-Layer TileMap Blending)&lt;/h2&gt;

&lt;p&gt;The concept sounds wonderful, but can the performance really hold up when actually deployed to the underlying hardware?&lt;/p&gt;

&lt;p&gt;To test the limits of Z-Terrain, we deliberately chose an extremely outdated mobile device by today&#39;s standards—a &lt;strong&gt;Samsung Galaxy S8 (Mali-G71 GPU)&lt;/strong&gt;—for a hellish stress test.&lt;/p&gt;

&lt;p&gt;We crazily stacked 13 layers of detail texture blending on the Z-Terrain (including 13 Albedo and 13 Normal maps, totaling 26 texture assets and 28 samples), and used a brush to arbitrarily paint complex landscape details on the ground.&lt;/p&gt;

&lt;p&gt;The actual test results completely shattered the performance ceiling of traditional terrain systems:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhJXgwoLQThQSNlNglFaqsSbk1waKFQo7hPm4uaVcBKtQs-2iYlSPyY276TY3u0y_ueOLDO-3FNLU1lNkgbKWAHsfbffzzWtqRvNoqK8fkTLM73ukSTaXwO_Wda3LIhb7LvIACDI69bO8Xrb3fOFT23v16y6pbovHPmKjYhhHIy5iqfEJuAfq-oroVQmZEA&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;311&quot; data-original-width=&quot;640&quot; height=&quot;312&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhJXgwoLQThQSNlNglFaqsSbk1waKFQo7hPm4uaVcBKtQs-2iYlSPyY276TY3u0y_ueOLDO-3FNLU1lNkgbKWAHsfbffzzWtqRvNoqK8fkTLM73ukSTaXwO_Wda3LIhb7LvIACDI69bO8Xrb3fOFT23v16y6pbovHPmKjYhhHIy5iqfEJuAfq-oroVQmZEA=w640-h312&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Even on the legacy S8 platform (Mali GPU), a 13-layer blend—including both Albedo and Normal maps—still manages to run at a locked 60 fps with stable performance.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhsV4gjsPntC8PITNNXDaJ2hh_ER9R5UoNkAGy-GmdYLgqovsSrUN7nYk37NmpZQZKFqCMCCgwaIHMT3qrHYxJvlLYYhLsEzF3Fmsv5E05PdAiaxFN2kFCbJUyc9sq8p_BISEjm_snL-X0MVYAPvwicuZsjqMy3x1jsA_xqmE7KYRaZ0icqWuV2SSMmFc4s&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;389&quot; data-original-width=&quot;640&quot; height=&quot;390&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhsV4gjsPntC8PITNNXDaJ2hh_ER9R5UoNkAGy-GmdYLgqovsSrUN7nYk37NmpZQZKFqCMCCgwaIHMT3qrHYxJvlLYYhLsEzF3Fmsv5E05PdAiaxFN2kFCbJUyc9sq8p_BISEjm_snL-X0MVYAPvwicuZsjqMy3x1jsA_xqmE7KYRaZ0icqWuV2SSMmFc4s=w640-h390&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;blockquote style=&quot;border-left: 4px solid rgb(204, 204, 204); color: #555555; padding-left: 15px;&quot;&gt;
&lt;/blockquote&gt;

&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Frame Debugger panel: The entire open-world ground rendering consumes only 1 Draw Call. The other Draw Call circled above is independently occupied by the lake object.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiGZusR0l8Nz0v7x5Cs5wpcWu-elKAGOYI_pYT1DgTkt9QDiLcCrhRkRHVFlk3AW4yv-4dAgZZtNR4TrhMWZtau9hIZTplM74ExHHb0mySEhJD6UpIBu-DI7oPNZb4Ok20y-IVxGJjhNopAT08JjEhXHm1Ss2ZQLPe9T4HtvC5pMshXSM2nwl5XrHVNqbaI&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;452&quot; data-original-width=&quot;800&quot; height=&quot;362&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiGZusR0l8Nz0v7x5Cs5wpcWu-elKAGOYI_pYT1DgTkt9QDiLcCrhRkRHVFlk3AW4yv-4dAgZZtNR4TrhMWZtau9hIZTplM74ExHHb0mySEhJD6UpIBu-DI7oPNZb4Ok20y-IVxGJjhNopAT08JjEhXHm1Ss2ZQLPe9T4HtvC5pMshXSM2nwl5XrHVNqbaI=w640-h362&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Once you exceed 5 layers on a native Unity Terrain—meaning you open a second Control map—you have to draw (render) the Terrain two or more times.&lt;/span&gt;&lt;/div&gt;&lt;h2&gt;Revealing the Core Black Magic: How Did I Force a 13-Layer Blend on Mobile?&lt;/h2&gt;

&lt;p&gt;The main reason traditional terrains cannot use One-Pass rendering is that their TileMaps do not utilize TextureArray, causing the number of Texture Bindings to exceed hardware limits. So, simply replacing the single-texture architecture of the TerrainLayer with a TextureArray should do the trick.&lt;/p&gt;

&lt;p&gt;But it&#39;s not that simple; multiple Control Maps suffer from the exact same hardware flaw. Therefore, Z-Terrain comprehensively consolidates both of these into TextureArrays, suppressing the number of Texture Bindings used to the absolute extreme:&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhunpuZ6NTa16D-xdx6GPLYCQ0gFk65vvvrOfdpGfvKPXk43S2Hm_GYmwXOiQSuCmafWJQotnItObTOWqxWbI6GWcItQrqFJrXLdx2kT2Qx-jWZYzs6DX8m6bbjTidMDCKgniI_ySz5LmSb65bX5EUH0480ddJulTgiQB5h26ZbtUp9c6qXlM-bggVbBQvL&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;947&quot; data-original-width=&quot;400&quot; height=&quot;640&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhunpuZ6NTa16D-xdx6GPLYCQ0gFk65vvvrOfdpGfvKPXk43S2Hm_GYmwXOiQSuCmafWJQotnItObTOWqxWbI6GWcItQrqFJrXLdx2kT2Qx-jWZYzs6DX8m6bbjTidMDCKgniI_ySz5LmSb65bX5EUH0480ddJulTgiQB5h26ZbtUp9c6qXlM-bggVbBQvL=w269-h640&quot; width=&quot;269&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;It consolidates the 13 layers of ground surface details (Albedo/Smoothness) into a single Texture Array of 13 Slices, occupying only 1 texture slot.&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhflN_pfYXNSHalZBmfmGAH6Nui2QWAPz5Pqj30oKEqUlw-IoHEK0XnKGQC366cT5zkJ4967S7lQfCKK486MAH4emzAgh4EYOXxWyf74lxzceFHKm1Pt7R58KkeFpNpQ91LWfio8AXB3_NK7wL2iVJc1eHUh8iq7GVU2ynjQiFyddekyKKKgXMgI8b-O8L-&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;947&quot; data-original-width=&quot;478&quot; height=&quot;640&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhflN_pfYXNSHalZBmfmGAH6Nui2QWAPz5Pqj30oKEqUlw-IoHEK0XnKGQC366cT5zkJ4967S7lQfCKK486MAH4emzAgh4EYOXxWyf74lxzceFHKm1Pt7R58KkeFpNpQ91LWfio8AXB3_NK7wL2iVJc1eHUh8iq7GVU2ynjQiFyddekyKKKgXMgI8b-O8L-=w323-h640&quot; width=&quot;323&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Multiple Control Maps cannot escape the fate of being arrayed. Under a 13-layer surface blend, a single terrain chunk originally required 3 independent Control Maps (12 channels); Z-Terrain directly consolidates multiple Control Maps into a single TextureArray, fundamentally resolving the register and Texture Binding limits during multi-layer blending.&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;Breaking the Visual Bottleneck: Perfect Edge Transition Blending Between Objects and Ground&lt;/h2&gt;

&lt;p&gt;This is the utmost priority of the entire Z-Terrain system. Since we opted for a &quot;Zero Deform all-object workflow,&quot; the transition quality at the contact surfaces between objects and the ground directly determines the success or failure of the entire large scene.&lt;/p&gt;

&lt;p&gt;To achieve perfect visual effects, Z-Terrain adopts the following architecture at the base level:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEinTSQKYN98iG4xo_ax6pywcuRm0-4tcDM2cuH2o0pWbzuKyTX-GARLYRjKPDyaaKlRCo1piLWtg6UebqefFFl7mNNpMDOx09aOq2TPQm-hGgXWkVW7YBCiK0TqgRDoJ-Tpi_VEPvA5O3wx3f6mKSxs61y9fbhjMLbVqvbqSWLKuLmmPr_foJwLotCQduoq&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;250&quot; data-original-width=&quot;480&quot; height=&quot;334&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEinTSQKYN98iG4xo_ax6pywcuRm0-4tcDM2cuH2o0pWbzuKyTX-GARLYRjKPDyaaKlRCo1piLWtg6UebqefFFl7mNNpMDOx09aOq2TPQm-hGgXWkVW7YBCiK0TqgRDoJ-Tpi_VEPvA5O3wx3f6mKSxs61y9fbhjMLbVqvbqSWLKuLmmPr_foJwLotCQduoq=w640-h334&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Live Demo: When dragging the object or tweaking the sliders, the mountain base textures and normals blend together dynamically with an extremely smooth gradient.&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;Implementation Philosophy: Globalized Assets and Multi-Dimensional Transitions&lt;/h3&gt;

&lt;p&gt;The core to Z-Terrain achieving seamless transitions lies in the globalization of Control Map (weight map) and Tile Map (detail texture) data:&lt;/p&gt;

&lt;ul&gt;
  &lt;li style=&quot;margin-bottom: 10px;&quot;&gt;&lt;strong&gt;World Space Sampling:&lt;/strong&gt; Whether it&#39;s the flat Quad ground serving as the foundation, or any scene objects placed on top of it (like mountains or rocks), they all uniformly use world coordinates (World Space Position.xz) within the Shader to sample textures and weights. This means objects and the ground share the exact same data perspective at the pixel level, naturally possessing continuity.&lt;/li&gt;
  &lt;li style=&quot;margin-bottom: 10px;&quot;&gt;&lt;strong&gt;Height Blending Weights:&lt;/strong&gt; Utilizing the Global weight data from the ground&#39;s control map, combined with the depth relationship of object pixels, it dynamically controls the transition range and blending weights of edge materials via blending sliders, achieving a visual deception effect identical to native Terrain Deform (terrain upheaval).&lt;/li&gt;
  &lt;li style=&quot;margin-bottom: 10px;&quot;&gt;&lt;strong&gt;Dual Blending of Texture and Normal:&lt;/strong&gt; If blending is only done on the Albedo texture, lighting will instantly reveal the trick because the normal directions of the object and the ground don&#39;t match. Z-Terrain achieves smooth transition of both textures and ground Normals simultaneously at the boundaries, ensuring that shadows and highlights remain completely continuous and unbroken when lit.&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgjr1K_8v-D3S09xDe7Gfy9C8b35n-EssHdCaM64dUYHQ5gaOnPx33grrcohWQvgJ2vjFQLIulB-MEdh8YiXqavjT1J-eydFQ1NNV3CuVq3Y7rsbGKKxuFq38yUPluYtBUQb0-reRvy6ImjMaHS6va7dDIYTdVRYLp642ujn4-lswUccL00j2TxUgEgncgc&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;337&quot; data-original-width=&quot;640&quot; height=&quot;338&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgjr1K_8v-D3S09xDe7Gfy9C8b35n-EssHdCaM64dUYHQ5gaOnPx33grrcohWQvgJ2vjFQLIulB-MEdh8YiXqavjT1J-eydFQ1NNV3CuVq3Y7rsbGKKxuFq38yUPluYtBUQb0-reRvy6ImjMaHS6va7dDIYTdVRYLp642ujn4-lswUccL00j2TxUgEgncgc=w640-h338&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;This is a fantastic solution for the notorious terrain UV stretching issue. It turns out that even when utilizing Unity&#39;s Terrain system, scattering environment assets remains unavoidable.&lt;/span&gt;&lt;/div&gt;

&lt;h2&gt;Another Key Point: Downward Surface Penetration (Reveal Processing)&lt;/h2&gt;

&lt;p&gt;For traditional terrain systems to create lakes, rivers, or cracks, the only pipeline is to use a heightmap to Deform (dig out a depression downwards) and then cover it with a Plane Mesh (water surface mesh) on top to simulate the water body.&lt;/p&gt;

&lt;p&gt;The core goal of Z-Terrain is to completely abandon this high-maintenance deformation operation. We want to be able to simply place a lake object to quickly modify a level. But this brings a soul-searching challenge: on a completely flat ground mesh, how do you allow the line of sight to penetrate the ground and see objects hidden below the surface?&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhIeGc27sagbZOEQizWaPojcFj6jz2XHqy0geIdjKUNtjWnwAQ5BHhJCSsQYKi99awdDMCy3CJumpLKkXbShhQN2U3cKySveeLj3NjDPs6W4wRQCiYFE9Y5WfKO3--ljhdNeR16Oyt9hRoz-gipmCbwqsEeJ2oTD-O_e16NZtHAg45Mo0pU4DXmqnvPhVWJ&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;450&quot; data-original-width=&quot;640&quot; height=&quot;450&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhIeGc27sagbZOEQizWaPojcFj6jz2XHqy0geIdjKUNtjWnwAQ5BHhJCSsQYKi99awdDMCy3CJumpLKkXbShhQN2U3cKySveeLj3NjDPs6W4wRQCiYFE9Y5WfKO3--ljhdNeR16Oyt9hRoz-gipmCbwqsEeJ2oTD-O_e16NZtHAg45Mo0pU4DXmqnvPhVWJ=w640-h450&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;The lake depth fade and shoreline textures blend seamlessly.&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;color: #999999;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;span style=&quot;color: #999999;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjs2UJUHbn6XpvpGW-KIeYAJS_1A4Yx8W2BDN0jkqnL3pyZsjumRPNT--8UeqOyyhxYmjraB_OZ1klTOvfCXDBnwQTPEkHxvpZ_wwqZs_Vif6nPSLFhaW6wsGlGemwnrydCD2X3pbIaMNhYmEiddrLDsUHI9LeNIdZKxPOs8Q8qpMHIiu01rSgnenhDkKzD&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;450&quot; data-original-width=&quot;640&quot; height=&quot;450&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjs2UJUHbn6XpvpGW-KIeYAJS_1A4Yx8W2BDN0jkqnL3pyZsjumRPNT--8UeqOyyhxYmjraB_OZ1klTOvfCXDBnwQTPEkHxvpZ_wwqZs_Vif6nPSLFhaW6wsGlGemwnrydCD2X3pbIaMNhYmEiddrLDsUHI9LeNIdZKxPOs8Q8qpMHIiu01rSgnenhDkKzD=w640-h450&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;The crack asset can also seamlessly intersect with flat surfaces and achieve a perfect edge blend.&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;Abandoning Stencil, Pivoting to an Extreme Blending Rendering Strategy&lt;/h3&gt;

&lt;p&gt;In the early stages of technical implementation, the most intuitive approach was to use the Stencil Buffer to carve out the ground. However, after actual stress testing, to ensure the perfect blending quality of semi-transparent transitions for ground materials, I ultimately abandoned the Stencil solution and adopted an extremely clever combination technique:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alpha Blending + Cull Off + Back-face occluding forward surface.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Although this approach slightly sacrifices some Overdraw overhead on mobile TBDR architectures, the return is unparalleled: we successfully achieved a pixel-level, high-quality &quot;downward surface penetration effect&quot; that perfectly complies with correct perspective occlusion relationships on a completely flat Quad mesh. Artists no longer need to re-bake heightmaps; simply drop a lake object, and the underlying Shader automatically digs open the ground.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjGKxTjoEElf_R-llw5GZkHjbhZAGKkpJg8T2EaJFHbwVPPk64lM23yMSPZ7FURFma0-XcgF9EQpSsVi-PjMP26l_9yRqVyTfnLb2y3LoDkcAJbpRlVg5XosCbXYzQO6o-o_e56dhk0wlKtK-Cp_kdD3FK0RDivxSi-F4nJ-1lWPGpuFIocEjC4X33LQ68Y&quot; style=&quot;margin-left: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img data-original-height=&quot;462&quot; data-original-width=&quot;640&quot; height=&quot;462&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjGKxTjoEElf_R-llw5GZkHjbhZAGKkpJg8T2EaJFHbwVPPk64lM23yMSPZ7FURFma0-XcgF9EQpSsVi-PjMP26l_9yRqVyTfnLb2y3LoDkcAJbpRlVg5XosCbXYzQO6o-o_e56dhk0wlKtK-Cp_kdD3FK0RDivxSi-F4nJ-1lWPGpuFIocEjC4X33LQ68Y=w640-h462&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;By disabling culling (Cull Off) and implementing occlusion logic, the system ensures that the terrain mesh is precisely hidden under any perspective view, revealing only the intended underlying content.&lt;/span&gt;&lt;/p&gt;

&lt;h2&gt;The Cross-Control Map Boundary Issue (Breadth Solution)&lt;/h2&gt;

&lt;p&gt;When facing an extremely massive terrain, the resolution of a single Control Map usually hits an upper limit. The conventional industry practice is to slice the terrain into many chunks (Pages). However, this triggers a catastrophic pipeline disaster during &quot;object blending&quot;: when a massive mountain crosses a chunk boundary, the object Shader cannot dynamically decide which Control Map to sample.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgnP8zQmV5aPkdDxJC0OKbgJ6nZUD32ghOPKjHiVAzm6eN0cCeLdRLTa07jkKSNVtRImCTiGl4sXKEA6CjuTNxuR-stI9nGBY4biuOCvLJQLOva21oTKqtBLM5eAo_LV6Pn6VprReywJnNDTFNAs0ULuZJjKuyecRI_gEzvQOp9ZXRkEiy275CS7cMa5NPc&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;479&quot; data-original-width=&quot;640&quot; height=&quot;480&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgnP8zQmV5aPkdDxJC0OKbgJ6nZUD32ghOPKjHiVAzm6eN0cCeLdRLTa07jkKSNVtRImCTiGl4sXKEA6CjuTNxuR-stI9nGBY4biuOCvLJQLOva21oTKqtBLM5eAo_LV6Pn6VprReywJnNDTFNAs0ULuZJjKuyecRI_gEzvQOp9ZXRkEiy275CS7cMa5NPc=w640-h480&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Z-Terrain stress test: Even when the object is placed directly at the center of a four-page quad-intersection, the underlying system maintains absolutely precise, fluid, and seamless material blending.&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;Pixel-Level &quot;Breadth&quot; Slice Conversion Mechanism&lt;/h3&gt;

&lt;p&gt;Z-Terrain comprehensively processes Control Maps into TextureArrays. No matter how many Pages the map is divided into, to the GPU, they are simply different Slices within the same texture register. The Shader directly uses the pixel&#39;s world coordinates to perform breadth mathematical formula conversions, dynamically calculating exactly which terrain chunk grid the current pixel is stepping on. This ensures that it only ever needs to sample the corresponding chunk&#39;s Control Map exactly once.&lt;/p&gt;

&lt;h2&gt;The Soul of the Toolchain: Real-Time Editing with TextureArray&lt;/h2&gt;

&lt;p&gt;If the rendering performance is robust but causes the artist to experience severe lag during editing, the system is still a failure. To end this pipeline pain point, Z-Terrain utilizes a proprietary brush tool driven entirely by Compute Shaders:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEh62rc9mJ-ouYWBO4lRcbetVn2czILrMpO43WtS1aVeDUGJx8RykSpMHzD4mlDdXfHDXk3WA1f2LASwYW5qrdlG8h_gFZHE6fwIA6sRQqdvOx5UFzbODZ2NtOBMKR9-O_VxeLzs1NUUq1F93FQcdU-EgjdgDsNbB5SRjw0MCHCVtugPAP3gwSERaUfpXpPI&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;335&quot; data-original-width=&quot;640&quot; height=&quot;336&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEh62rc9mJ-ouYWBO4lRcbetVn2czILrMpO43WtS1aVeDUGJx8RykSpMHzD4mlDdXfHDXk3WA1f2LASwYW5qrdlG8h_gFZHE6fwIA6sRQqdvOx5UFzbODZ2NtOBMKR9-O_VxeLzs1NUUq1F93FQcdU-EgjdgDsNbB5SRjw0MCHCVtugPAP3gwSERaUfpXpPI=w640-h336&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Z-Terrain Brush Editing Interface&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;ol&gt;&lt;li style=&quot;margin-bottom: 10px;&quot;&gt;&lt;strong&gt;Precise Pixel-Level Addressing:&lt;/strong&gt; Compute Shaders possess extremely powerful parallel read/write capabilities (RWTexture2DArray). It precisely addresses and overwrites weights solely for the pixels covered by the current brush radius.&lt;/li&gt;
  &lt;li style=&quot;margin-bottom: 10px;&quot;&gt;&lt;strong&gt;Inherently Friendly Output Targets:&lt;/strong&gt; Because CS can directly write data into specific Slices of a TextureArray, the brushing feel is incredibly silky smooth, completely solving the historical nightmare of &quot;unbearable lag&quot;.&lt;/li&gt;&lt;/ol&gt;For more details on the Compute Shader brush, please refer to my previous article here:&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://makedreamvsogre.blogspot.com/2025/09/unity-brush-technology-breakthroughs.html&quot;&gt;Unity Brush Technology: Breakthroughs and Practical Insights — From SetPixels to Compute Shader&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;ol&gt;
&lt;/ol&gt;

&lt;hr style=&quot;margin: 40px 0px;&quot; /&gt;

&lt;h2&gt;Conclusion: The Ultimate Answer for Next-Gen Mobile Terrains&lt;/h2&gt;

&lt;p&gt;Z-Terrain wasn&#39;t born to reinvent the wheel, but to forge a bloody path for art pipelines and rendering bandwidth within the extreme constraints of mobile performance. We can intuitively see the decisive differences:&lt;/p&gt;

&lt;ul&gt;
  &lt;li style=&quot;margin-bottom: 10px;&quot;&gt;&lt;strong&gt;1. Geometry Polygon Pressure:&lt;/strong&gt; Unity Native Terrain (Extremely High) vs. Z-Terrain (Zero Pressure - flat quad mesh).&lt;/li&gt;
  &lt;li style=&quot;margin-bottom: 10px;&quot;&gt;&lt;strong&gt;2. Landform Modification Cost:&lt;/strong&gt; Traditional Plugins (Destructive pipeline hell) vs. Z-Terrain (Extremely Low - all-object workflow, place and modify at will).&lt;/li&gt;
  &lt;li style=&quot;margin-bottom: 10px;&quot;&gt;&lt;strong&gt;3. Multi-Layer Material Blending:&lt;/strong&gt; Unity Native Terrain (Forces Multi-Pass &amp;gt; 5 layers) vs. Z-Terrain (Powerfully supports 13+ layers in 1-Pass).&lt;/li&gt;
  &lt;li style=&quot;margin-bottom: 10px;&quot;&gt;&lt;strong&gt;4. Cross-Page Edge Transition:&lt;/strong&gt; Traditional Plugins (Unsolvable) vs. Z-Terrain (Perfectly seamless via global world coordinates).&lt;/li&gt;
  &lt;li style=&quot;margin-bottom: 10px;&quot;&gt;&lt;strong&gt;5. Mobile Draw Call Overhead:&lt;/strong&gt; Traditional Plugins (Increases linearly) vs. Z-Terrain (Extreme batching, constant 1-Draw Call via SRP Batcher).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Z-Terrain proves one thing: in mobile graphics development, the top-tier optimization isn&#39;t usually optimizing the old pipeline, but rather thoroughly subverting and reconstructing from the base data structures and art pipelines.&lt;/p&gt;&lt;p&gt;The reference youtube：&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;515&quot; src=&quot;https://www.youtube.com/embed/bhHCF1Mj8cA&quot; width=&quot;620&quot; youtube-src-id=&quot;bhHCF1Mj8cA&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;You can get it here：&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/tools/terrain/z-terrain-zero-deform-ultimate-mobile-landscape-387322&quot;&gt;Z-Terrain: Zero Deform Ultimate Mobile Landscape&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Associated Asset Page：&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;SmallBurger&quot;&gt;https://assetstore.unity.com/publishers/86820&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/6842997562985036926/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2026/08/why-are-traditional-terrains.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/6842997562985036926'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/6842997562985036926'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2026/08/why-are-traditional-terrains.html' title='Why Are Traditional Terrains Performance Killers on Mobile? Z-Terrain (Zero Deform Ultimate Mobile Landscape) Development Insights and Solutions'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEiD3-sHPJTiIPJ4Q9nx-IjcTDnit91DPh55lsAiRHhEFojeL3rk50rnpv77LmOSG3qhyUM1KAkzfw1xInaBXp1pPyn0REGcoa_VhJOCKqGgUH3BUGv5C7Ko5g-tDjcw-1VLoVsCR5RM83Wtwqe8s1Q4LLHji0aWEoL768Bn2VNxp7TbWEaAeDrDhXDvO28G=s72-w640-h420-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-3326915184556188345</id><published>2026-03-03T09:44:00.001+08:00</published><updated>2026-03-03T09:45:09.825+08:00</updated><title type='text'>Development Insights: GPUDrivenForwardPlus</title><content type='html'>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjZlGtyDWZ6S5UOu_QLJgJSu01rFict6_04X74LdPorTP7PrMtiWShfX8GljwdweuIUlF-2tfVnWVOpNZDo4k9UeDUDYRWEkmmlzwEWm8W30cfTfgulpg3ei3lQDy65DHACPndGV1VV4ssbKF-NhXL-xQpOfxnn9vSs8CDZepYmQE23DAQBqhjdFSThJrzx&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;747&quot; data-original-width=&quot;1195&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjZlGtyDWZ6S5UOu_QLJgJSu01rFict6_04X74LdPorTP7PrMtiWShfX8GljwdweuIUlF-2tfVnWVOpNZDo4k9UeDUDYRWEkmmlzwEWm8W30cfTfgulpg3ei3lQDy65DHACPndGV1VV4ssbKF-NhXL-xQpOfxnn9vSs8CDZepYmQE23DAQBqhjdFSThJrzx=w640-h400&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;I have always been deeply fascinated by &lt;b data-index-in-node=&quot;40&quot; data-path-to-node=&quot;4&quot;&gt;Forward Plus&lt;/b&gt; lighting architectures. Before the official URP Forward Plus was released, I implemented a custom Forward Plus solution for URP based on &lt;b data-index-in-node=&quot;190&quot; data-path-to-node=&quot;4&quot;&gt;Depth Pre-pass&lt;/b&gt;, known as&amp;nbsp;&lt;a href=&quot;https://github.com/AkilarLiao/ForwardPlusURP&quot;&gt;ForwardPlusURP&lt;/a&gt;&lt;br /&gt;&lt;p data-end=&quot;523&quot; data-start=&quot;408&quot;&gt;The core logic of that version followed the classic &lt;b data-index-in-node=&quot;52&quot; data-path-to-node=&quot;5&quot;&gt;Tile-based Forward+&lt;/b&gt; approach: it rendered a depth pre-pass to calculate the depth range for each tile and performed light culling to reduce the cost of light loops in the fragment shader.&lt;span&gt;&lt;/span&gt;&lt;/p&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;p&gt;&lt;/p&gt;&lt;h3 data-path-to-node=&quot;6&quot;&gt;The Challenges of Mobile Platforms&lt;/h3&gt;&lt;p data-path-to-node=&quot;7&quot;&gt;However, during practical testing and development, I realized that this path faced structural issues on mobile platforms:&lt;/p&gt;&lt;ul data-path-to-node=&quot;8&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;8,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;Cost of Depth Pre-pass&lt;/b&gt;: On mobile GPUs (TBDR architecture), a depth pre-pass adds a significant geometry rendering stage, which often disrupts hardware-level optimizations like cache efficiency and Early-Z behavior.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;8,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;Dependency on Interlock Operations&lt;/b&gt;: The implementation relied heavily on &lt;b data-index-in-node=&quot;73&quot; data-path-to-node=&quot;8,1,0&quot;&gt;Pixel Interlock&lt;/b&gt; (or similar atomic operations) to maintain light lists. On mobile GPUs—specifically Mali and PowerVR architectures—this often leads to pipeline serialization and unstable performance.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p data-path-to-node=&quot;9&quot;&gt;Due to these factors, I decided not to pursue that specific direction further.&lt;/p&gt;&lt;h3 data-path-to-node=&quot;10&quot;&gt;A New Inspiration: Clustered Shading&lt;/h3&gt;&lt;p data-path-to-node=&quot;11&quot;&gt;When the official URP Forward Plus (based on CPU-side Job System light culling) was released, it provided a different perspective: a &quot;stable, predictable, and depth-pre-pass-independent&quot; light management model.&lt;/p&gt;&lt;p data-path-to-node=&quot;12&quot;&gt;This sparked a new question: &lt;i data-index-in-node=&quot;29&quot; data-path-to-node=&quot;12&quot;&gt;Is it possible to design a &lt;b data-index-in-node=&quot;56&quot; data-path-to-node=&quot;12&quot;&gt;GPU-driven light culling&lt;/b&gt; architecture specifically for mobile—one that doesn&#39;t rely on depth pre-passes or heavy interlocks, while maintaining compatibility with the standard Forward workflow?&lt;/i&gt;&lt;/p&gt;&lt;p data-path-to-node=&quot;13&quot;&gt;This goal led me to explore &lt;b data-index-in-node=&quot;28&quot; data-path-to-node=&quot;13&quot;&gt;Clustered Forward Plus&lt;/b&gt;, resulting in the implementation of &lt;b data-index-in-node=&quot;87&quot; data-path-to-node=&quot;13&quot;&gt;GPUDrivenForwardPlus&lt;/b&gt;.&lt;/p&gt;&lt;p data-end=&quot;1308&quot; data-start=&quot;1225&quot;&gt;Implementation Workflow&lt;/p&gt;&lt;p data-end=&quot;1308&quot; data-start=&quot;1225&quot;&gt;&lt;/p&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,0,0&quot;&gt;Frustum Voxelization&lt;/b&gt;: Subdivide the Camera&#39;s View Frustum into &lt;b data-index-in-node=&quot;63&quot; data-path-to-node=&quot;16,0,0&quot;&gt;AABB Clusters&lt;/b&gt;.&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEg_SEY2Ei0w8xwNc975S_NM0jqr1izgt1G32CbQMPfntc9bBcZ7nytGmyeIkn9eIlgwW6ToG0NYYGxNoAmetyanblFkJN3nKbxSAf3lHHE_je3fkbEi4so3TBiJa503na0ZjfU9gj4FDcUoiSzAhC2ZtwAwmkuxUBv0np59TJ7jD6EiY8kS5V8vgzlvJ3yX&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;563&quot; data-original-width=&quot;1161&quot; height=&quot;310&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEg_SEY2Ei0w8xwNc975S_NM0jqr1izgt1G32CbQMPfntc9bBcZ7nytGmyeIkn9eIlgwW6ToG0NYYGxNoAmetyanblFkJN3nKbxSAf3lHHE_je3fkbEi4so3TBiJa503na0ZjfU9gj4FDcUoiSzAhC2ZtwAwmkuxUBv0np59TJ7jD6EiY8kS5V8vgzlvJ3yX=w640-h310&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,1,0&quot;&gt;GPU Light Culling&lt;/b&gt;: Use a &lt;b data-index-in-node=&quot;25&quot; data-path-to-node=&quot;16,1,0&quot;&gt;Compute Shader&lt;/b&gt; to perform intersection tests between AABB Clusters and visible lights.&lt;/li&gt;&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,2,0&quot;&gt;Light Indexing&lt;/b&gt;: Record the culling results, including light indices and counts, into a buffer.&lt;/li&gt;&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,3,0&quot;&gt;Shading&lt;/b&gt;: Convert screen-space coordinates to the corresponding cluster index to retrieve light data for final shading.&lt;/li&gt;&lt;/ol&gt;Key Features &amp;amp; Optimizations&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,0,0&quot;&gt;Custom Light Culling&lt;/b&gt;: Since Unity’s SRP default &lt;code data-index-in-node=&quot;48&quot; data-path-to-node=&quot;18,0,0&quot;&gt;Visible Lights&lt;/code&gt; count is limited to 32 (including the main light), I re-implemented the culling logic to support up to &lt;b data-index-in-node=&quot;166&quot; data-path-to-node=&quot;18,0,0&quot;&gt;64 lights per camera&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhS9PdRbDXlt8Vs-3UaysB-vq3gCvel7AuCmjVShCqRlqEixBhn4-mBNUROUGF3Hqjh_mAfBx1h0Mmzywi8vcEe6FGDjvwxHg09vcBEciKFJoEwuXeKaMHM5wYfCs2yES_g-SdeOJm6Kj9knEBnkVDhBIuCrdOvHDIjw-7x-dw4Zm5UCohm01kndYmtebHL&quot; style=&quot;margin-left: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img data-original-height=&quot;743&quot; data-original-width=&quot;1337&quot; height=&quot;356&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhS9PdRbDXlt8Vs-3UaysB-vq3gCvel7AuCmjVShCqRlqEixBhn4-mBNUROUGF3Hqjh_mAfBx1h0Mmzywi8vcEe6FGDjvwxHg09vcBEciKFJoEwuXeKaMHM5wYfCs2yES_g-SdeOJm6Kj9knEBnkVDhBIuCrdOvHDIjw-7x-dw4Zm5UCohm01kndYmtebHL=w640-h356&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,1,0&quot;&gt;Performance Balancing&lt;/b&gt;: To ensure performance stability, each cluster currently supports a maximum of &lt;b data-index-in-node=&quot;101&quot; data-path-to-node=&quot;18,1,0&quot;&gt;8 additional lights&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjIaph0MvVgZwBjHQ0gd4Do9oU9Lm7oBdqiiJCQKiUVccFsyDk4eMLJ024xMjc4isfNOb0BZUbDfJLETXZMHlagDeTuVPhFxRp_IUx7XC_jtANITHTEQjlT4J-z3OaM6VswCCmTDgjNINUA2BJBE8LSDb28QQLJM3HdF17ARrXxJKmEXRXHOoSAxslkqDzM&quot; style=&quot;margin-left: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img data-original-height=&quot;747&quot; data-original-width=&quot;1342&quot; height=&quot;356&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjIaph0MvVgZwBjHQ0gd4Do9oU9Lm7oBdqiiJCQKiUVccFsyDk4eMLJ024xMjc4isfNOb0BZUbDfJLETXZMHlagDeTuVPhFxRp_IUx7XC_jtANITHTEQjlT4J-z3OaM6VswCCmTDgjNINUA2BJBE8LSDb28QQLJM3HdF17ARrXxJKmEXRXHOoSAxslkqDzM=w640-h356&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,2,0&quot;&gt;Mobile Friendly&lt;/b&gt;: By avoiding the Depth Pre-pass, this solution is highly efficient for mobile hardware and naturally supports &lt;b data-index-in-node=&quot;126&quot; data-path-to-node=&quot;18,2,0&quot;&gt;Alpha-blended (Transparent) objects&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgrX3RrdTDP8fkJb7q_DD2TpqqUD0SiuodqzCwl4O3Bhuq92H1GFqwvwUDPsVrHqDliKJynXcrkY46T73H5RTfaTDowNsdh1ZOP55VxiqAfm0jDkR982KjqtbKVzA-xJRLnV9vamxTNJ2a3s7f0-HghHJY7f_mMp3Kc_Hhd9A_-EYebf2IMuTjyL2MSGJtj&quot; style=&quot;margin-left: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img data-original-height=&quot;750&quot; data-original-width=&quot;1338&quot; height=&quot;358&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgrX3RrdTDP8fkJb7q_DD2TpqqUD0SiuodqzCwl4O3Bhuq92H1GFqwvwUDPsVrHqDliKJynXcrkY46T73H5RTfaTDowNsdh1ZOP55VxiqAfm0jDkR982KjqtbKVzA-xJRLnV9vamxTNJ2a3s7f0-HghHJY7f_mMp3Kc_Hhd9A_-EYebf2IMuTjyL2MSGJtj=w640-h358&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,3,0&quot;&gt;Supported Light Types&lt;/b&gt;: Currently supports &lt;b data-index-in-node=&quot;42&quot; data-path-to-node=&quot;18,3,0&quot;&gt;Point Lights&lt;/b&gt; and &lt;b data-index-in-node=&quot;59&quot; data-path-to-node=&quot;18,3,0&quot;&gt;Spot Lights&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgMNrugsWmnWb3eyka0-vBX4ssrj7A3e8idkqZphIjkpXlPV24issdrz-ibLbS_cUVltfx1jsJE_9XH2dq9M-zmpYmFBVkpLafCi_1CnVyblUUTeKSBe9zG-zjOKOwd6TqGpwnQ5MuyT6toe76PgVP_sVdUKMhyR-ZZG0ih6Z1RtvO7WqMdmVIq5Bzb9UmQ&quot; style=&quot;margin-left: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img data-original-height=&quot;847&quot; data-original-width=&quot;1163&quot; height=&quot;466&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgMNrugsWmnWb3eyka0-vBX4ssrj7A3e8idkqZphIjkpXlPV24issdrz-ibLbS_cUVltfx1jsJE_9XH2dq9M-zmpYmFBVkpLafCi_1CnVyblUUTeKSBe9zG-zjOKOwd6TqGpwnQ5MuyT6toe76PgVP_sVdUKMhyR-ZZG0ih6Z1RtvO7WqMdmVIq5Bzb9UmQ=w640-h466&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Resources：&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Demo Video:&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;596&quot; src=&quot;https://www.youtube.com/embed/o2fjCHA1kEk&quot; width=&quot;717&quot; youtube-src-id=&quot;o2fjCHA1kEk&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/vfx/shaders/gpudrivenforwardplus-354626&quot;&gt;GPUDrivenForwardPlus Asset&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://assetstore.unity.com/zh-CN/publishers/86820&quot;&gt;SmallBurger Assets&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/3326915184556188345/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2026/03/development-insights.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/3326915184556188345'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/3326915184556188345'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2026/03/development-insights.html' title='Development Insights: GPUDrivenForwardPlus'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEjZlGtyDWZ6S5UOu_QLJgJSu01rFict6_04X74LdPorTP7PrMtiWShfX8GljwdweuIUlF-2tfVnWVOpNZDo4k9UeDUDYRWEkmmlzwEWm8W30cfTfgulpg3ei3lQDy65DHACPndGV1VV4ssbKF-NhXL-xQpOfxnn9vSs8CDZepYmQE23DAQBqhjdFSThJrzx=s72-w640-h400-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-6428312029663925665</id><published>2026-01-08T20:51:00.000+08:00</published><updated>2026-01-08T20:51:44.738+08:00</updated><title type='text'>SmallBurger Plugins Now Support Unity 6.3 LTS</title><content type='html'>&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjtVZl1oN6MCRODEwbOE6XVrug708UiCxtY3ySUIlN41wr4I4882RfprFJDX6rxLEdZF4KZToIS0VqsyKGv0KU2L3ugAE1xwPHgQsctJ9zd0hDgduyANuw7FqwWknuU4_4-sy-ALu5sjE_mXvR5QzWpYWEvVg83Ozlc739RSXUh-_-eMaayBQXH4c-dN8g2&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;428&quot; data-original-width=&quot;979&quot; height=&quot;280&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjtVZl1oN6MCRODEwbOE6XVrug708UiCxtY3ySUIlN41wr4I4882RfprFJDX6rxLEdZF4KZToIS0VqsyKGv0KU2L3ugAE1xwPHgQsctJ9zd0hDgduyANuw7FqwWknuU4_4-sy-ALu5sjE_mXvR5QzWpYWEvVg83Ozlc739RSXUh-_-eMaayBQXH4c-dN8g2=w640-h280&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;A selection of SmallBurger Asset Store plugins has been tested and verified with Unity 6.3 LTS.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Currently, 8 out of 21 plugins have been validated on Unity 6.3 LTS.&lt;/p&gt;&lt;p&gt;Additional plugins will be tested and updated progressively based on project priorities.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;You can find the complete SmallBurger plugin lineup on the Unity Asset Store publisher page:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;👉 &lt;a href=&quot;https://assetstore.unity.com/publishers/86820&quot;&gt;Smallburger&lt;/a&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/6428312029663925665/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2026/01/smallburger-plugins-now-support-unity.html#comment-form' title='2 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/6428312029663925665'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/6428312029663925665'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2026/01/smallburger-plugins-now-support-unity.html' title='SmallBurger Plugins Now Support Unity 6.3 LTS'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEjtVZl1oN6MCRODEwbOE6XVrug708UiCxtY3ySUIlN41wr4I4882RfprFJDX6rxLEdZF4KZToIS0VqsyKGv0KU2L3ugAE1xwPHgQsctJ9zd0hDgduyANuw7FqwWknuU4_4-sy-ALu5sjE_mXvR5QzWpYWEvVg83Ozlc739RSXUh-_-eMaayBQXH4c-dN8g2=s72-w640-h280-c" height="72" width="72"/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-1397416059933890985</id><published>2025-12-17T12:56:00.001+08:00</published><updated>2025-12-17T12:57:13.963+08:00</updated><title type='text'>🌸GPU-Driven Poetic Dynamics: From Simulating Ghost of Tsushima’s Iconic Flower Field to the Birth of a Complete Vegetation System</title><content type='html'>&lt;p&gt;&amp;nbsp;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgBilGZoX1955OmCNOidhtASfPMyvDRFSIh6dMuZa2eurHGVK-rLgqRfFVWsTKAWJJWpJC_pFBVYmb9OVKCzbAn4jPQQaRtDDtmxiZCOjyQN-Xk7dsrrFayZ16xSNJWtwcwXrdBzmjBdhSk_42OtujE_ar5k2YyU_T0vSCP0MilRIN0qYB2tMAYrRiLbGq1&quot; style=&quot;margin-left: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img data-original-height=&quot;626&quot; data-original-width=&quot;960&quot; height=&quot;418&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgBilGZoX1955OmCNOidhtASfPMyvDRFSIh6dMuZa2eurHGVK-rLgqRfFVWsTKAWJJWpJC_pFBVYmb9OVKCzbAn4jPQQaRtDDtmxiZCOjyQN-Xk7dsrrFayZ16xSNJWtwcwXrdBzmjBdhSk_42OtujE_ar5k2YyU_T0vSCP0MilRIN0qYB2tMAYrRiLbGq1=w640-h418&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p data-end=&quot;420&quot; data-start=&quot;320&quot;&gt;In my previous post, &lt;em data-end=&quot;408&quot; data-start=&quot;341&quot;&gt;Simulating Ghost of Tsushima’s Iconic White Flower Field in Unity&lt;/em&gt;, I wrote:&lt;/p&gt;
&lt;blockquote data-end=&quot;491&quot; data-start=&quot;421&quot;&gt;
&lt;p data-end=&quot;491&quot; data-start=&quot;423&quot;&gt;“I hope it can eventually run just as smoothly on mobile devices.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;589&quot; data-start=&quot;493&quot;&gt;That goal was achieved sooner than expected.&lt;br data-end=&quot;540&quot; data-start=&quot;537&quot; /&gt;
And that white flower field is no longer alone.&lt;/p&gt;
&lt;p data-end=&quot;770&quot; data-start=&quot;591&quot;&gt;Today, the same architecture can support twenty types of vegetation — all processed on the GPU — handling &lt;strong data-end=&quot;767&quot; data-start=&quot;697&quot;&gt;wind simulation, interaction, painting, and culling simultaneously&lt;/strong&gt;.&lt;/p&gt;
&lt;p data-end=&quot;988&quot; data-start=&quot;772&quot;&gt;This article is not merely a showcase of results, but a record of the journey — from a simple flower field experiment to a complete vegetation system.&lt;br data-end=&quot;925&quot; data-start=&quot;922&quot; /&gt;
A journey to find what I call &lt;strong data-end=&quot;976&quot; data-start=&quot;955&quot;&gt;“poetic dynamics”&lt;/strong&gt; on the GPU.&lt;span&gt;&lt;/span&gt;&lt;/p&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;h2 data-end=&quot;1064&quot; data-start=&quot;995&quot;&gt;1. Architectural Philosophy — Let the GPU Become Nature’s Executor&lt;/h2&gt;
&lt;p data-end=&quot;1307&quot; data-start=&quot;1066&quot;&gt;The core design of this system is simple yet ambitious:&lt;br data-end=&quot;1124&quot; data-start=&quot;1121&quot; /&gt;
&lt;strong data-end=&quot;1161&quot; data-start=&quot;1124&quot;&gt;every natural dynamic computation&lt;/strong&gt; — vegetation generation, wind simulation, interaction, brush editing, and occlusion culling —&lt;br data-end=&quot;1258&quot; data-start=&quot;1255&quot; /&gt;
is done entirely inside the &lt;strong data-end=&quot;1304&quot; data-start=&quot;1286&quot;&gt;Compute Shader&lt;/strong&gt;.&lt;/p&gt;
&lt;p data-end=&quot;1474&quot; data-start=&quot;1309&quot;&gt;The CPU does only one thing: it sends the list of visible cells to the GPU — and then steps aside.&lt;br data-end=&quot;1410&quot; data-start=&quot;1407&quot; /&gt;
From there, all logic and rhythm evolve within the GPU itself.&lt;/p&gt;
&lt;p data-end=&quot;1725&quot; data-start=&quot;1476&quot;&gt;In traditional approaches, wind motion and collisions are usually computed in the Vertex Shader.&lt;br data-end=&quot;1575&quot; data-start=&quot;1572&quot; /&gt;
That means every vertex redundantly performs the same wind and displacement calculations —&lt;br data-end=&quot;1668&quot; data-start=&quot;1665&quot; /&gt;
repeating identical work for thousands of grass blades.&lt;/p&gt;
&lt;p data-end=&quot;1852&quot; data-start=&quot;1727&quot;&gt;On desktop platforms this might be tolerable,&lt;br data-end=&quot;1775&quot; data-start=&quot;1772&quot; /&gt;
but on mobile, that kind of &lt;strong data-end=&quot;1827&quot; data-start=&quot;1803&quot;&gt;repeated computation&lt;/strong&gt; comes at a steep cost.&lt;/p&gt;
&lt;p data-end=&quot;2129&quot; data-start=&quot;1854&quot;&gt;So I moved everything into the Compute Shader —&lt;br data-end=&quot;1904&quot; data-start=&quot;1901&quot; /&gt;
where each frame’s logic is calculated &lt;strong data-end=&quot;1951&quot; data-start=&quot;1943&quot;&gt;once&lt;/strong&gt; and passed to the Vertex Shader as &lt;strong data-end=&quot;2005&quot; data-start=&quot;1987&quot;&gt;RenderTextures&lt;/strong&gt;.&lt;br data-end=&quot;2009&quot; data-start=&quot;2006&quot; /&gt;
This eliminates redundant work and transforms each GPU dispatch into a self-contained &lt;strong data-end=&quot;2126&quot; data-start=&quot;2095&quot;&gt;ecological simulation cycle&lt;/strong&gt;.&lt;/p&gt;
&lt;p data-end=&quot;2216&quot; data-start=&quot;2131&quot;&gt;My goal was to let the GPU not just &lt;em data-end=&quot;2175&quot; data-start=&quot;2167&quot;&gt;render&lt;/em&gt; nature,&lt;br data-end=&quot;2186&quot; data-start=&quot;2183&quot; /&gt;
but &lt;em data-end=&quot;2202&quot; data-start=&quot;2190&quot;&gt;understand&lt;/em&gt; its rhythm.&lt;/p&gt;
&lt;p data-end=&quot;2339&quot; data-start=&quot;2218&quot;&gt;With data flow kept closed and continuous,&lt;br data-end=&quot;2263&quot; data-start=&quot;2260&quot; /&gt;
the system can maintain &lt;strong data-end=&quot;2300&quot; data-start=&quot;2287&quot;&gt;57–60 FPS&lt;/strong&gt; even on mid-to-low-end mobile devices.&lt;/p&gt;&lt;p data-end=&quot;2339&quot; data-start=&quot;2218&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiQjOebFCQDRT2Z4kYEygJIdz7lv875ivkb0suUY0cNKFKLtiD5IQpxBHTT-0R0yooTbcf7ZkU6IqGQf1Y5bEOFEoLyg0xiGPgQ3uBrI6Palb4Dk28RJ1dzvFlzwu79UWku1ZKWB1vvU--eo7GtED74_lan9KenJ6GP9EmjwoI4efpfWXB2C0aUZwPt5Yfo&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;332&quot; data-original-width=&quot;636&quot; height=&quot;334&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiQjOebFCQDRT2Z4kYEygJIdz7lv875ivkb0suUY0cNKFKLtiD5IQpxBHTT-0R0yooTbcf7ZkU6IqGQf1Y5bEOFEoLyg0xiGPgQ3uBrI6Palb4Dk28RJ1dzvFlzwu79UWku1ZKWB1vvU--eo7GtED74_lan9KenJ6GP9EmjwoI4efpfWXB2C0aUZwPt5Yfo=w640-h334&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;hr data-end=&quot;2344&quot; data-start=&quot;2341&quot; /&gt;
&lt;h2 data-end=&quot;2375&quot; data-start=&quot;2346&quot;&gt;2. Compute Shader Workflow&lt;/h2&gt;
&lt;p data-end=&quot;2534&quot; data-start=&quot;2377&quot;&gt;This is the technical heart of the entire system.&lt;br data-end=&quot;2429&quot; data-start=&quot;2426&quot; /&gt;
Every blade of grass, petal, gust of wind, and interaction is processed within a single Compute Dispatch.&lt;/p&gt;
&lt;h3 data-end=&quot;2563&quot; data-start=&quot;2536&quot;&gt;a. Cells and Visibility&lt;/h3&gt;
&lt;p data-end=&quot;2784&quot; data-start=&quot;2564&quot;&gt;The scene is divided into cells using a &lt;strong data-end=&quot;2616&quot; data-start=&quot;2604&quot;&gt;Quadtree&lt;/strong&gt; structure.&lt;br data-end=&quot;2630&quot; data-start=&quot;2627&quot; /&gt;
The CPU performs &lt;strong data-end=&quot;2671&quot; data-start=&quot;2647&quot;&gt;View Frustum Culling&lt;/strong&gt; to determine the &lt;strong data-end=&quot;2708&quot; data-start=&quot;2689&quot;&gt;VisibleCellList&lt;/strong&gt;,&lt;br data-end=&quot;2712&quot; data-start=&quot;2709&quot; /&gt;
then sends that list to the GPU.&lt;br data-end=&quot;2747&quot; data-start=&quot;2744&quot; /&gt;
That’s the only part the CPU handles.&lt;/p&gt;
&lt;h3 data-end=&quot;2840&quot; data-start=&quot;2786&quot;&gt;b. Procedural Generation and Coordinate Conversion&lt;/h3&gt;
&lt;p data-end=&quot;3118&quot; data-start=&quot;2841&quot;&gt;The GPU procedurally generates each instance’s transform and spline data,&lt;br data-end=&quot;2917&quot; data-start=&quot;2914&quot; /&gt;
using a &lt;strong data-end=&quot;2946&quot; data-start=&quot;2925&quot;&gt;Voronoi algorithm&lt;/strong&gt; to simulate natural clustering.&lt;br data-end=&quot;2981&quot; data-start=&quot;2978&quot; /&gt;
Each instance’s world coordinates are computed relative to its cell,&lt;br data-end=&quot;3052&quot; data-start=&quot;3049&quot; /&gt;
avoiding the memory and loading costs of pre-generated transforms.&lt;/p&gt;
&lt;h3 data-end=&quot;3151&quot; data-start=&quot;3120&quot;&gt;c. Unified Compute Dispatch&lt;/h3&gt;
&lt;p data-end=&quot;3212&quot; data-start=&quot;3152&quot;&gt;Within a single dispatch, all of the following are computed:&lt;/p&gt;
&lt;ul data-end=&quot;3457&quot; data-start=&quot;3213&quot;&gt;
&lt;li data-end=&quot;3301&quot; data-start=&quot;3213&quot;&gt;
&lt;p data-end=&quot;3301&quot; data-start=&quot;3215&quot;&gt;&lt;strong data-end=&quot;3237&quot; data-start=&quot;3215&quot;&gt;PerInstanceCulling&lt;/strong&gt;: view frustum, camera distance, and FilterMap-based filtering&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3379&quot; data-start=&quot;3302&quot;&gt;
&lt;p data-end=&quot;3379&quot; data-start=&quot;3304&quot;&gt;&lt;strong data-end=&quot;3323&quot; data-start=&quot;3304&quot;&gt;Wind simulation&lt;/strong&gt;: real-time calculation of wind direction and strength&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3457&quot; data-start=&quot;3380&quot;&gt;
&lt;p data-end=&quot;3457&quot; data-start=&quot;3382&quot;&gt;&lt;strong data-end=&quot;3412&quot; data-start=&quot;3382&quot;&gt;Interaction (Displacement)&lt;/strong&gt;: player or object-driven grass deformation&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;3485&quot; data-start=&quot;3459&quot;&gt;d. Writing the Results&lt;/h3&gt;
&lt;p data-end=&quot;3646&quot; data-start=&quot;3486&quot;&gt;Based on FilterMap data, the system generates &lt;strong data-end=&quot;3546&quot; data-start=&quot;3532&quot;&gt;GrassBlade&lt;/strong&gt; or &lt;strong data-end=&quot;3569&quot; data-start=&quot;3550&quot;&gt;Billboard Petal&lt;/strong&gt; instances,&lt;br data-end=&quot;3583&quot; data-start=&quot;3580&quot; /&gt;
and writes results into the corresponding &lt;strong data-end=&quot;3643&quot; data-start=&quot;3625&quot;&gt;RenderTextures&lt;/strong&gt;.&lt;/p&gt;
&lt;p data-end=&quot;3718&quot; data-start=&quot;3648&quot;&gt;Each dispatch thus becomes a complete &lt;strong data-end=&quot;3717&quot; data-start=&quot;3686&quot;&gt;cycle of natural simulation&lt;/strong&gt;.&lt;/p&gt;&lt;p data-end=&quot;3718&quot; data-start=&quot;3648&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhOtHYGZ1yoGuibNwpJ3Wl5cZgI62g71ZK3ea0xic8bKIqCZSdJCIeGgTjHUrMO0bPuVb_2vqhz3LB9U6HyluEBRaL9TAEfyPrkdBW9jC1ytVf7OQxGjNr4lpGy_rDENFQ-Fi5OcbrQGfQEqY4f5mDy1v-9TyT1L_RbRErX3D_bgWT3OF8hl0wl4wfvDr1P&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;730&quot; data-original-width=&quot;1090&quot; height=&quot;428&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhOtHYGZ1yoGuibNwpJ3Wl5cZgI62g71ZK3ea0xic8bKIqCZSdJCIeGgTjHUrMO0bPuVb_2vqhz3LB9U6HyluEBRaL9TAEfyPrkdBW9jC1ytVf7OQxGjNr4lpGy_rDENFQ-Fi5OcbrQGfQEqY4f5mDy1v-9TyT1L_RbRErX3D_bgWT3OF8hl0wl4wfvDr1P=w640-h428&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;hr data-end=&quot;3723&quot; data-start=&quot;3720&quot; /&gt;
&lt;h2 data-end=&quot;3774&quot; data-start=&quot;3725&quot;&gt;3. Vertex Shader — Petal Attachment and Motion&lt;/h2&gt;
&lt;p data-end=&quot;3870&quot; data-start=&quot;3776&quot;&gt;Petals are rendered as billboards,&lt;br data-end=&quot;3813&quot; data-start=&quot;3810&quot; /&gt;
but I didn’t want them to simply “stick” to the ground.&lt;/p&gt;
&lt;p data-end=&quot;3996&quot; data-start=&quot;3872&quot;&gt;Instead, each petal dynamically attaches to the &lt;strong data-end=&quot;3960&quot; data-start=&quot;3920&quot;&gt;tip of its corresponding grass blade&lt;/strong&gt;,&lt;br data-end=&quot;3964&quot; data-start=&quot;3961&quot; /&gt;
swaying in sync with the wind.&lt;/p&gt;
&lt;p data-end=&quot;4108&quot; data-start=&quot;3998&quot;&gt;This small detail transforms the scene from mere simulation&lt;br data-end=&quot;4060&quot; data-start=&quot;4057&quot; /&gt;
into something that feels like it’s &lt;em data-end=&quot;4107&quot; data-start=&quot;4096&quot;&gt;breathing&lt;/em&gt;.&lt;/p&gt;&lt;p data-end=&quot;4108&quot; data-start=&quot;3998&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgTp-UmNeK4fU3qGi5E9Q6vFgG_M4BCRsjJvcmxktDQGEjBal5hY1McH-A9WIqW7Xq4g1UdpDxlYvRCaa4OlcMrDwPk9_to6nLxtF0IK2mUJA1X7V7QqRPkFgZ2y3j1K-K6zOsHMapCOgYkVvI1_aYzX__bb9TIpMny9BBujSBXY-wwugQmGsleE9aa3b7n&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;715&quot; data-original-width=&quot;1200&quot; height=&quot;382&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgTp-UmNeK4fU3qGi5E9Q6vFgG_M4BCRsjJvcmxktDQGEjBal5hY1McH-A9WIqW7Xq4g1UdpDxlYvRCaa4OlcMrDwPk9_to6nLxtF0IK2mUJA1X7V7QqRPkFgZ2y3j1K-K6zOsHMapCOgYkVvI1_aYzX__bb9TIpMny9BBujSBXY-wwugQmGsleE9aa3b7n=w640-h382&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;hr data-end=&quot;4113&quot; data-start=&quot;4110&quot; /&gt;
&lt;h2 data-end=&quot;4147&quot; data-start=&quot;4115&quot;&gt;4. FilterMap and Brush System&lt;/h2&gt;
&lt;h3 data-end=&quot;4175&quot; data-start=&quot;4149&quot;&gt;a. FilterMap Structure&lt;/h3&gt;
&lt;ul data-end=&quot;4257&quot; data-start=&quot;4176&quot;&gt;
&lt;li data-end=&quot;4218&quot; data-start=&quot;4176&quot;&gt;
&lt;p data-end=&quot;4218&quot; data-start=&quot;4178&quot;&gt;&lt;strong data-end=&quot;4191&quot; data-start=&quot;4178&quot;&gt;G channel&lt;/strong&gt;: vegetation type (index)&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4257&quot; data-start=&quot;4219&quot;&gt;
&lt;p data-end=&quot;4257&quot; data-start=&quot;4221&quot;&gt;&lt;strong data-end=&quot;4234&quot; data-start=&quot;4221&quot;&gt;A channel&lt;/strong&gt;: density / intensity&lt;/p&gt;
&lt;/li&gt;&lt;li data-end=&quot;4257&quot; data-start=&quot;4219&quot;&gt;&lt;p data-end=&quot;4257&quot; data-start=&quot;4221&quot;&gt;It uses&amp;nbsp;&lt;strong data-end=&quot;4287&quot; data-start=&quot;4267&quot;&gt;ASTC compression&lt;/strong&gt;&amp;nbsp;to keep memory usage under control for large open worlds.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEidDXf-ZO0TvnjoFE1REuWidcvpW1kj0FgQ40xaIWw2-72kTgn0i2gB-sgQBaE4zWHgGJ0jBWdPq00biqtnZOZbORzZ95ijLdt8KeKKlVFxSUY4V2AORvJMyADkakwbqRLXBWKuokFmg9DThQjica-nqSVrmF1jJTKfs79351UtCAcVYvBtx4IN_fUcDN3M&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;718&quot; data-original-width=&quot;747&quot; height=&quot;614&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEidDXf-ZO0TvnjoFE1REuWidcvpW1kj0FgQ40xaIWw2-72kTgn0i2gB-sgQBaE4zWHgGJ0jBWdPq00biqtnZOZbORzZ95ijLdt8KeKKlVFxSUY4V2AORvJMyADkakwbqRLXBWKuokFmg9DThQjica-nqSVrmF1jJTKfs79351UtCAcVYvBtx4IN_fUcDN3M=w640-h614&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3 data-end=&quot;4372&quot; data-start=&quot;4347&quot;&gt;b. GPU-Based Painting&lt;/h3&gt;
&lt;p data-end=&quot;4570&quot; data-start=&quot;4373&quot;&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Brush editing is handled entirely on the GPU via &lt;strong data-end=&quot;4441&quot; data-start=&quot;4422&quot;&gt;Compute Shaders&lt;/strong&gt;, supporting &lt;strong data-end=&quot;4480&quot; data-start=&quot;4456&quot;&gt;TextureArray editing&lt;/strong&gt; directly.&lt;/li&gt;&lt;li&gt;All brush results are written to RenderTextures — no CPU feedback required.&lt;/li&gt;&lt;li&gt;Saving is done asynchronously with &lt;strong data-end=&quot;4635&quot; data-start=&quot;4607&quot;&gt;AsyncGPUReadback.Request&lt;/strong&gt;, allowing smooth, real-time saving of multiple FilterMap layers even in large scenes.&lt;/li&gt;&lt;/ul&gt;
&lt;p data-end=&quot;4837&quot; data-start=&quot;4727&quot;&gt;This design lets artists &lt;em data-end=&quot;4780&quot; data-start=&quot;4752&quot;&gt;literally paint landscapes&lt;/em&gt; —&lt;br data-end=&quot;4785&quot; data-start=&quot;4782&quot; /&gt;
while keeping full consistency within GPU data flow.&lt;/p&gt;&lt;hr data-end=&quot;4842&quot; data-start=&quot;4839&quot; /&gt;
&lt;h2 data-end=&quot;4880&quot; data-start=&quot;4844&quot;&gt;5. RenderTexture Data Flow Design&lt;/h2&gt;
&lt;p data-end=&quot;5054&quot; data-start=&quot;4882&quot;&gt;I initially used &lt;strong data-end=&quot;4915&quot; data-start=&quot;4899&quot;&gt;AppendBuffer&lt;/strong&gt;, but found it unstable on mobile devices.&lt;br data-end=&quot;4960&quot; data-start=&quot;4957&quot; /&gt;
Some GPUs have limited support for unordered buffers,&lt;br data-end=&quot;5016&quot; data-start=&quot;5013&quot; /&gt;
leading to inconsistent performance.&lt;/p&gt;
&lt;p data-end=&quot;5281&quot; data-start=&quot;5056&quot;&gt;Switching to &lt;strong data-end=&quot;5087&quot; data-start=&quot;5069&quot;&gt;RenderTextures&lt;/strong&gt; for all data storage solved this:&lt;br data-end=&quot;5124&quot; data-start=&quot;5121&quot; /&gt;
each channel now holds a different attribute — position, height, index, or type —&lt;br data-end=&quot;5208&quot; data-start=&quot;5205&quot; /&gt;
and integrates seamlessly with shaders, brushes, and occlusion systems.&lt;/p&gt;
&lt;p data-end=&quot;5395&quot; data-start=&quot;5283&quot;&gt;This shift significantly improved compatibility&lt;br data-end=&quot;5333&quot; data-start=&quot;5330&quot; /&gt;
and maintained &lt;strong data-end=&quot;5361&quot; data-start=&quot;5348&quot;&gt;57–60 FPS&lt;/strong&gt; even on mid-tier mobile hardware.&lt;/p&gt;&lt;p data-end=&quot;5395&quot; data-start=&quot;5283&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiA_oAO7kSjzqkwybQrfouI-BnniSGWN_4Iq7KBdX8F9rj2TdnKlBb5nIR_q-twMSE-Bts_q4SkHX90HtD64W8t5CN5k2CiG-gkNkzllea5oc0TT-eTPb_Wm9pyFcqSedzNxiSPQXoZt3sjaivtvwhFAVY7jmqFWPFlWZZFG_rVoHSCs1rGqE4k82Ze8J72&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;323&quot; data-original-width=&quot;601&quot; height=&quot;344&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiA_oAO7kSjzqkwybQrfouI-BnniSGWN_4Iq7KBdX8F9rj2TdnKlBb5nIR_q-twMSE-Bts_q4SkHX90HtD64W8t5CN5k2CiG-gkNkzllea5oc0TT-eTPb_Wm9pyFcqSedzNxiSPQXoZt3sjaivtvwhFAVY7jmqFWPFlWZZFG_rVoHSCs1rGqE4k82Ze8J72=w640-h344&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;hr data-end=&quot;5400&quot; data-start=&quot;5397&quot; /&gt;
&lt;h2 data-end=&quot;5450&quot; data-start=&quot;5402&quot;&gt;6. HeightMap Raycasting and Ground Conformity&lt;/h2&gt;
&lt;p data-end=&quot;5582&quot; data-start=&quot;5452&quot;&gt;During the editing phase, the system continuously performs &lt;strong data-end=&quot;5535&quot; data-start=&quot;5511&quot;&gt;HeightMap raycasting&lt;/strong&gt;&lt;br data-end=&quot;5538&quot; data-start=&quot;5535&quot; /&gt;
to capture terrain and object height data.&lt;/p&gt;
&lt;p data-end=&quot;5722&quot; data-start=&quot;5584&quot;&gt;That data is used to align vegetation precisely to the surface —&lt;br data-end=&quot;5651&quot; data-start=&quot;5648&quot; /&gt;
ensuring every blade and petal follows the true contour of the scene.&lt;/p&gt;
&lt;p data-end=&quot;5841&quot; data-start=&quot;5724&quot;&gt;The results are shown instantly to the artist,&lt;br data-end=&quot;5773&quot; data-start=&quot;5770&quot; /&gt;
enabling true &lt;em data-end=&quot;5817&quot; data-start=&quot;5787&quot;&gt;what-you-see-is-what-you-get&lt;/em&gt; vegetation placement.&lt;/p&gt;
&lt;p data-end=&quot;5962&quot; data-start=&quot;5843&quot;&gt;Once editing is complete, the HeightMap is saved as a static texture,&lt;br data-end=&quot;5915&quot; data-start=&quot;5912&quot; /&gt;
and no further raycasting is done at runtime.&lt;/p&gt;
&lt;p data-end=&quot;6085&quot; data-start=&quot;5964&quot;&gt;This approach strikes the perfect balance:&lt;br data-end=&quot;6009&quot; data-start=&quot;6006&quot; /&gt;
&lt;strong data-end=&quot;6050&quot; data-start=&quot;6009&quot;&gt;accurate editing, lightweight runtime&lt;/strong&gt; —&lt;br data-end=&quot;6055&quot; data-start=&quot;6052&quot; /&gt;
smooth, stable, and efficient.&lt;/p&gt;&lt;p data-end=&quot;6085&quot; data-start=&quot;5964&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiv8uwuVoCx3nSxIliu4qMj3xrgD24Zra-yR5DNSZLoXkOB67EEUeE3cU55qIJwBffG1E2FWI4jggA2-o95NCgWpooh7pOokl03BA-FddVaXtWYkg-eAD9mRO2DKWR2JQoOQSb0B0spy4ZG8oNChbpqkRNr0sOQlMyFJI9qfW3X6Sy8W5RNA7YOTvfUZJst&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;730&quot; data-original-width=&quot;1047&quot; height=&quot;446&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiv8uwuVoCx3nSxIliu4qMj3xrgD24Zra-yR5DNSZLoXkOB67EEUeE3cU55qIJwBffG1E2FWI4jggA2-o95NCgWpooh7pOokl03BA-FddVaXtWYkg-eAD9mRO2DKWR2JQoOQSb0B0spy4ZG8oNChbpqkRNr0sOQlMyFJI9qfW3X6Sy8W5RNA7YOTvfUZJst=w640-h446&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;hr data-end=&quot;6090&quot; data-start=&quot;6087&quot; /&gt;&lt;h2 data-end=&quot;6121&quot; data-start=&quot;6092&quot;&gt;7. Mobile Performance Test&lt;/h2&gt;&lt;p data-end=&quot;6325&quot; data-start=&quot;6123&quot;&gt;All tests were performed on a &lt;strong data-end=&quot;6175&quot; data-start=&quot;6153&quot;&gt;Samsung Galaxy S10&lt;/strong&gt;.&lt;br data-end=&quot;6179&quot; data-start=&quot;6176&quot; /&gt;
Even on this mid-range device, the system maintained a steady &lt;strong data-end=&quot;6254&quot; data-start=&quot;6241&quot;&gt;57–60 FPS&lt;/strong&gt;.&lt;br data-end=&quot;6258&quot; data-start=&quot;6255&quot; /&gt;
Initialization time was between &lt;strong data-end=&quot;6301&quot; data-start=&quot;6290&quot;&gt;0–65 ms&lt;/strong&gt;, essentially instant.&lt;/p&gt;&lt;p data-end=&quot;6085&quot; data-start=&quot;5964&quot;&gt;


&lt;/p&gt;&lt;p data-end=&quot;6532&quot; data-start=&quot;6327&quot;&gt;For smoother video recording, I used a &lt;strong data-end=&quot;6395&quot; data-start=&quot;6366&quot;&gt;Samsung Galaxy Z Fold2 5G&lt;/strong&gt;,&lt;br data-end=&quot;6399&quot; data-start=&quot;6396&quot; /&gt;
which further confirmed that this GPU-only workflow&lt;br data-end=&quot;6453&quot; data-start=&quot;6450&quot; /&gt;
is not just theoretically sound — it’s &lt;em data-end=&quot;6512&quot; data-start=&quot;6492&quot;&gt;practically proven&lt;/em&gt; on mobile hardware.&lt;/p&gt;&lt;p data-end=&quot;6532&quot; data-start=&quot;6327&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEh201voBSn_T70LJhimqJBXrHv_OidyCWn5dVslqIJ7St6xTE6vLZ20TcZMMu49bJLZ0tVzO6h_xXtVUMhZ_NMdPLglOeDL5C29gSFC-tqzbZBbWAGUJZMGhHvb6IamN-E_GuJTSaiFI4-hN6BfqCqEgZn95QVHeUnvn83BbRcy8D-w5R215CHE_0dbYQAB&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;661&quot; data-original-width=&quot;1200&quot; height=&quot;352&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEh201voBSn_T70LJhimqJBXrHv_OidyCWn5dVslqIJ7St6xTE6vLZ20TcZMMu49bJLZ0tVzO6h_xXtVUMhZ_NMdPLglOeDL5C29gSFC-tqzbZBbWAGUJZMGhHvb6IamN-E_GuJTSaiFI4-hN6BfqCqEgZn95QVHeUnvn83BbRcy8D-w5R215CHE_0dbYQAB=w640-h352&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;hr data-end=&quot;6537&quot; data-start=&quot;6534&quot; /&gt;&lt;h2 data-end=&quot;6601&quot; data-start=&quot;6539&quot;&gt;8. Epilogue — The Poetry of the GPU and the Logic of Nature&lt;/h2&gt;&lt;p data-end=&quot;6707&quot; data-start=&quot;6603&quot;&gt;It all began with a simple wish —&lt;br data-end=&quot;6639&quot; data-start=&quot;6636&quot; /&gt;
to recreate the swaying white flower field of &lt;em data-end=&quot;6704&quot; data-start=&quot;6685&quot;&gt;Ghost of Tsushima&lt;/em&gt;.&lt;/p&gt;&lt;p data-end=&quot;6819&quot; data-start=&quot;6709&quot;&gt;But through countless iterations,&lt;br data-end=&quot;6745&quot; data-start=&quot;6742&quot; /&gt;
the experiment grew into a tool capable of expressing &lt;strong data-end=&quot;6816&quot; data-start=&quot;6799&quot;&gt;wind and life&lt;/strong&gt;.&lt;/p&gt;&lt;p data-end=&quot;6944&quot; data-start=&quot;6821&quot;&gt;I realized that the GPU is more than a processor —&lt;br data-end=&quot;6874&quot; data-start=&quot;6871&quot; /&gt;
it can be part of nature, capable of expressing &lt;em data-end=&quot;6941&quot; data-start=&quot;6922&quot;&gt;breath and rhythm&lt;/em&gt;.&lt;/p&gt;&lt;p data-end=&quot;7000&quot; data-start=&quot;6946&quot;&gt;And thus, this system was named &lt;strong data-end=&quot;6997&quot; data-start=&quot;6978&quot;&gt;GPUFloraPainter&lt;/strong&gt;.&lt;/p&gt;&lt;p data-end=&quot;6532&quot; data-start=&quot;6327&quot;&gt;





&lt;/p&gt;&lt;p data-end=&quot;7138&quot; data-start=&quot;7002&quot;&gt;Its birth is not just the completion of a technology —&lt;br data-end=&quot;7059&quot; data-start=&quot;7056&quot; /&gt;
but the discovery that, in the digital world,&lt;br data-end=&quot;7107&quot; data-start=&quot;7104&quot; /&gt;
&lt;strong data-end=&quot;7138&quot; data-start=&quot;7107&quot;&gt;poetry too can be computed.&lt;/strong&gt;&lt;/p&gt;&lt;hr data-end=&quot;7143&quot; data-start=&quot;7140&quot; /&gt;&lt;h2 data-end=&quot;7164&quot; data-start=&quot;7145&quot;&gt;9. Related Video&lt;/h2&gt;&lt;p data-end=&quot;7138&quot; data-start=&quot;7002&quot;&gt;&lt;strong data-end=&quot;7138&quot; data-start=&quot;7107&quot;&gt;

&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;626&quot; src=&quot;https://www.youtube.com/embed/Tz2pXekPOpA&quot; width=&quot;754&quot; youtube-src-id=&quot;Tz2pXekPOpA&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;p data-end=&quot;7197&quot; data-start=&quot;7166&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;hr data-end=&quot;7202&quot; data-start=&quot;7199&quot; /&gt;&lt;h2 data-end=&quot;7235&quot; data-start=&quot;7204&quot;&gt;10. 🌸 You Can Find It Here…&lt;/h2&gt;&lt;p data-end=&quot;7197&quot; data-start=&quot;7166&quot;&gt;&lt;em data-end=&quot;7197&quot; data-start=&quot;7166&quot;&gt;

&lt;/em&gt;&lt;/p&gt;&lt;p data-end=&quot;7265&quot; data-start=&quot;7237&quot;&gt;&lt;em data-end=&quot;7265&quot; data-start=&quot;7237&quot;&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/tools/painting/gpuflorapainter-343612&quot;&gt;GPUFloraPainter&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;hr data-end=&quot;7270&quot; data-start=&quot;7267&quot; /&gt;&lt;h2 data-end=&quot;7292&quot; data-start=&quot;7272&quot;&gt;11. Other Assets&lt;/h2&gt;&lt;p data-end=&quot;7265&quot; data-start=&quot;7237&quot;&gt;&lt;em data-end=&quot;7265&quot; data-start=&quot;7237&quot;&gt;

&lt;/em&gt;&lt;/p&gt;&lt;p data-end=&quot;7339&quot; data-start=&quot;7294&quot;&gt;&lt;em data-end=&quot;7339&quot; data-start=&quot;7294&quot;&gt;&lt;a href=&quot;https://assetstore.unity.com/publishers/86820&quot;&gt;SmallBurger Assets&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/1397416059933890985/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/12/gpu-driven-poetic-dynamics-from.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/1397416059933890985'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/1397416059933890985'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/12/gpu-driven-poetic-dynamics-from.html' title='🌸GPU-Driven Poetic Dynamics: From Simulating Ghost of Tsushima’s Iconic Flower Field to the Birth of a Complete Vegetation System'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEgBilGZoX1955OmCNOidhtASfPMyvDRFSIh6dMuZa2eurHGVK-rLgqRfFVWsTKAWJJWpJC_pFBVYmb9OVKCzbAn4jPQQaRtDDtmxiZCOjyQN-Xk7dsrrFayZ16xSNJWtwcwXrdBzmjBdhSk_42OtujE_ar5k2YyU_T0vSCP0MilRIN0qYB2tMAYrRiLbGq1=s72-w640-h418-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-506197479102316346</id><published>2025-11-25T23:49:00.002+08:00</published><updated>2025-11-25T23:52:51.711+08:00</updated><title type='text'>Camera-Adherent GPU Particles 1.0.3 — Multi-Light, Forward+, and Hybrid Lighting Upgrade</title><content type='html'>&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEifu1JeXhcIi9x_-GwOJiWiJ1YiD3iHASHYntHdZSAfwiGQ_xvm6SQKWd6Sn1-R69X1_md8R2Or9k1JEc6tl1HTwDaZP_wtlrZO_K8RiGbVhk5P1CicKi35kM6kzjfuzV4GhvQBlkrdEIipHk7MMeDPH4At5DwN-yZoaS_SXXl6bv-qvCXHB_7OazlKl80R&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;776&quot; data-original-width=&quot;1354&quot; height=&quot;366&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEifu1JeXhcIi9x_-GwOJiWiJ1YiD3iHASHYntHdZSAfwiGQ_xvm6SQKWd6Sn1-R69X1_md8R2Or9k1JEc6tl1HTwDaZP_wtlrZO_K8RiGbVhk5P1CicKi35kM6kzjfuzV4GhvQBlkrdEIipHk7MMeDPH4At5DwN-yZoaS_SXXl6bv-qvCXHB_7OazlKl80R=w640-h366&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The 1.0.3 update brings a major visual improvement to the &lt;strong data-end=&quot;855&quot; data-start=&quot;823&quot;&gt;Camera-Adherent GPU Particle&lt;/strong&gt; system.&lt;p&gt;&lt;/p&gt;&lt;p data-end=&quot;1102&quot; data-start=&quot;765&quot;&gt;
This release focuses purely on &lt;strong data-end=&quot;943&quot; data-start=&quot;897&quot;&gt;foreground, camera-facing particle effects&lt;/strong&gt;—such as wind swirls, impact bursts, weapon trails, magic glows, atmosphere streaks, UI-adjacent FX, and any particle that stays tightly aligned to the screen.&lt;/p&gt;
&lt;p data-end=&quot;1278&quot; data-start=&quot;1104&quot;&gt;These particles now react to lighting in a way that matches the rest of your scene’s illumination—without losing the performance advantages of a camera-adherent GPU pipeline.&lt;span&gt;&lt;/span&gt;&lt;/p&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;p&gt;&lt;/p&gt;
&lt;hr data-end=&quot;1283&quot; data-start=&quot;1280&quot; /&gt;
&lt;h2 data-end=&quot;1347&quot; data-start=&quot;1285&quot;&gt;🌟 &lt;strong data-end=&quot;1347&quot; data-start=&quot;1291&quot;&gt;1. Full Multi-Light Support (URP Forward &amp;amp; Forward+)&lt;/strong&gt;&lt;/h2&gt;
&lt;p data-end=&quot;1469&quot; data-start=&quot;1349&quot;&gt;Camera-adherent particles traditionally rely on unlit shading because they live in screen-space rather than world-space.&lt;/p&gt;
&lt;p data-end=&quot;1496&quot; data-start=&quot;1471&quot;&gt;This update changes that.&lt;/p&gt;
&lt;ul data-end=&quot;1773&quot; data-start=&quot;1498&quot;&gt;
&lt;li data-end=&quot;1576&quot; data-start=&quot;1498&quot;&gt;
&lt;p data-end=&quot;1576&quot; data-start=&quot;1500&quot;&gt;&lt;strong data-end=&quot;1531&quot; data-start=&quot;1500&quot;&gt;Point lights and spotlights&lt;/strong&gt; now illuminate camera-aligned GPU particles.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1654&quot; data-start=&quot;1577&quot;&gt;
&lt;p data-end=&quot;1654&quot; data-start=&quot;1579&quot;&gt;Correct &lt;strong data-end=&quot;1596&quot; data-start=&quot;1587&quot;&gt;color&lt;/strong&gt;, &lt;strong data-end=&quot;1611&quot; data-start=&quot;1598&quot;&gt;intensity&lt;/strong&gt;, and &lt;strong data-end=&quot;1641&quot; data-start=&quot;1617&quot;&gt;distance attenuation&lt;/strong&gt; are applied.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1773&quot; data-start=&quot;1655&quot;&gt;
&lt;p data-end=&quot;1773&quot; data-start=&quot;1657&quot;&gt;Fully compatible with &lt;strong data-end=&quot;1714&quot; data-start=&quot;1679&quot;&gt;URP Forward+ clustered lighting&lt;/strong&gt;, enabling dozens of realtime lights on supported hardware.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1917&quot; data-start=&quot;1775&quot;&gt;Even though the particles are screen-aligned, the shading now reflects world lighting conditions—adding depth and realism to close-up effects.&lt;/p&gt;
&lt;hr data-end=&quot;1922&quot; data-start=&quot;1919&quot; /&gt;
&lt;h2 data-end=&quot;1991&quot; data-start=&quot;1924&quot;&gt;🌈 &lt;strong data-end=&quot;1991&quot; data-start=&quot;1930&quot;&gt;2. Hybrid Lighting Pipeline (Auto Per-Pixel / Per-Vertex)&lt;/strong&gt;&lt;/h2&gt;
&lt;p data-end=&quot;2127&quot; data-start=&quot;1993&quot;&gt;To balance visual fidelity and performance—especially on &lt;strong data-end=&quot;2079&quot; data-start=&quot;2050&quot;&gt;mobile and mid-range GPUs&lt;/strong&gt;—the lighting pipeline now adapts automatically:&lt;/p&gt;
&lt;div class=&quot;_tableContainer_1rjym_1&quot;&gt;&lt;div class=&quot;group _tableWrapper_1rjym_13 flex w-fit flex-col-reverse&quot; tabindex=&quot;-1&quot;&gt;&lt;table class=&quot;w-fit min-w-(--thread-content-width)&quot; data-end=&quot;2521&quot; data-start=&quot;2129&quot;&gt;&lt;thead data-end=&quot;2174&quot; data-start=&quot;2129&quot;&gt;&lt;tr data-end=&quot;2174&quot; data-start=&quot;2129&quot;&gt;&lt;th data-col-size=&quot;sm&quot; data-end=&quot;2146&quot; data-start=&quot;2129&quot;&gt;Material Setup&lt;/th&gt;&lt;th data-col-size=&quot;sm&quot; data-end=&quot;2162&quot; data-start=&quot;2146&quot;&gt;Lighting Mode&lt;/th&gt;&lt;th data-col-size=&quot;md&quot; data-end=&quot;2174&quot; data-start=&quot;2162&quot;&gt;Behavior&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody data-end=&quot;2521&quot; data-start=&quot;2222&quot;&gt;&lt;tr data-end=&quot;2321&quot; data-start=&quot;2222&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2247&quot; data-start=&quot;2222&quot;&gt;&lt;strong data-end=&quot;2246&quot; data-start=&quot;2224&quot;&gt;Normal Map enabled&lt;/strong&gt;&lt;/td&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2268&quot; data-start=&quot;2247&quot;&gt;Per-Pixel Lighting&lt;/td&gt;&lt;td data-col-size=&quot;md&quot; data-end=&quot;2321&quot; data-start=&quot;2268&quot;&gt;High-quality shading for glossy, detailed effects&lt;/td&gt;&lt;/tr&gt;&lt;tr data-end=&quot;2424&quot; data-start=&quot;2322&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2342&quot; data-start=&quot;2322&quot;&gt;&lt;strong data-end=&quot;2341&quot; data-start=&quot;2324&quot;&gt;No Normal Map&lt;/strong&gt;&lt;/td&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2364&quot; data-start=&quot;2342&quot;&gt;Per-Vertex Lighting&lt;/td&gt;&lt;td data-col-size=&quot;md&quot; data-end=&quot;2424&quot; data-start=&quot;2364&quot;&gt;High-performance mode without sacrificing color accuracy&lt;/td&gt;&lt;/tr&gt;&lt;tr data-end=&quot;2521&quot; data-start=&quot;2425&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2450&quot; data-start=&quot;2425&quot;&gt;&lt;strong data-end=&quot;2449&quot; data-start=&quot;2427&quot;&gt;Storm Mode enabled&lt;/strong&gt;&lt;/td&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2470&quot; data-start=&quot;2450&quot;&gt;Lighting disabled&lt;/td&gt;&lt;td data-col-size=&quot;md&quot; data-end=&quot;2521&quot; data-start=&quot;2470&quot;&gt;Recommended to avoid combining Storm + Lighting&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p data-end=&quot;2659&quot; data-start=&quot;2523&quot;&gt;This pipeline ensures that camera-adherent particles stay lightweight while still supporting physically meaningful lighting when needed.&lt;/p&gt;
&lt;hr data-end=&quot;2664&quot; data-start=&quot;2661&quot; /&gt;
&lt;h2 data-end=&quot;2699&quot; data-start=&quot;2666&quot;&gt;🌫️ &lt;strong data-end=&quot;2699&quot; data-start=&quot;2673&quot;&gt;3. URP Fog Integration&lt;/strong&gt;&lt;/h2&gt;
&lt;p data-end=&quot;2746&quot; data-start=&quot;2701&quot;&gt;Camera-aligned particles now respect URP fog:&lt;/p&gt;
&lt;ul data-end=&quot;2854&quot; data-start=&quot;2748&quot;&gt;
&lt;li data-end=&quot;2779&quot; data-start=&quot;2748&quot;&gt;
&lt;p data-end=&quot;2779&quot; data-start=&quot;2750&quot;&gt;Smooth depth-aware blending&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2816&quot; data-start=&quot;2780&quot;&gt;
&lt;p data-end=&quot;2816&quot; data-start=&quot;2782&quot;&gt;Consistent screen-space behavior&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2854&quot; data-start=&quot;2817&quot;&gt;
&lt;p data-end=&quot;2854&quot; data-start=&quot;2819&quot;&gt;Matches scene mood and atmosphere&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2968&quot; data-start=&quot;2856&quot;&gt;This prevents the visual mismatch that screen-aligned particles often have in foggy or atmospheric environments.&lt;/p&gt;
&lt;hr data-end=&quot;2973&quot; data-start=&quot;2970&quot; /&gt;
&lt;h2 data-end=&quot;3018&quot; data-start=&quot;2975&quot;&gt;🔧 &lt;strong data-end=&quot;3018&quot; data-start=&quot;2981&quot;&gt;4. Naming Consistency Improvement&lt;/strong&gt;&lt;/h2&gt;
&lt;p data-end=&quot;3043&quot; data-start=&quot;3020&quot;&gt;The material parameter:&lt;/p&gt;
&lt;p data-end=&quot;3071&quot; data-start=&quot;3045&quot;&gt;&lt;code data-end=&quot;3054&quot; data-start=&quot;3045&quot;&gt;MaskMap&lt;/code&gt; → &lt;code data-end=&quot;3071&quot; data-start=&quot;3057&quot;&gt;lightMaskMap&lt;/code&gt;&lt;/p&gt;
&lt;p data-end=&quot;3189&quot; data-start=&quot;3073&quot;&gt;This better reflects its role in controlling &lt;strong data-end=&quot;3156&quot; data-start=&quot;3118&quot;&gt;specular and emission contribution&lt;/strong&gt; under the new lighting pipeline.&lt;/p&gt;
&lt;hr data-end=&quot;3194&quot; data-start=&quot;3191&quot; /&gt;
&lt;h2 data-end=&quot;3221&quot; data-start=&quot;3196&quot;&gt;📝 &lt;strong data-end=&quot;3221&quot; data-start=&quot;3202&quot;&gt;1.0.3 Changelog&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 data-end=&quot;3232&quot; data-start=&quot;3223&quot;&gt;Fixes&lt;/h3&gt;
&lt;ul data-end=&quot;3377&quot; data-start=&quot;3233&quot;&gt;
&lt;li data-end=&quot;3286&quot; data-start=&quot;3233&quot;&gt;
&lt;p data-end=&quot;3286&quot; data-start=&quot;3235&quot;&gt;Renamed &lt;strong data-end=&quot;3273&quot; data-start=&quot;3243&quot;&gt;&lt;code data-end=&quot;3254&quot; data-start=&quot;3245&quot;&gt;MaskMap&lt;/code&gt; → &lt;code data-end=&quot;3271&quot; data-start=&quot;3257&quot;&gt;lightMaskMap&lt;/code&gt;&lt;/strong&gt; for clarity.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3377&quot; data-start=&quot;3287&quot;&gt;
&lt;p data-end=&quot;3377&quot; data-start=&quot;3289&quot;&gt;Fixed an issue where &lt;strong data-end=&quot;3326&quot; data-start=&quot;3310&quot;&gt;lightMaskMap&lt;/strong&gt; did not correctly influence specular and emission.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;3395&quot; data-start=&quot;3379&quot;&gt;Improvements&lt;/h3&gt;
&lt;ul data-end=&quot;3726&quot; data-start=&quot;3396&quot;&gt;
&lt;li data-end=&quot;3505&quot; data-start=&quot;3396&quot;&gt;
&lt;p data-end=&quot;3423&quot; data-start=&quot;3398&quot;&gt;&lt;strong data-end=&quot;3421&quot; data-start=&quot;3398&quot;&gt;Multi-light support&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-end=&quot;3505&quot; data-start=&quot;3426&quot;&gt;
&lt;li data-end=&quot;3471&quot; data-start=&quot;3426&quot;&gt;
&lt;p data-end=&quot;3471&quot; data-start=&quot;3428&quot;&gt;URP Forward &amp;amp; Forward+ clustered lighting&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3505&quot; data-start=&quot;3474&quot;&gt;
&lt;p data-end=&quot;3505&quot; data-start=&quot;3476&quot;&gt;Correct color &amp;amp; attenuation&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3647&quot; data-start=&quot;3506&quot;&gt;
&lt;p data-end=&quot;3538&quot; data-start=&quot;3508&quot;&gt;&lt;strong data-end=&quot;3536&quot; data-start=&quot;3508&quot;&gt;Hybrid Lighting Pipeline&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-end=&quot;3647&quot; data-start=&quot;3541&quot;&gt;
&lt;li data-end=&quot;3596&quot; data-start=&quot;3541&quot;&gt;
&lt;p data-end=&quot;3596&quot; data-start=&quot;3543&quot;&gt;Per-Pixel (normal map) / Per-Vertex (no normal map)&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3647&quot; data-start=&quot;3599&quot;&gt;
&lt;p data-end=&quot;3647&quot; data-start=&quot;3601&quot;&gt;Storm Mode disables lighting for consistency&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3726&quot; data-start=&quot;3648&quot;&gt;
&lt;p data-end=&quot;3671&quot; data-start=&quot;3650&quot;&gt;&lt;strong data-end=&quot;3669&quot; data-start=&quot;3650&quot;&gt;URP Fog support&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-end=&quot;3726&quot; data-start=&quot;3674&quot;&gt;
&lt;li data-end=&quot;3726&quot; data-start=&quot;3674&quot;&gt;
&lt;p data-end=&quot;3726&quot; data-start=&quot;3676&quot;&gt;Proper fog blending for camera-aligned particles&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;3731&quot; data-start=&quot;3728&quot; /&gt;
&lt;h2 data-end=&quot;3749&quot; data-start=&quot;3733&quot;&gt;🎬 Demo Video&lt;/h2&gt;
&lt;p data-end=&quot;3784&quot; data-start=&quot;3751&quot;&gt;&lt;em data-end=&quot;3784&quot; data-start=&quot;3751&quot;&gt;&lt;/em&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;em data-end=&quot;3784&quot; data-start=&quot;3751&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;529&quot; src=&quot;https://www.youtube.com/embed/52oplffJNNA&quot; width=&quot;637&quot; youtube-src-id=&quot;52oplffJNNA&quot;&gt;&lt;/iframe&gt;&lt;/em&gt;&lt;/div&gt;&lt;em data-end=&quot;3784&quot; data-start=&quot;3751&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/em&gt;&lt;div&gt;&lt;em data-end=&quot;3784&quot; data-start=&quot;3751&quot;&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/vfx/particles/environment/camera-adherent-gpu-particles-278222&quot;&gt;Get Camera-Adherent GPU Particles&lt;/a&gt;&lt;br /&gt;&lt;/em&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://assetstore.unity.com/publishers/86820&quot;&gt;All SmallBurger Assets on the Unity Asset Store&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/506197479102316346/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/11/camera-adherent-gpu-particles-103-multi.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/506197479102316346'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/506197479102316346'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/11/camera-adherent-gpu-particles-103-multi.html' title='Camera-Adherent GPU Particles 1.0.3 — Multi-Light, Forward+, and Hybrid Lighting Upgrade'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEifu1JeXhcIi9x_-GwOJiWiJ1YiD3iHASHYntHdZSAfwiGQ_xvm6SQKWd6Sn1-R69X1_md8R2Or9k1JEc6tl1HTwDaZP_wtlrZO_K8RiGbVhk5P1CicKi35kM6kzjfuzV4GhvQBlkrdEIipHk7MMeDPH4At5DwN-yZoaS_SXXl6bv-qvCXHB_7OazlKl80R=s72-w640-h366-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-826214117567538579</id><published>2025-10-13T13:36:00.003+08:00</published><updated>2025-10-13T13:37:45.457+08:00</updated><title type='text'>Simulating Ghost of Tsushima’s Iconic White Flower Field in Unity</title><content type='html'>&lt;p&gt;&amp;nbsp;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgfbFZtuT0CL6g07AMhiTzwVw-rEHYqJGHOfLFxErno2HkWJLpND4NERU01WMkxmrhG7moO_3sn9berFiYdyBp8M_eROcktKT6bCWHFtPBtsScLfb-RQuvLXzrQWjNowNOQe_PylyFiBPtPhAHY0NB9U3pWWqBU8iL3sRXutKvyAIUJzGusashLh0vDuKLH&quot; style=&quot;margin-left: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img data-original-height=&quot;468&quot; data-original-width=&quot;786&quot; height=&quot;382&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgfbFZtuT0CL6g07AMhiTzwVw-rEHYqJGHOfLFxErno2HkWJLpND4NERU01WMkxmrhG7moO_3sn9berFiYdyBp8M_eROcktKT6bCWHFtPBtsScLfb-RQuvLXzrQWjNowNOQe_PylyFiBPtPhAHY0NB9U3pWWqBU8iL3sRXutKvyAIUJzGusashLh0vDuKLH=w640-h382&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p data-end=&quot;369&quot; data-start=&quot;190&quot;&gt;I’ve always been fascinated by the vegetation system in &lt;em data-end=&quot;265&quot; data-start=&quot;246&quot;&gt;&lt;b&gt;Ghost of Tsushima&lt;/b&gt;&lt;/em&gt;.&lt;br data-end=&quot;269&quot; data-start=&quot;266&quot; /&gt;
After finishing the grass part earlier, I finally decided to tackle the iconic white &lt;b&gt;flower field&lt;/b&gt;.&lt;span&gt;&lt;/span&gt;&lt;/p&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;
&lt;/p&gt;&lt;p data-end=&quot;721&quot; data-start=&quot;371&quot;&gt;To keep the performance suitable for mobile platforms, the flowers are rendered using &lt;strong data-end=&quot;471&quot; data-start=&quot;457&quot;&gt;Billboards&lt;/strong&gt;,&lt;br data-end=&quot;475&quot; data-start=&quot;472&quot; /&gt;
while the stems are generated procedurally through a &lt;strong data-end=&quot;550&quot; data-start=&quot;528&quot;&gt;Noise Spline Blade&lt;/strong&gt; system.&lt;br data-end=&quot;561&quot; data-start=&quot;558&quot; /&gt;
The main challenge here is making each &lt;strong data-end=&quot;667&quot; data-start=&quot;600&quot;&gt;Billboard follow the endpoint of its corresponding Spline Blade&lt;/strong&gt;,&lt;br data-end=&quot;671&quot; data-start=&quot;668&quot; /&gt;
so the flowers can sway naturally with the wind.&lt;/p&gt;&lt;p data-end=&quot;1055&quot; data-start=&quot;723&quot;&gt;This was also my first attempt to handle &lt;strong data-end=&quot;824&quot; data-start=&quot;764&quot;&gt;multiple instance types within a single Compute Dispatch&lt;/strong&gt; — managing both the simulation and culling processes together.&lt;br data-end=&quot;892&quot; data-start=&quot;889&quot; /&gt;
Honestly, I didn’t expect the Billboard effect to look this good,&lt;br data-end=&quot;960&quot; data-start=&quot;957&quot; /&gt;
but once I saw the entire field moving with the wind, it completely exceeded my expectations.&lt;/p&gt;&lt;p data-end=&quot;1055&quot; data-start=&quot;723&quot;&gt;Youtube Video Link：&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;560&quot; src=&quot;https://www.youtube.com/embed/vepKFfY7MG0&quot; width=&quot;673&quot; youtube-src-id=&quot;vepKFfY7MG0&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;p data-end=&quot;1179&quot; data-start=&quot;1057&quot;&gt;This is only the &lt;strong data-end=&quot;1218&quot; data-start=&quot;1203&quot;&gt;first stage&lt;/strong&gt; of development.Next, I’ll be focusing on &lt;strong data-end=&quot;1293&quot; data-start=&quot;1263&quot;&gt;improving the editor tools&lt;/strong&gt; and &lt;strong data-end=&quot;1342&quot; data-start=&quot;1298&quot;&gt;optimizing performance on mobile devices&lt;/strong&gt;. Hopefully, this system can eventually run smoothly on phones as well.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/826214117567538579/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/10/simulating-ghost-of-tsushimas-iconic.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/826214117567538579'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/826214117567538579'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/10/simulating-ghost-of-tsushimas-iconic.html' title='Simulating Ghost of Tsushima’s Iconic White Flower Field in Unity'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEgfbFZtuT0CL6g07AMhiTzwVw-rEHYqJGHOfLFxErno2HkWJLpND4NERU01WMkxmrhG7moO_3sn9berFiYdyBp8M_eROcktKT6bCWHFtPBtsScLfb-RQuvLXzrQWjNowNOQe_PylyFiBPtPhAHY0NB9U3pWWqBU8iL3sRXutKvyAIUJzGusashLh0vDuKLH=s72-w640-h382-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-2450498407615839196</id><published>2025-09-30T14:59:00.001+08:00</published><updated>2025-09-30T15:12:52.153+08:00</updated><title type='text'>Building a Cross-Platform GPU Procedural Grass System in Unity URP (Optimized for Mobile)</title><content type='html'>&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEh0pdT6X2l7Ao2N8bEpcXr4_1H-T1CgiW32ToIJbuFoYt5fn0Mb4o9QBienfegiNnvKLrVvplOwLF4_xgbE_SXR3FuvyQZvguHdMntjZRlzL8YrZEvtrsz8VsN5HuI64dqTHER9Ttx518AxIjXH2IGjEqeIEUfjfmg0mDCVLwSI1Nu1895OQwEHfIj2EWur&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;483&quot; data-original-width=&quot;880&quot; height=&quot;352&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEh0pdT6X2l7Ao2N8bEpcXr4_1H-T1CgiW32ToIJbuFoYt5fn0Mb4o9QBienfegiNnvKLrVvplOwLF4_xgbE_SXR3FuvyQZvguHdMntjZRlzL8YrZEvtrsz8VsN5HuI64dqTHER9Ttx518AxIjXH2IGjEqeIEUfjfmg0mDCVLwSI1Nu1895OQwEHfIj2EWur=w640-h352&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Recently, I’ve been working on a project called UnityURP-Procedural-DrawMeshInstancedIndirect, and I’d like to share some advantages of procedural generation.&lt;p&gt;&lt;/p&gt;&lt;p&gt;Unlike traditional methods that rely on massive Transform Buffers or GameObject instances, this approach dynamically generates grass using ComputeShaders. For culling, I use a RenderTexture to store results (since SSBOs aren’t supported in many mobile VertexShaders), ensuring compatibility with most mobile devices.&lt;/p&gt;&lt;p&gt;This architecture significantly improves load times and performance, especially on mid- to low-end phones. Let’s break down the key design concepts step by step.&lt;span&gt;&lt;/span&gt;&lt;/p&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;span&gt;&lt;/span&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;b&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Why Not Use Traditional TransformBuffer-Based Grass?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Unity’s built-in grass and tree systems, or approaches using DrawMeshInstanced/DrawMeshInstancedIndirect, often require the CPU to prepare huge amounts of data before passing it to the GPU. When your scene contains hundreds of thousands or even millions of blades of grass, the memory and load cost of Transform Buffers becomes overwhelming.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;My solution: Don’t prepare this data at all. Let the GPU handle procedural generation. This way, grass distribution, size, rotation, and wind can be generated dynamically—saving memory and increasing flexibility.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;1. Creating and Binding CountBuffer and RenderTexture (No AppendBuffer)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;First, create and bind the CountBuffer for instance counting in the ComputeShader. Next, set up a RenderTexture to store culling results, and bind it to both the ComputeShader and the material.&lt;/p&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span class=&quot;pre--content&quot;&gt;m_instanceCountBuffer = &lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; ComputeBuffer(&lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;sizeof&lt;/span&gt;(&lt;span class=&quot;hljs-built_in&quot;&gt;uint&lt;/span&gt;),&lt;br /&gt;    ComputeBufferType.Counter);&lt;br /&gt;m_instanceCountBuffer.SetCounterValue(&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;);&lt;br /&gt;m_targetProceduralInstanceFilterCS.SetBuffer(&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;, msr_instanceCountBufferID,&lt;br /&gt;    m_instanceCountBuffer);&lt;br /&gt;&lt;br /&gt;m_filterResultRT = &lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; RenderTexture(m_filterResultSize.x,&lt;br /&gt;    m_filterResultSize.y, &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;, RenderTextureFormat.ARGBHalf);&lt;br /&gt;m_filterResultRT.enableRandomWrite = &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;&lt;br /&gt;m_filterResultRT.filterMode = FilterMode.Point;&lt;br /&gt;m_filterResultRT.Create();&lt;br /&gt;&lt;span class=&quot;hljs-comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;m_targetProceduralInstanceFilterCS.SetTexture(&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;, msr_filterResultRTID,&lt;br /&gt;  m_targetFilterResultRT);&lt;br /&gt;m_targetGrassMaterial.SetTexture(msr_filterResultRTID,&lt;br /&gt;m_targetFilterResultRT);&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span class=&quot;pre--content&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;2. Visible Cell Culling List&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-family: Times New Roman;&quot;&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;Use the VisableCellsCuller class to get the visible cell list, pass it to the ComputeShader, and use it to compute world positions for grass. Since each cell contains a fixed number of grass instances, it also helps calculate thread group counts.&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;7518&quot; spellcheck=&quot;false&quot;&gt;&lt;span class=&quot;pre--content&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;OnBeginCameraRendering&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;ScriptableRenderContext context, &lt;br /&gt;    Camera camera&lt;/span&gt;)&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (IsSkipCamera(camera))&lt;br /&gt;        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;hljs-keyword&quot;&gt;var&lt;/span&gt; cullingCamera = camera;&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; UNITY_EDITOR&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (m_isOnlyViewMainCameraCulling)&lt;br /&gt;        cullingCamera = Camera.main;&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;endif&lt;/span&gt; //UNITY_EDITOR&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    Bounds renderBounds;&lt;br /&gt;    Vector2 cellSize;&lt;br /&gt;    m_grassParamsSector.GetRenderBoundAndCellSize(&lt;span class=&quot;hljs-keyword&quot;&gt;out&lt;/span&gt; renderBounds, &lt;br /&gt;        &lt;span class=&quot;hljs-keyword&quot;&gt;out&lt;/span&gt; cellSize);&lt;br /&gt;&lt;br /&gt;    m_visableCellsCuller.ProcessCulling(cullingCamera, &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; cellSize,&lt;br /&gt;        &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; m_proceduralGrassData.m_worldRect, &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; msr_worldMinMaxHeight);&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;hljs-keyword&quot;&gt;var&lt;/span&gt; visibleCellIndices = m_visableCellsCuller.GetVisibleCellIndices();&lt;br /&gt;    &lt;span class=&quot;hljs-keyword&quot;&gt;var&lt;/span&gt; visibleCellIndexCount = visibleCellIndices.Count;&lt;br /&gt;    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (visibleCellIndexCount &amp;lt;= &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;)&lt;br /&gt;        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;    m_targetProceduralInstanceFilterCS.SetVector(msr_cameraPositionID,&lt;br /&gt;        camera.transform.position);&lt;br /&gt;&lt;br /&gt;    visibleCellIndices.CopyTo(m_visibleCellIndices);&lt;br /&gt;    m_visibleCellIndexBuffer.SetData(m_visibleCellIndices, &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;,&lt;br /&gt;        visibleCellIndexCount);    &lt;br /&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;7518&quot; spellcheck=&quot;false&quot;&gt;&lt;span class=&quot;pre--content&quot;&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;3.DispatchComputeInBatches&lt;/b&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-family: Times New Roman;&quot;&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;Wrap the ComputeShader dispatch logic in a DispatchComputeInBatches function to handle the 65535 dispatch count limit. If the count exceeds this, split into multiple dispatches.&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;java&quot; data-code-block-mode=&quot;1&quot; name=&quot;d373&quot; spellcheck=&quot;false&quot;&gt;&lt;span class=&quot;pre--content&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;DispatchComputeInBatches&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(ComputeShader targetComputeShader,&lt;br /&gt;    &lt;span class=&quot;hljs-type&quot;&gt;int&lt;/span&gt; processInstanceCount, &lt;span class=&quot;hljs-type&quot;&gt;int&lt;/span&gt; kernel = &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;hljs-type&quot;&gt;int&lt;/span&gt; threadGroupSize = &lt;span class=&quot;hljs-number&quot;&gt;64&lt;/span&gt;, &lt;br /&gt;    &lt;span class=&quot;hljs-type&quot;&gt;int&lt;/span&gt; maxDispatchCount = &lt;span class=&quot;hljs-number&quot;&gt;65535&lt;/span&gt;)&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;    &lt;span class=&quot;hljs-comment&quot;&gt;// Set the total number of instances for the shader to access&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;hljs-comment&quot;&gt;// (for bounds checking)&lt;/span&gt;&lt;br /&gt;    targetComputeShader.SetInt(msr_maxProcessCountID, processInstanceCount);&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;hljs-comment&quot;&gt;// &#39;offset&#39; tracks how many instances have already been processed&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;hljs-type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;offset&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;;&lt;br /&gt;    &lt;span class=&quot;hljs-keyword&quot;&gt;while&lt;/span&gt; (offset &amp;lt; processInstanceCount)&lt;br /&gt;    {&lt;br /&gt;        &lt;span class=&quot;hljs-comment&quot;&gt;// Calculate how many instances remain to be processed in this batch&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;hljs-type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;remainInstance&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; processInstanceCount - offset;&lt;br /&gt;&lt;br /&gt;        &lt;span class=&quot;hljs-comment&quot;&gt;// Dynamically calculate how many thread groups are needed for this batch&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;hljs-comment&quot;&gt;// This is crucial: the last batch may not fill a whole thread group,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;hljs-comment&quot;&gt;// so we must recalculate based on the remaining instances.&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;hljs-type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;groupCountThisBatch&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; Mathf.CeilToInt(remainInstance / (&lt;span class=&quot;hljs-type&quot;&gt;float&lt;/span&gt;)threadGroupSize);&lt;br /&gt;&lt;br /&gt;        &lt;span class=&quot;hljs-comment&quot;&gt;// Clamp the number of thread groups to the API limit (65535)&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;hljs-type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;dispatchThisBatch&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; Mathf.Min(groupCountThisBatch, maxDispatchCount);&lt;br /&gt;&lt;br /&gt;        &lt;span class=&quot;hljs-comment&quot;&gt;// Set the offset for this batch so the shader knows the starting index&lt;/span&gt;&lt;br /&gt;        targetComputeShader.SetInt(msr_offestCountID, offset);&lt;br /&gt;&lt;br /&gt;        &lt;span class=&quot;hljs-comment&quot;&gt;// Dispatch the current batch&lt;/span&gt;&lt;br /&gt;        targetComputeShader.Dispatch(kernel, dispatchThisBatch, &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;        &lt;span class=&quot;hljs-comment&quot;&gt;// Update offset to mark processed instances&lt;/span&gt;&lt;br /&gt;        offset += dispatchThisBatch * threadGroupSize;&lt;br /&gt;    }&lt;br /&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;4. ComputeShader Implementation&lt;/b&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-family: Times New Roman;&quot;&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;The core is procedural generation of transforms, using SimplexNoise for natural height and width variation. Culling includes both view frustum and a WeightMap filter, so grass only appears if its weight exceeds the minimum threshold. Results are stored in a RenderTexture.&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;cpp&quot; data-code-block-mode=&quot;1&quot; name=&quot;1526&quot; spellcheck=&quot;false&quot;&gt;&lt;span class=&quot;pre--content&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;hljs-comment&quot;&gt;/// Author: SmallBurger Inc&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;hljs-comment&quot;&gt;/// Date: 2025/09/19&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;hljs-comment&quot;&gt;/// Desc:&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;hljs-comment&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;pragma&lt;/span&gt; kernel CSMain&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;hljs-comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;[&lt;span class=&quot;hljs-built_in&quot;&gt;numthreads&lt;/span&gt;(&lt;span class=&quot;hljs-number&quot;&gt;64&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;)]&lt;br /&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;CSMain&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(uint3 id : SV_DispatchThreadID)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;{ &lt;br /&gt; uint instanceIndex = id.x + _OffsetCount;&lt;br /&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (instanceIndex &amp;gt;= _MaxProcessCount)&lt;br /&gt;  &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt; float2 position2D, randomOffest2D;&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; !defined(PROCESS_BILLBOARD)&lt;/span&gt;&lt;br /&gt; half yawRadians;&lt;br /&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;GetInstanceTransform&lt;/span&gt;(instanceIndex, position2D, randomOffest2D,&lt;br /&gt;  yawRadians);&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt;&lt;/span&gt;&lt;br /&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;GetInstanceTransform&lt;/span&gt;(instanceIndex, position2D, randomOffest2D);&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;endif&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;//!defined(PROCESS_BILLBOARD)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; ((position2D.x &amp;lt; _WorldMinMax.x) ||&lt;br /&gt;  (position2D.y &amp;lt; _WorldMinMax.y) ||&lt;br /&gt;  (position2D.x &amp;gt; _WorldMinMax.z) ||&lt;br /&gt;  (position2D.y &amp;gt; _WorldMinMax.w))&lt;br /&gt;  &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt; float3 delta = _CameraPosition - &lt;span class=&quot;hljs-built_in&quot;&gt;float3&lt;/span&gt;(position2D.x, &lt;span class=&quot;hljs-number&quot;&gt;0.0f&lt;/span&gt;, position2D.y);&lt;br /&gt; &lt;span class=&quot;hljs-type&quot;&gt;float&lt;/span&gt; viewSquareDistance = &lt;span class=&quot;hljs-built_in&quot;&gt;dot&lt;/span&gt;(delta, delta);&lt;br /&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt;(viewSquareDistance &amp;gt; _MaxViewSquareDistance)&lt;br /&gt;  &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt; half grassWeight = &lt;span class=&quot;hljs-number&quot;&gt;1.0&lt;/span&gt;;&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;ifdef&lt;/span&gt; PROCESS_WEIGHT_MAP_FILTER&lt;/span&gt;&lt;br /&gt; grassWeight = _WeightMap.&lt;span class=&quot;hljs-built_in&quot;&gt;SampleLevel&lt;/span&gt;(sampler_WeightMap, &lt;span class=&quot;hljs-built_in&quot;&gt;GetWorldUV&lt;/span&gt;(position2D), &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;).a;&lt;br /&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (grassWeight &amp;lt;= sc_skipWeightRatio)&lt;br /&gt;  &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;endif&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; float4 absPosCS = &lt;span class=&quot;hljs-built_in&quot;&gt;abs&lt;/span&gt;(&lt;span class=&quot;hljs-built_in&quot;&gt;mul&lt;/span&gt;(_ViewProjectionMatrix,&lt;br /&gt;  &lt;span class=&quot;hljs-built_in&quot;&gt;float4&lt;/span&gt;(position2D.x, &lt;span class=&quot;hljs-number&quot;&gt;0.0&lt;/span&gt;, position2D.y, &lt;span class=&quot;hljs-number&quot;&gt;1.0&lt;/span&gt;)));&lt;br /&gt;&lt;br /&gt; &lt;span class=&quot;hljs-comment&quot;&gt;//do culling test in clip space, result is the same as doing&lt;/span&gt;&lt;br /&gt; &lt;span class=&quot;hljs-comment&quot;&gt;//test in NDC space.&lt;/span&gt;&lt;br /&gt; &lt;span class=&quot;hljs-comment&quot;&gt;//prefer clip space here because doing culling test in clip space&lt;/span&gt;&lt;br /&gt; &lt;span class=&quot;hljs-comment&quot;&gt;//is faster.&lt;/span&gt;&lt;br /&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; ((absPosCS.z &amp;gt; absPosCS.w) ||&lt;br /&gt;  (absPosCS.y &amp;gt; absPosCS.w * _MaxInstanceSize) ||&lt;br /&gt;  (absPosCS.x &amp;gt; absPosCS.w * _MaxInstanceSize))&lt;br /&gt;  &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt; uint index = _InstanceCountBuffer.&lt;span class=&quot;hljs-built_in&quot;&gt;IncrementCounter&lt;/span&gt;();&lt;br /&gt; &lt;br /&gt; uint offestIndex = index * &lt;span class=&quot;hljs-number&quot;&gt;2&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt; half2 worldUV = &lt;span class=&quot;hljs-built_in&quot;&gt;GetWorldUV&lt;/span&gt;(position2D);&lt;br /&gt;&lt;br /&gt; _FilterResultRT[&lt;span class=&quot;hljs-built_in&quot;&gt;int2&lt;/span&gt;(offestIndex % _FilterResultRTSize.x, offestIndex / _FilterResultRTSize.x)] =&lt;br /&gt;  &lt;span class=&quot;hljs-built_in&quot;&gt;float4&lt;/span&gt;(worldUV, &lt;span class=&quot;hljs-built_in&quot;&gt;GetDestSize&lt;/span&gt;(position2D, grassWeight));&lt;br /&gt;&lt;br /&gt; ++offestIndex;&lt;br /&gt;&lt;br /&gt; half wind = &lt;span class=&quot;hljs-built_in&quot;&gt;CalculateWind&lt;/span&gt;(position2D);&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;ifdef&lt;/span&gt; PROCESS_BILLBOARD &lt;/span&gt;&lt;br /&gt; &lt;span class=&quot;hljs-type&quot;&gt;float&lt;/span&gt; hash = &lt;span class=&quot;hljs-built_in&quot;&gt;frac&lt;/span&gt;(&lt;span class=&quot;hljs-built_in&quot;&gt;sin&lt;/span&gt;(&lt;span class=&quot;hljs-built_in&quot;&gt;dot&lt;/span&gt;(position2D, &lt;span class=&quot;hljs-built_in&quot;&gt;float2&lt;/span&gt;(&lt;span class=&quot;hljs-number&quot;&gt;12.9898&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;78.233&lt;/span&gt;))) * &lt;span class=&quot;hljs-number&quot;&gt;43758.5453&lt;/span&gt;);&lt;br /&gt; &lt;span class=&quot;hljs-type&quot;&gt;float&lt;/span&gt; angle = hash * &lt;span class=&quot;hljs-number&quot;&gt;6.2831853&lt;/span&gt;;&lt;br /&gt; float2 randDir = &lt;span class=&quot;hljs-built_in&quot;&gt;float2&lt;/span&gt;(&lt;span class=&quot;hljs-built_in&quot;&gt;cos&lt;/span&gt;(angle), &lt;span class=&quot;hljs-built_in&quot;&gt;sin&lt;/span&gt;(angle));&lt;br /&gt;&lt;br /&gt; half3 randomAddToN =&lt;br /&gt;  (_RandomNormalWeight * randDir.x) * _CameraRightWS +&lt;br /&gt;  (_RandomNormalWeight * randDir.y) * _CameraForwardWS;&lt;br /&gt;&lt;br /&gt; half3 windAddToN = wind * (&lt;span class=&quot;hljs-number&quot;&gt;-0.25&lt;/span&gt; * _CameraRightWS);&lt;br /&gt; half3 normalWS = &lt;span class=&quot;hljs-built_in&quot;&gt;normalize&lt;/span&gt;(&lt;span class=&quot;hljs-built_in&quot;&gt;half3&lt;/span&gt;(&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;,&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;) + randomAddToN);&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;endif&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; _FilterResultRT[&lt;span class=&quot;hljs-built_in&quot;&gt;int2&lt;/span&gt;(offestIndex % _FilterResultRTSize.x, offestIndex / _FilterResultRTSize.x)] =&lt;br /&gt;  &lt;span class=&quot;hljs-built_in&quot;&gt;float4&lt;/span&gt;(&lt;br /&gt;   wind,&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; !defined(PROCESS_BILLBOARD)&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;hljs-built_in&quot;&gt;sin&lt;/span&gt;(yawRadians), &lt;br /&gt;   &lt;span class=&quot;hljs-built_in&quot;&gt;cos&lt;/span&gt;(yawRadians),&lt;br /&gt;   &lt;span class=&quot;hljs-number&quot;&gt;0.0&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   normalWS&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;endif&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;//!defined(PROCESS_BILLBOARD)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   );&lt;br /&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;cpp&quot; data-code-block-mode=&quot;1&quot; name=&quot;1526&quot; spellcheck=&quot;false&quot;&gt;&lt;span class=&quot;pre--content&quot;&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;5. WeightMapFilter: Artistic Control Over Grass Distribution&lt;/b&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-family: Times New Roman;&quot;&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;Procedural generation alone can make grass look “too regular.” I added a WeightMapFilter mechanism so artists can paint grass weights directly onto a texture. The ComputeShader uses this filter map during generation, allowing artists to control large-scale distribution while procedural generation handles fine details.&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-family: Times New Roman;&quot;&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;With FilterMap&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-family: Times New Roman;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center; white-space: normal;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgSR2KwG8bsEnKWy9uQSvP_2OjdvqjSzxXLQUiDoW4U0zo_IL9vBtmzrmW9UqRz0CfSdalFYP4HRfOse1lL6ReDAzHPCkCZXjfugGPCroywsa1Jh8Pulil74uIL-Jluxbxq1EOVx7vTiMGAlfxFEhL0rUt_KlHCUTlD3uKHtMEfmKwBRjykHHez53_dbLC0&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;532&quot; data-original-width=&quot;880&quot; height=&quot;386&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgSR2KwG8bsEnKWy9uQSvP_2OjdvqjSzxXLQUiDoW4U0zo_IL9vBtmzrmW9UqRz0CfSdalFYP4HRfOse1lL6ReDAzHPCkCZXjfugGPCroywsa1Jh8Pulil74uIL-Jluxbxq1EOVx7vTiMGAlfxFEhL0rUt_KlHCUTlD3uKHtMEfmKwBRjykHHez53_dbLC0=w640-h386&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;&lt;br /&gt;Without FilterMap&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-family: Times New Roman;&quot;&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjTiAn7SsT9E_QsH6Kcy3F59KKepjYNqC6cqNJMq2iI-YIE8QrZvqCIV7OksqMwZIy76VChMY3xhrgxI_tlqllwKGfJpCHPpnDGabYCezb3CCKPPouKgdxxHyxCUfIJEEwrf6sgGoca_f2FABBiaJrsA-zgD4TI-saO30wX1I0OKVfkW2shygEhCDaXDp1q&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;532&quot; data-original-width=&quot;880&quot; height=&quot;386&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjTiAn7SsT9E_QsH6Kcy3F59KKepjYNqC6cqNJMq2iI-YIE8QrZvqCIV7OksqMwZIy76VChMY3xhrgxI_tlqllwKGfJpCHPpnDGabYCezb3CCKPPouKgdxxHyxCUfIJEEwrf6sgGoca_f2FABBiaJrsA-zgD4TI-saO30wX1I0OKVfkW2shygEhCDaXDp1q=w640-h386&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;6. Prefer ComputeShader Over VertexShader for Instance Logic&lt;/b&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-family: Times New Roman;&quot;&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;Many DrawMeshInstancedIndirect solutions compute normals, random size, rotation, wind, etc., in the VertexShader. If you only need one instance’s data, move as much logic as possible to the ComputeShader.&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;cpp&quot; data-code-block-mode=&quot;1&quot; name=&quot;37b7&quot; spellcheck=&quot;false&quot;&gt;&lt;span class=&quot;pre--content&quot;&gt;half wind = &lt;span class=&quot;hljs-built_in&quot;&gt;CalculateWind&lt;/span&gt;(position2D);&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;ifdef&lt;/span&gt; PROCESS_BILLBOARD &lt;/span&gt;&lt;br /&gt; &lt;span class=&quot;hljs-type&quot;&gt;float&lt;/span&gt; hash = &lt;span class=&quot;hljs-built_in&quot;&gt;frac&lt;/span&gt;(&lt;span class=&quot;hljs-built_in&quot;&gt;sin&lt;/span&gt;(&lt;span class=&quot;hljs-built_in&quot;&gt;dot&lt;/span&gt;(position2D, &lt;span class=&quot;hljs-built_in&quot;&gt;float2&lt;/span&gt;(&lt;span class=&quot;hljs-number&quot;&gt;12.9898&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;78.233&lt;/span&gt;))) * &lt;span class=&quot;hljs-number&quot;&gt;43758.5453&lt;/span&gt;);&lt;br /&gt; &lt;span class=&quot;hljs-type&quot;&gt;float&lt;/span&gt; angle = hash * &lt;span class=&quot;hljs-number&quot;&gt;6.2831853&lt;/span&gt;;&lt;br /&gt; float2 randDir = &lt;span class=&quot;hljs-built_in&quot;&gt;float2&lt;/span&gt;(&lt;span class=&quot;hljs-built_in&quot;&gt;cos&lt;/span&gt;(angle), &lt;span class=&quot;hljs-built_in&quot;&gt;sin&lt;/span&gt;(angle));&lt;br /&gt;&lt;br /&gt; half3 randomAddToN =&lt;br /&gt;  (_RandomNormalWeight * randDir.x) * _CameraRightWS +&lt;br /&gt;  (_RandomNormalWeight * randDir.y) * _CameraForwardWS;&lt;br /&gt;&lt;br /&gt; half3 windAddToN = wind * (&lt;span class=&quot;hljs-number&quot;&gt;-0.25&lt;/span&gt; * _CameraRightWS);&lt;br /&gt; half3 normalWS = &lt;span class=&quot;hljs-built_in&quot;&gt;normalize&lt;/span&gt;(&lt;span class=&quot;hljs-built_in&quot;&gt;half3&lt;/span&gt;(&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;,&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;) + randomAddToN);&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;endif&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; _FilterResultRT[&lt;span class=&quot;hljs-built_in&quot;&gt;int2&lt;/span&gt;(offestIndex % _FilterResultRTSize.x, offestIndex / _FilterResultRTSize.x)] =&lt;br /&gt;  &lt;span class=&quot;hljs-built_in&quot;&gt;float4&lt;/span&gt;(&lt;br /&gt;   wind,&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; !defined(PROCESS_BILLBOARD)&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;hljs-built_in&quot;&gt;sin&lt;/span&gt;(yawRadians), &lt;br /&gt;   &lt;span class=&quot;hljs-built_in&quot;&gt;cos&lt;/span&gt;(yawRadians),&lt;br /&gt;   &lt;span class=&quot;hljs-number&quot;&gt;0.0&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   normalWS&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;endif&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;//!defined(PROCESS_BILLBOARD)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;cpp&quot; data-code-block-mode=&quot;1&quot; name=&quot;37b7&quot; spellcheck=&quot;false&quot;&gt;&lt;span class=&quot;pre--content&quot;&gt;&lt;span class=&quot;hljs-meta&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;7. GrassShader Implementation
&lt;/b&gt;&lt;/span&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-family: Times New Roman;&quot;&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;Parse RenderTexture data: each instance uses two pixels—one for position and size, another for wind and normal (or rotation for non-billboard mode).&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;7080&quot; spellcheck=&quot;false&quot;&gt;&lt;span class=&quot;pre--content&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;GetInstanceTransform&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;uint&lt;/span&gt; instanceID, &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; float4 filterResultRTTexelSize, &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; float4 positionOS,&lt;br /&gt;    &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; half3 normalOS, &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; half2 windDirection, &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; float4 interactorCollisionSphere,&lt;br /&gt;    &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; half interactorAffectWeight, &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; half windNormalWeight,&lt;br /&gt;    &lt;span class=&quot;hljs-keyword&quot;&gt;out&lt;/span&gt; half2 worldUV,&lt;br /&gt;    &lt;span class=&quot;hljs-keyword&quot;&gt;out&lt;/span&gt; float3 positionWS,&lt;br /&gt;    &lt;span class=&quot;hljs-keyword&quot;&gt;out&lt;/span&gt; half affectWeight,&lt;br /&gt;    &lt;span class=&quot;hljs-keyword&quot;&gt;out&lt;/span&gt; half3 normalWS,    &lt;br /&gt;    &lt;span class=&quot;hljs-keyword&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;float&lt;/span&gt; viewDistance&lt;/span&gt;)&lt;/span&gt;&lt;br /&gt;{   &lt;br /&gt;    float2 position2D;&lt;br /&gt;    half2 sizeFactor;&lt;br /&gt;    half wind;&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#ifdef PROCESS_BILLBOARD&lt;/span&gt;&lt;br /&gt;    GetGrassInstanceData(instanceID, filterResultRTTexelSize, worldUV, position2D, sizeFactor, wind,&lt;br /&gt;        normalWS);    &lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    half yawSin, yawCos;&lt;br /&gt;    GetGrassInstanceData(instanceID, filterResultRTTexelSize, worldUV, position2D, sizeFactor, wind,&lt;br /&gt;        yawSin,&lt;br /&gt;        yawCos);&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;endif&lt;/span&gt; //PROCESS_BILLBOARD&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    float3 instancePosition = float3(position2D.x, &lt;span class=&quot;hljs-number&quot;&gt;0.0&lt;/span&gt;, position2D.y);&lt;br /&gt;    viewDistance = length(_WorldSpaceCameraPos - instancePosition);&lt;br /&gt;&lt;br /&gt;    affectWeight = positionOS.y;&lt;br /&gt;    &lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; !defined(PROCESS_BILLBOARD)&lt;/span&gt;&lt;br /&gt;    GetRotateWorldPosition(positionOS.xyz, instancePosition, sizeFactor, yawSin, yawCos, positionWS);&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    GetBillboardWorldPosition(positionOS.xyz, instancePosition, sizeFactor, viewDistance, positionWS);&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;endif&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    float2 windOffest = windDirection * wind * affectWeight;&lt;br /&gt;    positionWS.xz += windOffest;&lt;br /&gt;&lt;br /&gt;    ApplyInteractorOffest(instancePosition, interactorCollisionSphere,&lt;br /&gt;        step(&lt;span class=&quot;hljs-number&quot;&gt;0.5&lt;/span&gt;, affectWeight) * interactorAffectWeight, positionWS);&lt;br /&gt;    &lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; !defined(PROCESS_BILLBOARD)&lt;/span&gt;&lt;br /&gt;    CalculateNormal(normalOS, yawSin, yawCos, windNormalWeight * affectWeight, windOffest, normalWS);&lt;br /&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;endif&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;7080&quot; spellcheck=&quot;false&quot;&gt;&lt;span class=&quot;pre--content&quot;&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;8. Collision Handling&lt;/b&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-family: Times New Roman;&quot;&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;As mentioned, try to move as much logic as possible to the ComputeShader, especially when handling multiple interactors—doing this in the VertexShader is not ideal for performance. In this project, since there is only one interactor and moving it to the ComputeShader would require extra pixels, I kept it in the VertexShader. But feel free to refactor if needed.&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;java&quot; data-code-block-mode=&quot;1&quot; name=&quot;b6c9&quot; spellcheck=&quot;false&quot;&gt;&lt;span class=&quot;pre--content&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;ApplyInteractorOffest&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(in float3 instancePosition, in float4 collisionSphere,&lt;br /&gt;    in half applyWeight, inout float3 positionWS)&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;    &lt;span class=&quot;hljs-type&quot;&gt;float3&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;delta&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; instancePosition - collisionSphere.xyz;&lt;br /&gt;    &lt;span class=&quot;hljs-type&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;squareDistance&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; dot(delta, delta);&lt;br /&gt;    &lt;span class=&quot;hljs-type&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;maxCheckRadius&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; _MaxInstanceSize + collisionSphere.w;&lt;br /&gt;    &lt;span class=&quot;hljs-type&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;maxCheckSquareRadius&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; maxCheckRadius * maxCheckRadius;&lt;br /&gt;    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (squareDistance &amp;gt; maxCheckSquareRadius)&lt;br /&gt;        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;hljs-comment&quot;&gt;// linear falloff (1 at center, 0 at radius) — can be changed to smoothstep/pow/exp&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;hljs-type&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;falloff&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; saturate(&lt;span class=&quot;hljs-number&quot;&gt;1.0&lt;/span&gt; - (squareDistance / maxCheckSquareRadius));&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;hljs-comment&quot;&gt;// Direction: from interactor to instance (avoid divide-by-zero)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;hljs-type&quot;&gt;float3&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;dir&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; (squareDistance &amp;gt; &lt;span class=&quot;hljs-number&quot;&gt;1e-5&lt;/span&gt;) ? normalize(delta) : float3(&lt;span class=&quot;hljs-number&quot;&gt;0.0&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;hljs-comment&quot;&gt;// Use interactor.w as base strength (multiply global scale here if needed)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;hljs-type&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;magnitude&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; falloff * collisionSphere.w;&lt;span class=&quot;hljs-comment&quot;&gt;// *_InteractorAffectWeight;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;hljs-comment&quot;&gt;// Storage format: float4(dx, dy, dz, strength)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;hljs-type&quot;&gt;float3&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;displacement&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; dir * magnitude;&lt;br /&gt;&lt;br /&gt;    positionWS.xz += applyWeight * displacement.xz;&lt;br /&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;java&quot; data-code-block-mode=&quot;1&quot; name=&quot;b6c9&quot; spellcheck=&quot;false&quot;&gt;&lt;span class=&quot;pre--content&quot;&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;9. Loading Time&lt;/b&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-family: Times New Roman;&quot;&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;With no precomputed Transforms or runtime initialization, loading is extremely fast—often under 0.1 seconds. (Games like Ghost of Tsushima likely use similar techniques.)&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-family: Times New Roman; font-size: large;&quot;&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;&lt;b&gt;Demo Video&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-family: Times New Roman;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;542&quot; src=&quot;https://www.youtube.com/embed/7fhobV4y3yY&quot; width=&quot;651&quot; youtube-src-id=&quot;7fhobV4y3yY&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;/span&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot; style=&quot;font-size: x-large;&quot;&gt;&lt;b&gt;Github&lt;/b&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-family: Times New Roman;&quot;&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;&lt;a href=&quot;https://github.com/AkilarLiao/UnityURP-Procedural-DrawMeshInstancedIndirect&quot;&gt;UnityURP-Procedural-DrawMeshInstancedIndirect&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;References &amp;amp; Further Reading&lt;/b&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-family: Times New Roman;&quot;&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;If you want to support more vegetation types, advanced brush tools (ComputeShader-based brushes), optimized multi-interactor collision flows, or mesh-conforming features, check out:&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-family: Times New Roman;&quot;&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/tools/painting/gpuplantpainter-266965&quot;&gt;GPUPlantPainter&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-family: Times New Roman;&quot;&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;For more complex procedural workflows (procedural random curve vegetation effects, similar to Ghost of Tsushima), check out:&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class=&quot;graf graf--pre graf--preV2&quot; data-code-block-lang=&quot;csharp&quot; data-code-block-mode=&quot;1&quot; name=&quot;a8bd&quot; spellcheck=&quot;false&quot;&gt;&lt;span style=&quot;font-family: Times New Roman;&quot;&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/tools/painting/gpugrassbladepainter-316417&quot;&gt;GPUGrassBladePainter&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;&lt;/pre&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/2450498407615839196/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/09/building-cross-platform-gpu-procedural.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/2450498407615839196'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/2450498407615839196'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/09/building-cross-platform-gpu-procedural.html' title='Building a Cross-Platform GPU Procedural Grass System in Unity URP (Optimized for Mobile)'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEh0pdT6X2l7Ao2N8bEpcXr4_1H-T1CgiW32ToIJbuFoYt5fn0Mb4o9QBienfegiNnvKLrVvplOwLF4_xgbE_SXR3FuvyQZvguHdMntjZRlzL8YrZEvtrsz8VsN5HuI64dqTHER9Ttx518AxIjXH2IGjEqeIEUfjfmg0mDCVLwSI1Nu1895OQwEHfIj2EWur=s72-w640-h352-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-5681044721973681527</id><published>2025-09-26T10:31:00.001+08:00</published><updated>2025-09-26T10:36:12.420+08:00</updated><title type='text'>Unity Shader Keywords: shader_feature vs multi_compile — Complete Comparison Table and Usage Guide</title><content type='html'>&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEh9KtGRdp6re_Yt-wRAJdNhMKEvp_kmzFPUGFkYx9ifZdQDYPF6W3iUHhpMFgSHvNmgNTKF-4Sw3dbGEKPS4EnovI_rstHwMa9LFI_ae1CswB9AWNPnkfApFijkEz1ld3hxpnSW2hMkJMpOlb_AkPkQrfecnlMj-KsaSCGQwMT7y4VjLqq6vKpnoE9CrdYm&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;1024&quot; data-original-width=&quot;1536&quot; height=&quot;426&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEh9KtGRdp6re_Yt-wRAJdNhMKEvp_kmzFPUGFkYx9ifZdQDYPF6W3iUHhpMFgSHvNmgNTKF-4Sw3dbGEKPS4EnovI_rstHwMa9LFI_ae1CswB9AWNPnkfApFijkEz1ld3hxpnSW2hMkJMpOlb_AkPkQrfecnlMj-KsaSCGQwMT7y4VjLqq6vKpnoE9CrdYm=w640-h426&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Many Unity Shader developers often get confused about the differences between &lt;code&gt;shader_feature&lt;/code&gt; and &lt;code&gt;multi_compile&lt;/code&gt;, and when to use each one. I’ve encountered similar confusion myself, so I’ve put together this comparison table and practical guide to help everyone quickly understand the correct usage.&lt;p&gt;&lt;/p&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;&lt;/p&gt;
&lt;table border=&quot;1&quot; cellpadding=&quot;6&quot; cellspacing=&quot;0&quot;&gt;
  &lt;tbody&gt;&lt;tr&gt;
    &lt;th&gt;Keyword Type&lt;/th&gt;
    &lt;th&gt;Variant Depends on Material&lt;/th&gt;
    &lt;th&gt;Global Scope&lt;/th&gt;
    &lt;th&gt;Material Keyword State Saved&lt;/th&gt;
    &lt;th&gt;Runtime Dynamic Switch&lt;/th&gt;
    &lt;th&gt;Typical Usage&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;shader_feature&lt;/td&gt;
    &lt;td&gt;✔️ (Yes)&lt;/td&gt;
    &lt;td&gt;✔️ (Global)&lt;/td&gt;
    &lt;td&gt;✔️&lt;/td&gt;
    &lt;td&gt;❌&lt;/td&gt;
    &lt;td&gt;Static config, lean build&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;shader_feature_local&lt;/td&gt;
    &lt;td&gt;✔️ (Yes)&lt;/td&gt;
    &lt;td&gt;❌ (Local)&lt;/td&gt;
    &lt;td&gt;✔️&lt;/td&gt;
    &lt;td&gt;❌&lt;/td&gt;
    &lt;td&gt;Static config, lean build&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;multi_compile&lt;/td&gt;
    &lt;td&gt;❌ (No)&lt;/td&gt;
    &lt;td&gt;✔️ (Global)&lt;/td&gt;
    &lt;td&gt;❌&lt;/td&gt;
    &lt;td&gt;✔️&lt;/td&gt;
    &lt;td&gt;Runtime switching&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;multi_compile_local&lt;/td&gt;
    &lt;td&gt;❌ (No)&lt;/td&gt;
    &lt;td&gt;❌ (Local)&lt;/td&gt;
    &lt;td&gt;❌&lt;/td&gt;
    &lt;td&gt;✔️&lt;/td&gt;
    &lt;td&gt;Runtime switching&lt;/td&gt;
  &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/5681044721973681527/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/09/unity-shader-keywords-shaderfeature-vs.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/5681044721973681527'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/5681044721973681527'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/09/unity-shader-keywords-shaderfeature-vs.html' title='Unity Shader Keywords: shader_feature vs multi_compile — Complete Comparison Table and Usage Guide'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEh9KtGRdp6re_Yt-wRAJdNhMKEvp_kmzFPUGFkYx9ifZdQDYPF6W3iUHhpMFgSHvNmgNTKF-4Sw3dbGEKPS4EnovI_rstHwMa9LFI_ae1CswB9AWNPnkfApFijkEz1ld3hxpnSW2hMkJMpOlb_AkPkQrfecnlMj-KsaSCGQwMT7y4VjLqq6vKpnoE9CrdYm=s72-w640-h426-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-6569869799178928293</id><published>2025-09-01T12:53:00.001+08:00</published><updated>2025-09-01T13:08:02.523+08:00</updated><title type='text'>Unity Brush Technology: Breakthroughs and Practical Insights — From SetPixels to Compute Shader</title><content type='html'>&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjHJs5HFqRqQRKJFSYwf7bgvfnwHCXggk3wPkmG15iKG5W7V6m_qbn0bzNn4fRhM5rnu8LzaLxnPL0CPK8D0f7eimTFQFUNIjRqe78FQH-qK4Auv3Gxl0CgWjHzCDLH00hKm6P3wJUxvDtofIwIvMaAieFw9TaFxTSug0-ToOkctfRn922leSBtu55BCmS4&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;379&quot; data-original-width=&quot;760&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjHJs5HFqRqQRKJFSYwf7bgvfnwHCXggk3wPkmG15iKG5W7V6m_qbn0bzNn4fRhM5rnu8LzaLxnPL0CPK8D0f7eimTFQFUNIjRqe78FQH-qK4Auv3Gxl0CgWjHzCDLH00hKm6P3wJUxvDtofIwIvMaAieFw9TaFxTSug0-ToOkctfRn922leSBtu55BCmS4=w640-h320&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;During the process of developing a brush system in Unity, I found that in-depth discussions about the “principles of brushes” are quite rare online. Many developers tend to use the engine’s built-in tools directly, rarely exploring the underlying mechanisms. In reality, only by truly understanding how brushes work can we break through the limitations of existing tools and create fully customized brushes tailored to our needs. This is also my motivation for writing this article — to share my technical explorations and practical insights, and to help more people understand and build their own brush systems.&lt;span&gt;&lt;/span&gt;&lt;/p&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;SetPixels32 Brush: Moving Data from CPU to GPU&lt;/h3&gt;
&lt;p&gt;
Let’s start with the simplest: SetPixels32. Anyone who has worked on fog of war systems is probably very familiar with it. Some might think using Graphics.CopyTexture is more efficient, but since brushes usually need to support arbitrary sizes, such approaches aren’t really suitable.
&lt;/p&gt;
&lt;p&gt;
The following code is a typical SetPixels32 implementation: it iterates over every pixel within the brush coverage area, calculates the color based on distance and weight, and finally applies the entire texture to the GPU in one go.
&lt;/p&gt;

&lt;pre&gt;var destColor = new Color32(0, 0, 0, 0);
for (int rowIndex = startRowIndex; rowIndex&amp;lt;=endRowIndex; ++rowIndex)
{
    for (int columnIndex = startColumnIndex; columnIndex&amp;lt;=endColumnIndex; ++columnIndex)
    {
        Vector2 gridCenter = new Vector2(
            worldMin.x + columnIndex * gridSize.x + halfGridSize.x,
            worldMin.y + rowIndex * gridSize.y + halfGridSize.y);

        int index = columnIndex + rowIndex * mapWidth;
        float sqrDistance = (brushCenter - gridCenter).sqrMagnitude;

        sqrDistance = Mathf.Clamp(sqrDistance, sqrhalfBrushInnerSize, sqrhalfBrushSize);
        float weightRatio = 1.0f - (sqrDistance - sqrhalfBrushInnerSize) / (sqrhalfBrushSize - sqrhalfBrushInnerSize);
        
        destColor = editColorBuffer[index];

        ProcessPixelColor(weightRatio, speed * 0.033f, isAdd, sqrDistance, sqrhalfBrushSize,
            in mouseDelta, ref destColor);
        
        editColorBuffer[index] = destColor;
    }
}
editTexture.SetPixels32(editColorBuffer);
editTexture.Apply();
&lt;/pre&gt;

&lt;p&gt;
The principle here is simple: the CPU processes each pixel in the brush area, then sends the entire texture data back to the GPU at once. This causes a large amount of CPU → GPU data transfer. The bigger the texture or the more textures you process simultaneously, the more apparent the performance bottleneck becomes. For brush operations that require real-time feedback and fine control, this approach is often not sufficient.
&lt;/p&gt;

&lt;h3&gt;Fragment Shader Brush: Shifting Computation to the GPU&lt;/h3&gt;
&lt;p&gt;
After understanding the performance bottleneck of SetPixels32, another way to optimize brush operations is to move the pixel processing from the CPU to the GPU — that is, using a Fragment Shader to achieve brush effects. This greatly reduces the CPU-to-GPU data transfer and allows each pixel’s calculation to be efficiently handled in parallel by the GPU.
&lt;/p&gt;
&lt;p&gt;
The following shader code demonstrates how the calculation of pixel weight and color within the brush area is entirely handled by the Fragment Shader:
&lt;/p&gt;

&lt;pre&gt;bool GetBltTextureColorAndBrushWeight(in VertexOutput input, 
    out half brushWeight, out half4 bltTextureColor)
{
    half2 dsetUV = input.screenPosition.xy / input.screenPosition.w;
    bltTextureColor = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex,
        input.screenPosition.xy / input.screenPosition.w);
    uint rowIndex = _TextureArrayIndex / 2;
    uint columnIndex = _TextureArrayIndex % 2;

    float2 wordPosition2D = float2((columnIndex + dsetUV.x) * _BrushInfo.z,
        (rowIndex + dsetUV.y) * _BrushInfo.w);

    float distance = length(wordPosition2D - _BrushTransform.xy);
    if (distance &amp;gt; _BrushTransform.w)
        return false;

    distance = clamp(distance, _BrushTransform.z, _BrushTransform.w);
    brushWeight = (1.0 - (distance - _BrushTransform.z) / _BrushInfo.y) * 
        unity_DeltaTime.z * _BrushInfo.x * sc_weightApplyRatio;
    return true;
}
&lt;/pre&gt;

&lt;p&gt;
This code mainly does two things:
&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;Calculates the current pixel’s world position based on screen coordinates, then determines whether it falls within the brush radius.&lt;/li&gt;
&lt;li&gt;If it is within range, it calculates the weight (&lt;code&gt;brushWeight&lt;/code&gt;), which determines the brush’s influence on that pixel.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Advantages&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fully handled on the GPU: No need to send pixel data from CPU back to GPU, resulting in a significant performance boost, especially suitable for high-resolution or multi-brush operations.&lt;/li&gt;
&lt;li&gt;Real-time feedback: Provides a more immediate and nuanced brush feel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;Disadvantages&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Must traverse the entire RenderTexture: Even if the brush only covers a small area, the Fragment Shader still processes the entire texture, causing unnecessary computation.&lt;/li&gt;
&lt;li&gt;Input/Output RenderTarget limitation: During processing, the input and output RenderTargets cannot be the same texture. Each draw requires copying (blitting) the original content to another RenderTexture before processing, which adds an extra GPU pass.&lt;/li&gt;
&lt;li&gt;More complex for multiple targets: Managing and optimizing brush operations on TextureArrays becomes even more challenging.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
P.S.: Unity’s official Terrain brush also uses this approach. For reference, see this link:&lt;br /&gt;
&lt;a href=&quot;https://github.com/needle-mirror/com.unity.terrain-tools/blob/master/Shaders/PaintTexture.shader&quot; target=&quot;_blank&quot;&gt;PaintTexture.shader&lt;/a&gt;
&lt;/p&gt;

&lt;h3&gt;Compute Shader Brush: OnePass High-Efficiency Processing, TextureArray Support&lt;/h3&gt;
&lt;p&gt;
After understanding the limitations of Fragment Shaders regarding Input/Output RenderTargets, we can further leverage the features of Compute Shaders to effectively solve these bottlenecks:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Input can be equal to Output&lt;/b&gt;&lt;br /&gt;
You can read and write directly on the same RenderTexture, without the need for extra Blit copies, significantly reducing resource consumption and latency.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Only process the pixels within the brush area&lt;/b&gt;&lt;br /&gt;
No need to traverse the entire RenderTexture; instead, calculations are performed precisely on the area affected by the brush, greatly improving performance.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Full support for TextureArray&lt;/b&gt;&lt;br /&gt;
You can directly read and write to TextureArrays, easily meeting advanced multi-layer texture requirements, offering greater flexibility and scalability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
These advantages make Compute Shader brushes far superior to traditional Fragment Shader brushes in terms of both performance and functionality, achieving true OnePass efficient painting.
&lt;/p&gt;

&lt;p&gt;
The following Compute Shader code demonstrates these features:
&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;Input is Output: Directly read and write on the target texture.&lt;/li&gt;
&lt;li&gt;Only process a local area: Only computes the Rect area covered by the brush.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;

&lt;pre&gt;[numthreads(8, 8, 1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{   
    if ((id.x &amp;gt;= _BrushPixelsColumnCount) ||
        (id.y &amp;gt;= _BrushPixelsRowCount))
        return;
    
    TextureIndex textureIndex;
    half brushWeight;    
    if (!GetDestPixelWeight(id.xy, textureIndex, brushWeight))
        return;

    SetEditTextureValue(textureIndex, half4(_TintColor,
        saturate(GetEditTextureValue(textureIndex).a + brushWeight)));
}
&lt;/pre&gt;

&lt;p&gt;
As shown above, the Compute Shader only processes the area affected by the brush (controlled by &lt;code&gt;_BrushPixelsColumnCount&lt;/code&gt; and &lt;code&gt;_BrushPixelsRowCount&lt;/code&gt;), and directly reads and writes on the target texture (which may be a TextureArray), fully leveraging the advantages of OnePass and high efficiency. Here, &lt;code&gt;TextureIndex&lt;/code&gt; refers to the index of the TextureArray.
&lt;/p&gt;

&lt;h3&gt;Comparison of the Three Approaches&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;SetPixels32 Brush&lt;/b&gt;&lt;br /&gt;
Advantages: Simple to implement, easy to understand.&lt;br /&gt;
Disadvantages: Large CPU→GPU data transfer, obvious performance bottleneck.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Fragment Shader Brush&lt;/b&gt;&lt;br /&gt;
Advantages: All computation is done on the GPU, improved performance, great real-time feedback.&lt;br /&gt;
Disadvantages: Must traverse the entire texture, computational waste; Input/Output RenderTarget limitations.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Compute Shader Brush&lt;/b&gt;&lt;br /&gt;
Advantages: Only processes the affected area, Input is Output, supports TextureArray, best performance and flexibility.&lt;br /&gt;
Disadvantages: More complex to implement, requires understanding of GPU parallel computation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Among these three solutions, the Compute Shader brush stands out not just for its performance advantage, but as a cleaner, more direct, and elegant solution.
&lt;/p&gt;
&lt;p&gt;
It transforms brushes from a process of data shuffling and repeated copying to a real-time creative process on the canvas by the GPU.
&lt;/p&gt;
&lt;p&gt;
This shift brings not just “faster” performance, but an entirely new experience:
&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;Artists can feel real-time feedback as if using a real paintbrush.&lt;/li&gt;
&lt;li&gt;Engineers can implement complex brush requirements with cleaner logic.&lt;/li&gt;
&lt;li&gt;Project development gains a highly efficient solution for large scenes and multi-layer textures.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;
From now on, SmallBurger’s Brush tool plugins will gradually transition from FragmentShader to ComputeShader.
&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assetstore.unity.com/publishers/86820?fbclid=IwAR0GvglGbFYqpE7LAwZJdfMJEhz_FAXjR4VhVk7QVbBjgGiUy6wYsH7E5rI&quot;&gt;SmallBurgerAssets&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/6569869799178928293/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/09/unity-brush-technology-breakthroughs.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/6569869799178928293'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/6569869799178928293'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/09/unity-brush-technology-breakthroughs.html' title='Unity Brush Technology: Breakthroughs and Practical Insights — From SetPixels to Compute Shader'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEjHJs5HFqRqQRKJFSYwf7bgvfnwHCXggk3wPkmG15iKG5W7V6m_qbn0bzNn4fRhM5rnu8LzaLxnPL0CPK8D0f7eimTFQFUNIjRqe78FQH-qK4Auv3Gxl0CgWjHzCDLH00hKm6P3wJUxvDtofIwIvMaAieFw9TaFxTSug0-ToOkctfRn922leSBtu55BCmS4=s72-w640-h320-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-4802381431046311735</id><published>2025-08-12T14:09:00.000+08:00</published><updated>2025-08-12T14:09:57.541+08:00</updated><title type='text'>A Hidden Benefit of RenderGraph in Unity 6 URP: Public Access to Core Render Resources</title><content type='html'>&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEg-jo_QATWYb-1AsZahLBDYEx8v8BF2WMgczjk8NqAAq4xH6wbu8hk1ZUxtn9z2xXttnR-GeaJQ-oM6RuzuUBx2L9SQIrwfIWTCScQRJ9ZVL4ijDqZbvQmLfcMB2t3lZwxSl_CYB0O4v0uJ-qD7Ca8xaSsF6_K-2_PROCsprA39ZrReii4Lamo9CsiiI3rf&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;512&quot; data-original-width=&quot;512&quot; height=&quot;640&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEg-jo_QATWYb-1AsZahLBDYEx8v8BF2WMgczjk8NqAAq4xH6wbu8hk1ZUxtn9z2xXttnR-GeaJQ-oM6RuzuUBx2L9SQIrwfIWTCScQRJ9ZVL4ijDqZbvQmLfcMB2t3lZwxSl_CYB0O4v0uJ-qD7Ca8xaSsF6_K-2_PROCsprA39ZrReii4Lamo9CsiiI3rf=w640-h640&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;While exploring Unity 6’s URP, I noticed an interesting change: In the 2022.3.x versions, many internal render textures (like m_DepthTexture) could only be accessed via reflection, which was neither convenient nor safe. &lt;span&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/span&gt;However, with the introduction of the RenderGraph architecture in Unity 6, these internal resources are now exposed through public APIs!&lt;p&gt;&lt;/p&gt;&lt;p&gt;This means we can now access these core rendering resources in a much safer and more stable way, without relying on hacks or reflection. In my opinion, this is a great “side benefit” brought by RenderGraph, making custom rendering workflows more flexible and projects easier to maintain.&lt;/p&gt;&lt;p&gt;If you need to deeply customize your rendering pipeline, definitely take advantage of this improvement!&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/4802381431046311735/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/08/a-hidden-benefit-of-rendergraph-in.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/4802381431046311735'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/4802381431046311735'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/08/a-hidden-benefit-of-rendergraph-in.html' title='A Hidden Benefit of RenderGraph in Unity 6 URP: Public Access to Core Render Resources'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEg-jo_QATWYb-1AsZahLBDYEx8v8BF2WMgczjk8NqAAq4xH6wbu8hk1ZUxtn9z2xXttnR-GeaJQ-oM6RuzuUBx2L9SQIrwfIWTCScQRJ9ZVL4ijDqZbvQmLfcMB2t3lZwxSl_CYB0O4v0uJ-qD7Ca8xaSsF6_K-2_PROCsprA39ZrReii4Lamo9CsiiI3rf=s72-w640-h640-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-643362307456246961</id><published>2025-07-30T22:49:00.002+08:00</published><updated>2025-07-31T00:59:30.340+08:00</updated><title type='text'>Tile3DInstancer: A Practical Solution for Large-Scale, Multi-Type GPU Instancing in Unity</title><content type='html'>&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgG7_l7_6d-LjiWWnf7R1PA5DHRuAqjoeFfrTsU16RfeNHlr2IO6LRgbm11njeyMY2ThAntKQ5Wg7_wKlJbD7uGvoNc5Pg0A5SpNaLljMJw0xEXEOmE1XroZ4WnM1cNDpKLZEThH2neBjQHUPJXDkrDcJMHPzNNDKWZRiNIyXBBgXESfFrFnshUfQLfIzgf&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;765&quot; data-original-width=&quot;1348&quot; height=&quot;364&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgG7_l7_6d-LjiWWnf7R1PA5DHRuAqjoeFfrTsU16RfeNHlr2IO6LRgbm11njeyMY2ThAntKQ5Wg7_wKlJbD7uGvoNc5Pg0A5SpNaLljMJw0xEXEOmE1XroZ4WnM1cNDpKLZEThH2neBjQHUPJXDkrDcJMHPzNNDKWZRiNIyXBBgXESfFrFnshUfQLfIzgf=w640-h364&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3 data-end=&quot;325&quot; data-start=&quot;259&quot;&gt;Have you ever run into this when building a large-scale scene?&lt;/h3&gt;&lt;p data-end=&quot;445&quot; data-start=&quot;327&quot;&gt;You used &lt;code data-end=&quot;363&quot; data-start=&quot;336&quot;&gt;DrawMeshInstancedIndirect&lt;/code&gt; to render millions of objects, and at first, it seemed like the perfect solution.&lt;/p&gt;&lt;p data-end=&quot;681&quot; data-start=&quot;447&quot;&gt;But then, as the number of instance &lt;strong data-end=&quot;492&quot; data-start=&quot;483&quot;&gt;types&lt;/strong&gt; increased, things got messy. You had to break things up into multiple buffers, issue multiple draw calls—and worst of all, you had &lt;strong data-end=&quot;680&quot; data-start=&quot;624&quot;&gt;no way to know which instances were actually visible&lt;/strong&gt;.&lt;/p&gt;&lt;p data-end=&quot;926&quot; data-start=&quot;683&quot;&gt;That means for every type of object, &lt;strong data-end=&quot;755&quot; data-start=&quot;720&quot;&gt;even if it’s not visible at all&lt;/strong&gt;, the &lt;strong data-end=&quot;822&quot; data-start=&quot;761&quot;&gt;CPU still needs to issue a DrawMeshInstancedIndirect call&lt;/strong&gt; to the GPU. That’s wasted draw overhead and CPU-GPU communication cost—for absolutely nothing rendered.&lt;/p&gt;&lt;p data-end=&quot;1128&quot; data-start=&quot;928&quot;&gt;And on top of that? You’re manually managing transforms, rotations, scaling, and LODs for potentially millions of instances. Eventually, your scene becomes an unmaintainable blob of dense matrix data.&lt;/p&gt;&lt;p data-end=&quot;444&quot; data-start=&quot;424&quot;&gt;




&lt;/p&gt;&lt;p data-end=&quot;1178&quot; data-start=&quot;1130&quot;&gt;That’s exactly why we built &lt;strong data-end=&quot;1177&quot; data-start=&quot;1158&quot;&gt;Tile3DInstancer&lt;/strong&gt;.&lt;span&gt;&lt;/span&gt;&lt;/p&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;p&gt;&lt;/p&gt;&lt;h3 data-end=&quot;1238&quot; data-start=&quot;1185&quot;&gt;Tile3DInstancer was born from a very simple idea:&lt;/h3&gt;&lt;blockquote data-end=&quot;1320&quot; data-start=&quot;1240&quot;&gt;
&lt;p data-end=&quot;1320&quot; data-start=&quot;1242&quot;&gt;&lt;strong data-end=&quot;1320&quot; data-start=&quot;1242&quot;&gt;&quot;Why are we duplicating every object, when we could just reuse templates?&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;p data-end=&quot;736&quot; data-start=&quot;703&quot;&gt;

&lt;/p&gt;&lt;p data-end=&quot;1441&quot; data-start=&quot;1322&quot;&gt;Just like building a puzzle, we only need to store &lt;strong data-end=&quot;1398&quot; data-start=&quot;1373&quot;&gt;where each piece goes&lt;/strong&gt;, not copy every piece over and over again.&lt;/p&gt;&lt;p data-end=&quot;1598&quot; data-start=&quot;1448&quot;&gt;We implemented a &lt;strong data-end=&quot;1495&quot; data-start=&quot;1465&quot;&gt;Template Cell architecture&lt;/strong&gt; that takes this concept to the extreme—and the results were not just elegant, but shockingly powerful:&lt;/p&gt;&lt;p data-end=&quot;932&quot; data-start=&quot;865&quot;&gt;
&lt;/p&gt;&lt;ul data-end=&quot;1949&quot; data-start=&quot;1600&quot;&gt;
&lt;li data-end=&quot;1655&quot; data-start=&quot;1600&quot;&gt;
&lt;p data-end=&quot;1655&quot; data-start=&quot;1602&quot;&gt;🔁 All instance data is expanded &lt;strong data-end=&quot;1655&quot; data-start=&quot;1635&quot;&gt;only when needed&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1703&quot; data-start=&quot;1656&quot;&gt;
&lt;p data-end=&quot;1703&quot; data-start=&quot;1658&quot;&gt;💾 No more transform bloat or memory overload&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1746&quot; data-start=&quot;1704&quot;&gt;
&lt;p data-end=&quot;1746&quot; data-start=&quot;1706&quot;&gt;🚀 Per-instance culling via &lt;strong data-end=&quot;1746&quot; data-start=&quot;1734&quot;&gt;CPU Jobs&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1849&quot; data-start=&quot;1747&quot;&gt;
&lt;p data-end=&quot;1849&quot; data-start=&quot;1749&quot;&gt;🧠 &lt;strong data-end=&quot;1768&quot; data-start=&quot;1752&quot;&gt;NonSync Jobs&lt;/strong&gt; for lightweight objects (e.g. grass, pebbles), to avoid blocking the main thread&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1949&quot; data-start=&quot;1850&quot;&gt;
&lt;p data-end=&quot;1949&quot; data-start=&quot;1852&quot;&gt;🏔 &lt;strong data-end=&quot;1868&quot; data-start=&quot;1855&quot;&gt;Sync Jobs&lt;/strong&gt; for rare large objects (e.g. mountains), to ensure rendering is visually correct&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiR-eV2KN2Th1JQ_XmM6c31Se4HFqf5wd6zv42XY01LyJHeu6Xf5kDD55ELY5Kul62xMLXZOX6vhzYALuSntgHIyCMtiVRdsRqDR2BqK3_cQ1GiXDlEMpxdrJW3TkYBeF9eYjE4wSo6dVsj3iGFNzxhdnUS1lzJpCSecKnOKRUWWFtmsC-taWSeN4uD22d7/s1920/SBFramework%20-%20EditTest%20-%20Android%20-%20Unity%202022.3.33f1_%20_DX11_%202025-07-10%2016-51-09.gif&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1000&quot; data-original-width=&quot;1920&quot; height=&quot;334&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiR-eV2KN2Th1JQ_XmM6c31Se4HFqf5wd6zv42XY01LyJHeu6Xf5kDD55ELY5Kul62xMLXZOX6vhzYALuSntgHIyCMtiVRdsRqDR2BqK3_cQ1GiXDlEMpxdrJW3TkYBeF9eYjE4wSo6dVsj3iGFNzxhdnUS1lzJpCSecKnOKRUWWFtmsC-taWSeN4uD22d7/w640-h334/SBFramework%20-%20EditTest%20-%20Android%20-%20Unity%202022.3.33f1_%20_DX11_%202025-07-10%2016-51-09.gif&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;Unlike traditional instancing systems that flatten every transform into a dense memory buffer, &lt;strong data-end=&quot;2180&quot; data-start=&quot;2161&quot;&gt;Tile3DInstancer&lt;/strong&gt; keeps things modular and scalable. Our &lt;strong data-end=&quot;2237&quot; data-start=&quot;2220&quot;&gt;Template Cell&lt;/strong&gt; system reuses transform data, and only expands real geometry at the right time.&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT0mwy4FX310iYQzjAHeB9tTBx45cLGNKDjUUBZ2NYSPHTtfQBfnNbdOAimoJZ5fW2hwnjQZTZ6EgjUyYbpEQgOHkyBy1S6iVaHY1GQ2faXg6Bxpqd1EZvN_Y9UHhccZzgiX3Gf4_mCjZacU0nxQvoNGZY9brYLbnZziqWvARss-STSdv_GV9Hvb9U9305/s1920/SBFramework%20-%20EditTest%20-%20Android%20-%20Unity%202022.3.33f1%20_DX11_%202025-07-10%2016-28-21.gif&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1000&quot; data-original-width=&quot;1920&quot; height=&quot;334&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT0mwy4FX310iYQzjAHeB9tTBx45cLGNKDjUUBZ2NYSPHTtfQBfnNbdOAimoJZ5fW2hwnjQZTZ6EgjUyYbpEQgOHkyBy1S6iVaHY1GQ2faXg6Bxpqd1EZvN_Y9UHhccZzgiX3Gf4_mCjZacU0nxQvoNGZY9brYLbnZziqWvARss-STSdv_GV9Hvb9U9305/w640-h334/SBFramework%20-%20EditTest%20-%20Android%20-%20Unity%202022.3.33f1%20_DX11_%202025-07-10%2016-28-21.gif&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;We also provide a full &lt;strong data-end=&quot;2487&quot; data-start=&quot;2463&quot;&gt;TileMap Editing Mode&lt;/strong&gt;, allowing you to paint out large areas using tile-based layout, and preview results in real time. No more planting thousands of objects manually in the Hierarchy view—&lt;strong data-end=&quot;2702&quot; data-start=&quot;2655&quot;&gt;everything is fast, visual, and GPU-backed.&lt;/strong&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiad_JB8Lxjoroqp0Hd7DNlD9RwcJ1WD3ZqK5gwBIolVh70bwQGkNk4wct_cqzMKtlgvdU8ju7laOvBEdQmPXNp8Z7ku3Pm_jEiosx50kcGy9aWoZvIZfoQmr6i6hkhTYPrxq4s0hyphenhyphen5pPLZLt6oxQJibk6tEXuyaAj-BgP5wn5P1iS9sdSrUUXmJEoMWpcT/s1920/SBFramework%20-%20EditTest%20-%20Android%20-%20Unity%202022.3.33f1%20_DX11_%202025-07-10%2016-23-46.gif&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1000&quot; data-original-width=&quot;1920&quot; height=&quot;334&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiad_JB8Lxjoroqp0Hd7DNlD9RwcJ1WD3ZqK5gwBIolVh70bwQGkNk4wct_cqzMKtlgvdU8ju7laOvBEdQmPXNp8Z7ku3Pm_jEiosx50kcGy9aWoZvIZfoQmr6i6hkhTYPrxq4s0hyphenhyphen5pPLZLt6oxQJibk6tEXuyaAj-BgP5wn5P1iS9sdSrUUXmJEoMWpcT/w640-h334/SBFramework%20-%20EditTest%20-%20Android%20-%20Unity%202022.3.33f1%20_DX11_%202025-07-10%2016-23-46.gif&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Want even more control? Our &lt;strong data-end=&quot;2856&quot; data-start=&quot;2831&quot;&gt;CellHolder Brush Tool&lt;/strong&gt; lets you create reusable templates with ease. You also get &lt;strong data-end=&quot;2954&quot; data-start=&quot;2916&quot;&gt;per-cell instance count monitoring&lt;/strong&gt;, so you can paint freely without accidentally overpopulating your scene.&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPg3R-97OYAdsiLox2dpVgSR1DoqNnsR_6U37dKwAVXXSH_UN5g_ZM9V_cuDqsCqYtqVhyv3e771Qk1yDBUaDq3rgbNNUs_phD7NXUkEGfBb7EJMW0lRq_a2hkK1WA-k0nJP8vF_r3nZkikNI4rK2rrHDa1w_FW9CmETdpFbOqVhH0zl4ePdQcG4qhi2r2/s1920/SBFramework%20-%20EditTest%20-%20Android%20-%20Unity%202022.3.33f1_%20_DX11_%202025-07-10%2017-07-08.gif&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1000&quot; data-original-width=&quot;1920&quot; height=&quot;334&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPg3R-97OYAdsiLox2dpVgSR1DoqNnsR_6U37dKwAVXXSH_UN5g_ZM9V_cuDqsCqYtqVhyv3e771Qk1yDBUaDq3rgbNNUs_phD7NXUkEGfBb7EJMW0lRq_a2hkK1WA-k0nJP8vF_r3nZkikNI4rK2rrHDa1w_FW9CmETdpFbOqVhH0zl4ePdQcG4qhi2r2/w640-h334/SBFramework%20-%20EditTest%20-%20Android%20-%20Unity%202022.3.33f1_%20_DX11_%202025-07-10%2017-07-08.gif&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;h3 data-end=&quot;3152&quot; data-start=&quot;3137&quot;&gt;The result?&lt;/h3&gt;&lt;p data-end=&quot;3371&quot; data-start=&quot;3154&quot;&gt;A Unity toolset that combines GPU-friendly batching, per-instance culling, and real-time TileMap editing—letting you build and run &lt;strong data-end=&quot;3327&quot; data-start=&quot;3285&quot;&gt;massive, complex, multi-type 3D worlds&lt;/strong&gt; with performance that works even on mobile.&lt;/p&gt;&lt;p&gt;🎥 Demo Video:&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;507&quot; src=&quot;https://www.youtube.com/embed/MfK6Uo_J7GQ&quot; width=&quot;609&quot; youtube-src-id=&quot;MfK6Uo_J7GQ&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;p&gt;📦 Try Tile3DInstancer now:&lt;br /&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/tools/painting/tile3dinstancer-325810&quot;&gt;Tile3DInstancer&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;🧰 More tools by SmallBurger:&lt;br /&gt;&lt;a href=&quot;https://assetstore.unity.com/publishers/86820?fbclid=IwAR0GvglGbFYqpE7LAwZJdfMJEhz_FAXjR4VhVk7QVbBjgGiUy6wYsH7E5rI&quot;&gt;SmallBurger&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;https://drive.google.com/file/d/15rysW3nFSwX7JLdYAuyf-ojaHWueCHjQ/view?usp=drive_link&quot;&gt;TestAPK&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/643362307456246961/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/07/tile3dinstancer-practical-solution-for.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/643362307456246961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/643362307456246961'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/07/tile3dinstancer-practical-solution-for.html' title='Tile3DInstancer: A Practical Solution for Large-Scale, Multi-Type GPU Instancing in Unity'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEgG7_l7_6d-LjiWWnf7R1PA5DHRuAqjoeFfrTsU16RfeNHlr2IO6LRgbm11njeyMY2ThAntKQ5Wg7_wKlJbD7uGvoNc5Pg0A5SpNaLljMJw0xEXEOmE1XroZ4WnM1cNDpKLZEThH2neBjQHUPJXDkrDcJMHPzNNDKWZRiNIyXBBgXESfFrFnshUfQLfIzgf=s72-w640-h364-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-4578102946455025859</id><published>2025-07-22T09:51:00.001+08:00</published><updated>2025-07-22T09:52:07.324+08:00</updated><title type='text'>Just Released: MobileSSAO with Full Unity 6 Support</title><content type='html'>&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEg9mtL7o91zHxuxpkS7kdZA2QomJ0rOLg7Llm0C8V5RQoUK-NpFEfnRVCMkykOgmQLDpGxYhQfzf_AgEo0h71pqHY2fClN25SuvDRrf5d7Bs9e_wEn6tWjkCxpE2jxY91ghtIVVL7Kq0eJGJTl0F5W8uAky9A1hUMp5Ngdf5nX8GD1M9E8WBrhQO-NMtA7P&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;882&quot; data-original-width=&quot;831&quot; height=&quot;640&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEg9mtL7o91zHxuxpkS7kdZA2QomJ0rOLg7Llm0C8V5RQoUK-NpFEfnRVCMkykOgmQLDpGxYhQfzf_AgEo0h71pqHY2fClN25SuvDRrf5d7Bs9e_wEn6tWjkCxpE2jxY91ghtIVVL7Kq0eJGJTl0F5W8uAky9A1hUMp5Ngdf5nX8GD1M9E8WBrhQO-NMtA7P=w603-h640&quot; width=&quot;603&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;&lt;b&gt;What&#39;s New in the Unity 6 Version of MobileSSAO?&lt;span&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;✅ Full support for &lt;b&gt;RenderGraph&lt;/b&gt; architecture&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;✅ Now compatible with &lt;b&gt;URP Forward+&lt;/b&gt; rendering path&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;✅ &lt;b&gt;Edge Down Sampling&lt;/b&gt; remains available for optimal performance&lt;br /&gt;&lt;br /&gt;🔗 &lt;strong data-end=&quot;384&quot; data-start=&quot;369&quot;&gt;Asset Store&lt;/strong&gt;:&lt;a href=&quot;https://assetstore.unity.com/packages/vfx/shaders/fullscreen-camera-effects/mobilessao-286085&quot;&gt;MobileSSAO&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;br /&gt;🔗 &lt;strong data-end=&quot;428&quot; data-start=&quot;407&quot;&gt;More Assets by Us&lt;/strong&gt;:&lt;a href=&quot;https://assetstore.unity.com/publishers/86820?fbclid=IwAR0GvglGbFYqpE7LAwZJdfMJEhz_FAXjR4VhVk7QVbBjgGiUy6wYsH7E5rI&quot;&gt;SmallBurgerAsset&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/4578102946455025859/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/07/just-released-mobilessao-with-full.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/4578102946455025859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/4578102946455025859'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/07/just-released-mobilessao-with-full.html' title='Just Released: MobileSSAO with Full Unity 6 Support'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEg9mtL7o91zHxuxpkS7kdZA2QomJ0rOLg7Llm0C8V5RQoUK-NpFEfnRVCMkykOgmQLDpGxYhQfzf_AgEo0h71pqHY2fClN25SuvDRrf5d7Bs9e_wEn6tWjkCxpE2jxY91ghtIVVL7Kq0eJGJTl0F5W8uAky9A1hUMp5Ngdf5nX8GD1M9E8WBrhQO-NMtA7P=s72-w603-h640-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-8160620376395161731</id><published>2025-07-04T17:47:00.000+08:00</published><updated>2025-07-04T17:47:10.666+08:00</updated><title type='text'>Tile3DInstancer</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;531&quot; src=&quot;https://www.youtube.com/embed/9WX4bpfNCy8&quot; width=&quot;639&quot; youtube-src-id=&quot;9WX4bpfNCy8&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;p&gt;&lt;span style=&quot;font-family: &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;PingFang TC&amp;quot;, &amp;quot;Hiragino Sans&amp;quot;, &amp;quot;Hiragino Kaku Gothic Pro&amp;quot;, &amp;quot;Noto Sans Simplified Chinese&amp;quot;, &amp;quot;Noto Sans Traditional Chinese&amp;quot;; font-size: 15px;&quot;&gt;&lt;span style=&quot;background-color: white;&quot;&gt;Tile3DInstancer is built on the core architecture of InstanceCollector (with instance culling and collection handled outside of the same job), and incorporates cell template processing for objects. It can load over 5 million instance transforms in just 2 seconds, while significantly reducing memory usage. I am currently developing an editor for it.&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Microsoft Yahei, PingFang SC, PingFang TC, Hiragino Sans, Hiragino Kaku Gothic Pro, Noto Sans Simplified Chinese, Noto Sans Traditional Chinese; font-size: 15px;&quot;&gt;If you are interested, you are welcome to download and test the APK here&lt;/span&gt;&lt;span style=&quot;font-family: Microsoft Yahei, PingFang SC, PingFang TC, Hiragino Sans, Hiragino Kaku Gothic Pro, Noto Sans Simplified Chinese, Noto Sans Traditional Chinese; font-size: 15px;&quot;&gt;:&lt;br /&gt;&lt;a href=&quot;https://drive.google.com/file/d/15rysW3nFSwX7JLdYAuyf-ojaHWueCHjQ/view?usp=drive_link&quot;&gt;https://drive.google.com/file/d/15rysW3nFSwX7JLdYAuyf-ojaHWueCHjQ/view?usp=drive_link&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;p&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/8160620376395161731/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/07/tile3dinstancer.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/8160620376395161731'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/8160620376395161731'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/07/tile3dinstancer.html' title='Tile3DInstancer'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/9WX4bpfNCy8/default.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-1230287605246666363</id><published>2025-06-16T17:27:00.005+08:00</published><updated>2025-06-17T20:01:54.713+08:00</updated><title type='text'>GodViewFakeDOF: Introducing Blur Rect Height Ratio for Better Performance</title><content type='html'>&lt;p&gt;&amp;nbsp;&lt;br /&gt;Although &lt;strong data-end=&quot;75&quot; data-start=&quot;57&quot;&gt;GodViewFakeDOF&lt;/strong&gt; already supports &lt;strong data-end=&quot;108&quot; data-start=&quot;93&quot;&gt;Mask Filter&lt;/strong&gt;, it still processes the entire screen’s pixels.&lt;br data-end=&quot;159&quot; data-start=&quot;156&quot; /&gt;
This can easily lead to performance bottlenecks when using a high &lt;strong data-end=&quot;241&quot; data-start=&quot;225&quot;&gt;Render Scale&lt;/strong&gt; and setting &lt;strong data-end=&quot;270&quot; data-start=&quot;254&quot;&gt;Blur Quality&lt;/strong&gt; to &lt;strong data-end=&quot;282&quot; data-start=&quot;274&quot;&gt;High&lt;/strong&gt;.&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgzotM6NNb3Zb_ADEox6L8p8haG0m3cE6lgv590J3-KFMO5WHzNWKhjvKuxXb52jkgD6GclfnQZG4j5e88ZCFm7bZG6xQvrlcGBKMUYXfXNb_vIabC-gTWSSj6cFnlzcxbwpcEI5rVe5xGzhi1FTWNCRb2Vzc2UIMxGdwxHex1dH3j1J6oHfHSNbmNJLIzI&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;2466&quot; data-original-width=&quot;1200&quot; height=&quot;240&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgzotM6NNb3Zb_ADEox6L8p8haG0m3cE6lgv590J3-KFMO5WHzNWKhjvKuxXb52jkgD6GclfnQZG4j5e88ZCFm7bZG6xQvrlcGBKMUYXfXNb_vIabC-gTWSSj6cFnlzcxbwpcEI5rVe5xGzhi1FTWNCRb2Vzc2UIMxGdwxHex1dH3j1J6oHfHSNbmNJLIzI&quot; width=&quot;117&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;span&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/span&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;However, in practice, only about &lt;strong data-end=&quot;346&quot; data-start=&quot;318&quot;&gt;40% of the screen height&lt;/strong&gt; actually requires blur processing.&lt;br data-end=&quot;384&quot; data-start=&quot;381&quot; /&gt;
To avoid unnecessary computation, we introduced the concept of a &lt;strong data-end=&quot;462&quot; data-start=&quot;449&quot;&gt;Blur Rect&lt;/strong&gt; — only applying blur to a designated area, significantly reducing GPU workload.&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEg05GzQVtfUlqAG249w_n_WO6b3Vbz43L2Xb8VagSh7fw_Psg6Hh4TF-CqS-A7dDtPTMoHppYYZU4mje8FFYlwSGeLqJWX6im3oaamn-uJmoequmXvzZVw545rj1xjE7DJaEalZvU6rXBQ7alRJ_8dSOot8iTTy1md56WvWqwsXBSQf3laj9SRogLqI7bnR&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;752&quot; data-original-width=&quot;422&quot; height=&quot;240&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEg05GzQVtfUlqAG249w_n_WO6b3Vbz43L2Xb8VagSh7fw_Psg6Hh4TF-CqS-A7dDtPTMoHppYYZU4mje8FFYlwSGeLqJWX6im3oaamn-uJmoequmXvzZVw545rj1xjE7DJaEalZvU6rXBQ7alRJ_8dSOot8iTTy1md56WvWqwsXBSQf3laj9SRogLqI7bnR&quot; width=&quot;135&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;This update also introduces a new parameter: &lt;strong data-end=&quot;614&quot; data-start=&quot;589&quot;&gt;&lt;code data-end=&quot;612&quot; data-start=&quot;591&quot;&gt;BlurRectHeightRatio&lt;/code&gt;&lt;/strong&gt;, allowing users to customize the vertical range of the blur area according to the needs of their scene, enabling more flexible performance tuning.&lt;br /&gt;&lt;br /&gt;🔹 The upper portion of the diagram below represents the area where &lt;strong data-end=&quot;859&quot; data-start=&quot;831&quot;&gt;blur is actually applied&lt;/strong&gt;:&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEj5cn0RuDwZAordyPn9oItqvfSxvoWc5NRToa57OTSZ3Z8KBpuPQDjeUzRBGCqUKwhanFn-o2ANSFKm-MZERvVHOLqTgeLLuL94Xi2Ldta-sVP5R0IuMfhX1CYnEeVgfvLe9mObHyknLUZTMPuVs8GwMxfgAg25VdLuQ_ZJM-9wFtLVViMh03YZjn4YLr1w&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;357&quot; data-original-width=&quot;206&quot; height=&quot;240&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEj5cn0RuDwZAordyPn9oItqvfSxvoWc5NRToa57OTSZ3Z8KBpuPQDjeUzRBGCqUKwhanFn-o2ANSFKm-MZERvVHOLqTgeLLuL94Xi2Ldta-sVP5R0IuMfhX1CYnEeVgfvLe9mObHyknLUZTMPuVs8GwMxfgAg25VdLuQ_ZJM-9wFtLVViMh03YZjn4YLr1w&quot; width=&quot;138&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;🟢 Final result shown below:&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgAA714wf-3UB_hQuVvteKlayrKnnCkB7022otm9cY1NL57PnhpkJq_DC-56lSxPR6_D676GL8dWvyUydr9Pwx4spL5Zb-L3BlNNX-J9bzrdlzuSsIrGnVeWTrhHyKNYVv-seMMw09wpR5bjMddr_35kzqjStcPgS1Z0GRq9HQ3OED8dCYdRZ_C6-HQeO0c&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;2466&quot; data-original-width=&quot;1200&quot; height=&quot;240&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgAA714wf-3UB_hQuVvteKlayrKnnCkB7022otm9cY1NL57PnhpkJq_DC-56lSxPR6_D676GL8dWvyUydr9Pwx4spL5Zb-L3BlNNX-J9bzrdlzuSsIrGnVeWTrhHyKNYVv-seMMw09wpR5bjMddr_35kzqjStcPgS1Z0GRq9HQ3OED8dCYdRZ_C6-HQeO0c&quot; width=&quot;117&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;📦 &lt;strong data-end=&quot;915&quot; data-start=&quot;895&quot;&gt;Plugin location:&lt;/strong&gt;&lt;br /&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/vfx/shaders/fullscreen-camera-effects/godviewfakedof-289580&quot;&gt;GodViewFakeDOF&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;📎 &lt;strong data-end=&quot;950&quot; data-start=&quot;920&quot;&gt;Related plugin references:&lt;/strong&gt;&lt;br /&gt;&lt;a href=&quot;https://assetstore.unity.com/publishers/86820?fbclid=IwAR0GvglGbFYqpE7LAwZJdfMJEhz_FAXjR4VhVk7QVbBjgGiUy6wYsH7E5rI&quot;&gt;SmallBurger Assets&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/1230287605246666363/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/06/godviewfakedof-introducing-blur-rect.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/1230287605246666363'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/1230287605246666363'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/06/godviewfakedof-introducing-blur-rect.html' title='GodViewFakeDOF: Introducing Blur Rect Height Ratio for Better Performance'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEgzotM6NNb3Zb_ADEox6L8p8haG0m3cE6lgv590J3-KFMO5WHzNWKhjvKuxXb52jkgD6GclfnQZG4j5e88ZCFm7bZG6xQvrlcGBKMUYXfXNb_vIabC-gTWSSj6cFnlzcxbwpcEI5rVe5xGzhi1FTWNCRb2Vzc2UIMxGdwxHex1dH3j1J6oHfHSNbmNJLIzI=s72-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-722602190651178102</id><published>2025-06-07T01:37:00.002+08:00</published><updated>2025-06-07T02:30:43.112+08:00</updated><title type='text'>Optimizing SSAO on Mobile: Introducing EdgeDownSample in MobileSSAO (SmallBurger Assets)</title><content type='html'>&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjGbDz5DwZ0TdbsJ-XpNma43EDQ9tidGdhsRTf4qxWJnf4fHY2chQE-4yiXyhPZkuyYgobYVyAFGn3TXULZ4TjbnOLkGMT3X6_uIuGxuhtyk8CKHwOk8v-yeYkWFHea0oK37czgDe-AgrtBWdorwW-a5sCwvyHWY54r5ECHITGcfNMQrVYsLgfmENKzbR_r&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;649&quot; data-original-width=&quot;1131&quot; height=&quot;368&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjGbDz5DwZ0TdbsJ-XpNma43EDQ9tidGdhsRTf4qxWJnf4fHY2chQE-4yiXyhPZkuyYgobYVyAFGn3TXULZ4TjbnOLkGMT3X6_uIuGxuhtyk8CKHwOk8v-yeYkWFHea0oK37czgDe-AgrtBWdorwW-a5sCwvyHWY54r5ECHITGcfNMQrVYsLgfmENKzbR_r=w640-h368&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p data-end=&quot;602&quot; data-start=&quot;80&quot;&gt;On mobile platforms, &lt;strong data-end=&quot;142&quot; data-start=&quot;101&quot;&gt;SSAO (Screen Space Ambient Occlusion)&lt;/strong&gt; has always been a performance-intensive visual effect.&lt;/p&gt;&lt;p data-end=&quot;602&quot; data-start=&quot;80&quot;&gt;
In &lt;strong data-end=&quot;217&quot; data-start=&quot;203&quot;&gt;MobileSSAO&lt;/strong&gt;, we have already optimized the &lt;strong data-end=&quot;271&quot; data-start=&quot;249&quot;&gt;PreDepthNormalPass&lt;/strong&gt; using &lt;strong data-end=&quot;311&quot; data-start=&quot;278&quot;&gt;MRT (Multiple Render Targets)&lt;/strong&gt;, but under high-quality settings with increased &lt;strong data-end=&quot;376&quot; data-start=&quot;360&quot;&gt;Sample Count&lt;/strong&gt;, performance bottlenecks can still be noticeable.&lt;br data-end=&quot;429&quot; data-start=&quot;426&quot; /&gt;
This is especially true on mid-to-low-end devices such as the &lt;strong data-end=&quot;504&quot; data-start=&quot;491&quot;&gt;Galaxy S8&lt;/strong&gt;, where achieving a balance between visual quality and smooth frame rates remains a key challenge.&lt;/p&gt;
&lt;p data-end=&quot;937&quot; data-start=&quot;604&quot;&gt;To address this, we’ve introduced a new feature in this update: &lt;strong data-end=&quot;686&quot; data-start=&quot;668&quot;&gt;EdgeDownSample&lt;/strong&gt;.&lt;br data-end=&quot;690&quot; data-start=&quot;687&quot; /&gt;
This is a flexible SSAO optimization technique that maintains &lt;strong data-end=&quot;773&quot; data-start=&quot;752&quot;&gt;high Sample Count&lt;/strong&gt; in the &lt;strong data-end=&quot;805&quot; data-start=&quot;781&quot;&gt;center of the screen&lt;/strong&gt;, while dynamically reducing SSAO sampling costs along the &lt;strong data-end=&quot;887&quot; data-start=&quot;864&quot;&gt;edges of the screen&lt;/strong&gt;, resulting in significantly improved performance.&lt;/p&gt;&lt;span&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/span&gt;&lt;div&gt;This design concept also aligns well with the current trend of &lt;strong data-end=&quot;1024&quot; data-start=&quot;1002&quot;&gt;Foveated Rendering&lt;/strong&gt;, which is widely adopted in &lt;strong data-end=&quot;1067&quot; data-start=&quot;1053&quot;&gt;VR devices&lt;/strong&gt; and supported by many modern hardware solutions. It is also highly suitable for &lt;strong data-end=&quot;1178&quot; data-start=&quot;1148&quot;&gt;portrait-mode mobile games&lt;/strong&gt;, where UI elements are often positioned around the edges of the screen. In such cases, reducing SSAO sampling precision in these peripheral areas has minimal visual impact but can greatly reduce GPU workload.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;div&gt;&lt;div&gt;Using the EdgeDownSampleScreenSizeRatio parameter, you can easily adjust the size of the edge zone.&lt;/div&gt;&lt;div&gt;This makes the feature especially effective for portrait-mode mobile games, where the main UI is typically placed along the screen edges—perfectly aligning with the principles of Foveated Rendering.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Performance Comparison (Galaxy S8, High-Quality Mode):&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Without &lt;strong data-end=&quot;1796&quot; data-start=&quot;1778&quot;&gt;EdgeDownSample&lt;/strong&gt; enabled:&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgpze6g7CMZgu4WbLhFyacnzqBh5bE9LjNIYf37sTPfTDLn0Vc5snR1jpfB1kYXT6pPxsIqo1Cqba4kpAMwGUFlXZCtF9Ih8yDYGDe9pORw6oXML3pcN5h0siFTu11ZlU1fsLxbvPgGYQ-asjvEoUMQM7DGXpJfGf-qcTIx3eQWJDonA-UHIfAgZdoYnAxn&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;1080&quot; data-original-width=&quot;2220&quot; height=&quot;312&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgpze6g7CMZgu4WbLhFyacnzqBh5bE9LjNIYf37sTPfTDLn0Vc5snR1jpfB1kYXT6pPxsIqo1Cqba4kpAMwGUFlXZCtF9Ih8yDYGDe9pORw6oXML3pcN5h0siFTu11ZlU1fsLxbvPgGYQ-asjvEoUMQM7DGXpJfGf-qcTIx3eQWJDonA-UHIfAgZdoYnAxn=w640-h312&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;With &lt;strong data-end=&quot;1894&quot; data-start=&quot;1876&quot;&gt;EdgeDownSample&lt;/strong&gt; enabled:&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEi1aU5nk2qML6PiKeauIfAEKeNIG8z0OSrJSjlD9Zi8wSK9BZineUooeZnmG55xqxVkhMJoIPI8QnvQi4YdQBHi9YGS0q9KyoIntOLcUE62pUEWH7IJm_zhUHlpmpi3fygyvpvhGCMbxxnAHuxcsdYok26uiC8LsfRGj1Oc_AkLga7mNu9os0ARnzsZrEzb&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;1080&quot; data-original-width=&quot;2220&quot; height=&quot;312&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEi1aU5nk2qML6PiKeauIfAEKeNIG8z0OSrJSjlD9Zi8wSK9BZineUooeZnmG55xqxVkhMJoIPI8QnvQi4YdQBHi9YGS0q9KyoIntOLcUE62pUEWH7IJm_zhUHlpmpi3fygyvpvhGCMbxxnAHuxcsdYok26uiC8LsfRGj1Oc_AkLga7mNu9os0ARnzsZrEzb=w640-h312&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Related video：&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;555&quot; src=&quot;https://www.youtube.com/embed/pfBx7pDUUrg&quot; width=&quot;668&quot; youtube-src-id=&quot;pfBx7pDUUrg&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;br /&gt;&lt;div style=&quot;text-align: left;&quot;&gt;Asset:&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/vfx/shaders/fullscreen-camera-effects/mobilessao-286085&quot;&gt;MobileSSAO&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Other related assets:&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://assetstore.unity.com/publishers/86820?fbclid=IwAR0GvglGbFYqpE7LAwZJdfMJEhz_FAXjR4VhVk7QVbBjgGiUy6wYsH7E5rI&quot;&gt;SmallBurger Assets&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/722602190651178102/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/06/optimizing-ssao-on-mobile-introducing.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/722602190651178102'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/722602190651178102'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/06/optimizing-ssao-on-mobile-introducing.html' title='Optimizing SSAO on Mobile: Introducing EdgeDownSample in MobileSSAO (SmallBurger Assets)'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEjGbDz5DwZ0TdbsJ-XpNma43EDQ9tidGdhsRTf4qxWJnf4fHY2chQE-4yiXyhPZkuyYgobYVyAFGn3TXULZ4TjbnOLkGMT3X6_uIuGxuhtyk8CKHwOk8v-yeYkWFHea0oK37czgDe-AgrtBWdorwW-a5sCwvyHWY54r5ECHITGcfNMQrVYsLgfmENKzbR_r=s72-w640-h368-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-878036517003315227</id><published>2025-05-28T11:22:00.003+08:00</published><updated>2025-05-28T11:31:06.559+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="技術分享"/><title type='text'>ScriptableRenderPass to RenderGraph: Smooth Transition in Unity 6 URP</title><content type='html'>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEi15pYK5Hf3VkkwiEpiPztqwtv5jR8VjmFDHsMdGXc9d2s1iAnYJu6O60pV4T0aBIKrlgdZZQdC50C8G7p-FR5YRp6UcaqquuFi0odnNgkWRzC2UhlD5FkvpE_Xnd7sX88TIqpozFjICCSmRCCR8hDebbpZuQGXGsQH1b67wtCvqR4JYlaMKSEyskhb2n6v&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;336&quot; data-original-width=&quot;646&quot; height=&quot;332&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEi15pYK5Hf3VkkwiEpiPztqwtv5jR8VjmFDHsMdGXc9d2s1iAnYJu6O60pV4T0aBIKrlgdZZQdC50C8G7p-FR5YRp6UcaqquuFi0odnNgkWRzC2UhlD5FkvpE_Xnd7sX88TIqpozFjICCSmRCCR8hDebbpZuQGXGsQH1b67wtCvqR4JYlaMKSEyskhb2n6v=w640-h332&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white;&quot;&gt;With the official release of Unity 6 LTS, RenderGraph is no longer an experimental toy—it has become a significant evolution in Unity’s rendering pipeline. For developers using the Universal Render Pipeline (URP), how to seamlessly transition from the traditional ScriptableRenderPass to RenderGraph has become a hot topic.&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;p&gt;&lt;/p&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white;&quot;&gt;I created the&amp;nbsp;&lt;span style=&quot;font-weight: 600;&quot;&gt;URPRenderGraphBridgeExample&lt;/span&gt;&amp;nbsp;project with the hope of helping everyone maintain compatibility with different versions of Unity’s rendering systems on a single codebase, making it easier to learn and use RenderGraph.&lt;/span&gt;&lt;/p&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white;&quot;&gt;Next, I’d like to share some design ideas and insights, which I hope will help those of you who are exploring RenderGraph!&lt;/span&gt;&lt;/p&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white;&quot;&gt;Let me start with a key takeaway:&amp;nbsp;&lt;span style=&quot;font-weight: 600;&quot;&gt;“Always bind your resources precisely.”&lt;/span&gt;&lt;br /&gt;When working with RenderGraph, this should be like a warning label stuck on every developer’s monitor. The core philosophy of RenderGraph is explicit resource declaration—you must clearly state what you’re using and how you’re using it. No ambiguity, no wishful thinking.&lt;/span&gt;&lt;/p&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white;&quot;&gt;Now, let’s go through the overall workflow:&lt;/span&gt;&lt;/p&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&lt;span style=&quot;font-weight: 600;&quot;&gt;1. Retrieve relevant bound resources from ContextContainer (frameData):&lt;/span&gt;&lt;br /&gt;In the Unity RenderGraph workflow, you typically obtain the necessary context for the current frame (such as camera data, command buffer, resource handles, etc.) from RenderGraphContext or a similar container.&lt;/span&gt;&lt;/p&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white; font-weight: 600;&quot;&gt;2. Create a RenderGraphBuilder interface and corresponding PassData:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: white;&quot;&gt;You call&amp;nbsp;&lt;/span&gt;&lt;code class=&quot;&quot; style=&quot;background-color: #eeeeee; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;RenderGraph.AddRenderPass&amp;lt;T&amp;gt;(&quot;PassName&quot;, out passData)&lt;/code&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&amp;nbsp;to create a pass, which returns a RenderGraphBuilder. You use this builder to set up resource reads/writes, dependencies, and more.&lt;/span&gt;&lt;/p&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white; font-weight: 600;&quot;&gt;3. Bind the required resources precisely:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: white;&quot;&gt;With the builder’s&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #eeeeee;&quot;&gt;&lt;code class=&quot;&quot; style=&quot;border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;ReadTexture&lt;/code&gt;,&amp;nbsp;&lt;code class=&quot;&quot; style=&quot;border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;WriteTexture&lt;/code&gt;,&amp;nbsp;&lt;code class=&quot;&quot; style=&quot;border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;ReadBuffer&lt;/code&gt;&lt;/span&gt;&lt;span style=&quot;background-color: white;&quot;&gt;, etc., you explicitly declare which resources the current pass needs. RenderGraph will automatically track dependencies.&lt;/span&gt;&lt;/p&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&lt;span style=&quot;font-weight: 600;&quot;&gt;4. Use the RenderGraphBuilder interface to call SetRenderFunc:&lt;/span&gt;&lt;br /&gt;You must use a lambda function here; you cannot use a global function, or it will cause garbage collection (GC) issues.&lt;/span&gt;&lt;/p&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white; font-weight: 600;&quot;&gt;5. The entry point: RecordRenderGraph:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: white;&quot;&gt;As the name suggests, this is where you collect your rendering pass requirements. The order and process of rendering are managed by RenderGraph. For example, instead of worrying about how many times you call&amp;nbsp;&lt;/span&gt;&lt;code class=&quot;&quot; style=&quot;background-color: #eeeeee; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;ConfigureRenderTarget&lt;/code&gt;&lt;span style=&quot;background-color: white;&quot;&gt;, you just need to specify what you want to bind (remember: bind precisely!).&lt;/span&gt;&lt;/p&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white;&quot;&gt;Let’s take a look at the implementation of a typical render object pass,&amp;nbsp;&lt;/span&gt;&lt;code class=&quot;&quot; style=&quot;background-color: #eeeeee; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;CustomRenderObjectPass&lt;/code&gt;&lt;span style=&quot;background-color: white;&quot;&gt;:&lt;/span&gt;&lt;/p&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white; font-weight: 600;&quot;&gt;First, retrieve the relevant resources:&lt;/span&gt;&lt;/p&gt;&lt;pre style=&quot;background: rgb(26, 27, 38); border-radius: 6px; color: #cbd2ea; direction: ltr; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 0px; position: relative;&quot;&gt;&lt;span class=&quot;copy-code-button&quot; style=&quot;background-color: #303030; border-color: rgb(222, 222, 222); border-image: none 100% / 1 / 0 stretch; border-radius: 10px; border-style: solid; border-width: 0.666667px; color: white; cursor: pointer; opacity: 0; padding: 0px 5px; pointer-events: none; position: absolute; right: 10px; top: 1em; transform: translateX(10px); transition: 0.3s;&quot;&gt;&lt;/span&gt;&lt;code class=&quot;hljs language-csharp&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border-radius: 6px; border: 0px; display: block; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; line-height: inherit; margin: 0px; max-height: none; max-width: 100%; overflow-wrap: normal; overflow: auto hidden; padding: 1em; word-break: normal;&quot;&gt;UniversalResourceData resourceData = frameData.Get&amp;lt;UniversalResourceData&amp;gt;();
UniversalRenderingData renderingData = frameData.Get&amp;lt;UniversalRenderingData&amp;gt;();
UniversalCameraData cameraData = frameData.Get&amp;lt;UniversalCameraData&amp;gt;();
UniversalLightData lightData = frameData.Get&amp;lt;UniversalLightData&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white; font-weight: 600;&quot;&gt;Next, set up the Builder interface:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: white;&quot;&gt;Use the builder to clearly define how each resource will be accessed.&lt;/span&gt;&lt;br /&gt;&lt;code class=&quot;&quot; style=&quot;background-color: #eeeeee; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;SetRenderAttachment&lt;/code&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&amp;nbsp;and&amp;nbsp;&lt;/span&gt;&lt;code class=&quot;&quot; style=&quot;background-color: #eeeeee; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;SetRenderAttachmentDepth&lt;/code&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&amp;nbsp;are especially important here—unlike&amp;nbsp;&lt;/span&gt;&lt;code class=&quot;&quot; style=&quot;background-color: #eeeeee; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;UseTexture&lt;/code&gt;&lt;span style=&quot;background-color: white;&quot;&gt;, RenderGraph will batch render state changes for these attachments. For other&amp;nbsp;&lt;/span&gt;&lt;code class=&quot;&quot; style=&quot;background-color: #eeeeee; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;UseTexture&lt;/code&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&amp;nbsp;calls, if the resource is not written to, just specify&amp;nbsp;&lt;/span&gt;&lt;code class=&quot;&quot; style=&quot;background-color: white; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;read&lt;/code&gt;&lt;span style=&quot;background-color: white;&quot;&gt;.&lt;/span&gt;&lt;/p&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white; font-weight: 600;&quot;&gt;P.S.:&lt;/span&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&amp;nbsp;If you’re using ForwardPlus, you must set&amp;nbsp;&lt;/span&gt;&lt;code class=&quot;&quot; style=&quot;background-color: #eeeeee; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;builder.UseAllGlobalTextures(true)&lt;/code&gt;&lt;span style=&quot;background-color: white;&quot;&gt;. I personally disagree with this approach—shouldn’t we specify resource usage precisely? Using everything means no optimization. I think Unity should provide a handle for ZBins-related buffers so we can bind resources precisely. See the code comments for more details.&lt;/span&gt;&lt;/p&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white; font-weight: 600;&quot;&gt;Then, RenderStateBlock:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: white;&quot;&gt;Since&amp;nbsp;&lt;/span&gt;&lt;code class=&quot;&quot; style=&quot;background-color: #eeeeee; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;RendererListParams&lt;/code&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&amp;nbsp;requires a&amp;nbsp;&lt;/span&gt;&lt;code class=&quot;&quot; style=&quot;background-color: #eeeeee; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;RenderStateBlock&lt;/code&gt;&lt;span style=&quot;background-color: white;&quot;&gt;, you must provide one even if you don’t use it—just set it to&amp;nbsp;&lt;/span&gt;&lt;code class=&quot;&quot; style=&quot;background-color: #eeeeee; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;RenderStateMask.Nothing&lt;/code&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&amp;nbsp;if you rely entirely on material definitions.&lt;/span&gt;&lt;/p&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&lt;span style=&quot;font-weight: 600;&quot;&gt;Finally, SetRenderFunc:&lt;/span&gt;&lt;br /&gt;This is where the actual rendering call happens. In my tests, you must use a lambda function to avoid GC. If you don’t like this style, consider calling a static function within the lambda to maintain a reusable structure.&lt;/span&gt;&lt;/p&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white;&quot;&gt;As for the example of a full-screen rendering pass (&lt;/span&gt;&lt;code class=&quot;&quot; style=&quot;background-color: #eeeeee; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;DistortionPass&lt;/code&gt;&lt;span style=&quot;background-color: white;&quot;&gt;), there’s nothing particularly special to mention, except that the render target written to is only used by the current pass. So here, use&amp;nbsp;&lt;/span&gt;&lt;code class=&quot;&quot; style=&quot;background-color: #eeeeee; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;builder.UseTexture(activeColorTH, AccessFlags.Write)&lt;/code&gt;&lt;code class=&quot;&quot; style=&quot;background-color: white; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;;&lt;/code&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&amp;nbsp;and call&amp;nbsp;&lt;/span&gt;&lt;code class=&quot;&quot; style=&quot;background-color: #eeeeee; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;Blitter.BlitCameraTexture&lt;/code&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&amp;nbsp;to handle the full-screen pass.&lt;/span&gt;&lt;/p&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&lt;span style=&quot;font-weight: 600;&quot;&gt;Finally, regarding compatibility between 2022.3.x and Unity 6:&lt;/span&gt;&lt;br /&gt;I use partial classes to separate the logic, unlike Unity URP where everything is written in a single RenderPass. Here’s a summary:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; padding-left: 2em;&quot;&gt;&lt;li&gt;&lt;span style=&quot;background-color: white; font-weight: 600;&quot;&gt;Shared parts:&lt;/span&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&amp;nbsp;Elements like&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #eeeeee;&quot;&gt;&lt;code class=&quot;&quot; style=&quot;border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;RenderEvent&lt;/code&gt;,&amp;nbsp;&lt;code class=&quot;&quot; style=&quot;border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;ShaderTagID&lt;/code&gt;,&amp;nbsp;&lt;code class=&quot;&quot; style=&quot;border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;FilteringSettings&lt;/code&gt;&lt;/span&gt;&lt;span style=&quot;background-color: white;&quot;&gt;, and&amp;nbsp;&lt;/span&gt;&lt;code class=&quot;&quot; style=&quot;background-color: #eeeeee; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;ProfileSampler&lt;/code&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&amp;nbsp;are kept in the original RenderPass.&lt;/span&gt;&lt;/li&gt;&lt;li style=&quot;margin-top: 0.25em;&quot;&gt;&lt;span style=&quot;background-color: white; font-weight: 600;&quot;&gt;Original RenderPass:&lt;/span&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&amp;nbsp;Override&amp;nbsp;&lt;/span&gt;&lt;code class=&quot;&quot; style=&quot;background-color: #eeeeee; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;Execute&lt;/code&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&amp;nbsp;and use the command buffer as before.&lt;/span&gt;&lt;/li&gt;&lt;li style=&quot;margin-top: 0.25em;&quot;&gt;&lt;span style=&quot;background-color: white; font-weight: 600;&quot;&gt;RenderGraphPass:&lt;/span&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&amp;nbsp;The file name is suffixed with _RenderGraph, and you override&amp;nbsp;&lt;/span&gt;&lt;code class=&quot;&quot; style=&quot;background-color: #eeeeee; border-radius: 6px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 11.9px; margin: 0px; max-height: none; overflow-y: hidden; padding: 3px 5px; white-space-collapse: break-spaces;&quot;&gt;RecordRenderGraph&lt;/code&gt;&lt;span style=&quot;background-color: white;&quot;&gt;.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white;&quot;&gt;Why use partial classes instead of inheritance? Mainly because the event sources are different, so inheritance doesn’t fit well. If anyone has better suggestions for splitting the logic, feel free to comment and discuss.&lt;/span&gt;&lt;/p&gt;&lt;p dir=&quot;auto&quot; node=&quot;[object Object]&quot; style=&quot;color: #24292f; font-family: &amp;quot;Noto Sans&amp;quot;, &amp;quot;SF Pro SC&amp;quot;, &amp;quot;SF Pro Text&amp;quot;, &amp;quot;SF Pro Icons&amp;quot;, &amp;quot;PingFang SC&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&lt;span style=&quot;font-weight: 600;&quot;&gt;Related GitHub project:&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/AkilarLiao/URPRenderGraphBridgeExample&quot;&gt;URPRenderGraphBridgeExample&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/878036517003315227/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/scriptablerenderpass-to-rendergraph.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/878036517003315227'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/878036517003315227'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/scriptablerenderpass-to-rendergraph.html' title='ScriptableRenderPass to RenderGraph: Smooth Transition in Unity 6 URP'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEi15pYK5Hf3VkkwiEpiPztqwtv5jR8VjmFDHsMdGXc9d2s1iAnYJu6O60pV4T0aBIKrlgdZZQdC50C8G7p-FR5YRp6UcaqquuFi0odnNgkWRzC2UhlD5FkvpE_Xnd7sX88TIqpozFjICCSmRCCR8hDebbpZuQGXGsQH1b67wtCvqR4JYlaMKSEyskhb2n6v=s72-w640-h332-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-8409354801361548235</id><published>2025-05-23T23:19:00.001+08:00</published><updated>2025-05-23T23:24:22.979+08:00</updated><title type='text'>Try It Now – MobileSSAO Now Supports RenderGraph (Unity 6 Ready!)</title><content type='html'>&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgdOzaNrX5qoRvYCHH_kp66H8Ck6mtF9GBoxbeCfAjza__mxTsR47x-b5HXHMlNmWe0NZTo-s0sVI1WmS0cQwizlMauPLcuyKcJu6y9iT_AYLM1WXUju_umQOkp-PbpUJq2vwnOJRDRlc_cYri_hgx_r-c-Ls07DdHF92snb30mWfBKrnwfqwBXLa6zGYk5&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;811&quot; data-original-width=&quot;1606&quot; height=&quot;324&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgdOzaNrX5qoRvYCHH_kp66H8Ck6mtF9GBoxbeCfAjza__mxTsR47x-b5HXHMlNmWe0NZTo-s0sVI1WmS0cQwizlMauPLcuyKcJu6y9iT_AYLM1WXUju_umQOkp-PbpUJq2vwnOJRDRlc_cYri_hgx_r-c-Ls07DdHF92snb30mWfBKrnwfqwBXLa6zGYk5=w640-h324&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;We’re excitgd to announce the latest update to **MobileSSAO**:&lt;br /&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;✅ Fixed an issue where shader variants might fail to compile.&amp;nbsp;&amp;nbsp;&lt;/li&gt;&lt;li&gt;✅ Added initial support for the new RenderGraph pipeline (tested in Unity 2022.3.x and newer).&amp;nbsp;&amp;nbsp;&lt;/li&gt;&lt;li&gt;✅ Fully tested and verified in both Unity 2022.3.x and Unity 6.0.&lt;/li&gt;&lt;/ol&gt;This update ensures smooth compatibility with current and next-gen Unity versions, while maintaining maximum performance for mobile platforms.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/vfx/shaders/fullscreen-camera-effects/mobilessao-286085&quot;&gt;MobileSSAO Asset&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;https://assetstore.unity.com/publishers/86820?fbclid=IwAR0GvglGbFYqpE7LAwZJdfMJEhz_FAXjR4VhVk7QVbBjgGiUy6wYsH7E5rI&quot;&gt;SmallBurgertAssets&lt;/a&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/8409354801361548235/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/try-it-now-mobilessao-now-supports.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/8409354801361548235'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/8409354801361548235'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/try-it-now-mobilessao-now-supports.html' title='Try It Now – MobileSSAO Now Supports RenderGraph (Unity 6 Ready!)'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEgdOzaNrX5qoRvYCHH_kp66H8Ck6mtF9GBoxbeCfAjza__mxTsR47x-b5HXHMlNmWe0NZTo-s0sVI1WmS0cQwizlMauPLcuyKcJu6y9iT_AYLM1WXUju_umQOkp-PbpUJq2vwnOJRDRlc_cYri_hgx_r-c-Ls07DdHF92snb30mWfBKrnwfqwBXLa6zGYk5=s72-w640-h324-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-5044820703376207686</id><published>2025-05-12T17:53:00.002+08:00</published><updated>2025-05-12T17:53:37.047+08:00</updated><title type='text'>A Tribute to Ghost of Tsushima: Creating a High-Quality, Mobile-Friendly GPU Grass System</title><content type='html'>&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgiRyopR6_a45cC3rKJwGnEcF-8mfBriclxHR0V4PVLwN6bcZH2D9sUAb5dJTZ_SLwBP9-rPRuSYdAO8OGi9H8KRYefzoskGs-XpjR8BNPER67wXeIG-MRoNNjBrw8oqo3dw72mNIUIS3XOoFzB3e6_gBykMpbEqhXsialFdKo9u2xJD5_m1uzf4MLCKTnF&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;429&quot; data-original-width=&quot;786&quot; height=&quot;350&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgiRyopR6_a45cC3rKJwGnEcF-8mfBriclxHR0V4PVLwN6bcZH2D9sUAb5dJTZ_SLwBP9-rPRuSYdAO8OGi9H8KRYefzoskGs-XpjR8BNPER67wXeIG-MRoNNjBrw8oqo3dw72mNIUIS3XOoFzB3e6_gBykMpbEqhXsialFdKo9u2xJD5_m1uzf4MLCKTnF=w640-h350&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;div style=&quot;text-align: center;&quot;&gt;GPUGrassBladePainter&lt;/div&gt;&lt;/span&gt;&lt;br /&gt;&lt;div&gt;The vegetation system in Ghost of Tsushima left a lasting impression on me. Its rice-plant-like grass blade design avoids the common unnatural look of billboards and eliminates the visual flaws of cross-plane meshes when viewed from above. This became the starting point for developing GPUGrassBladePainter—my goal was to create a grass system capable of delivering similar visual quality on mobile platforms.&lt;span&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/span&gt;&lt;p&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;A Framework Tailored for Mobile Platforms&lt;/h3&gt;&lt;p&gt;Since the target platform is mobile devices, the first challenge to tackle was the bottleneck of memory usage and loading performance. Traditional approaches often rely on pre-storing large amounts of Transform data, which not only consumes memory but also slows down loading times. To address this, I adopted a fully procedural method for randomly distributing grass positions—saving resources while improving efficiency.&lt;/p&gt;&lt;p&gt;Another common issue is the use of SSBOs (Shader Storage Buffer Objects), which either perform poorly or are completely unsupported on many mobile devices. To ensure both compatibility and runtime performance, I chose to store grass culling results using RenderTextures instead.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEi89Hp1_lX-68sf-QowG9AA_rYdCo-6G0sZvs7l6R_OSiWFy42ICKriZzN5-cmR1b5BJyJ_CAgetyBOqoufTQW8wWVXDcdTEKXnXUagMnjODyZuISfStUkBFGApiot-eDzo6yFKZYoLwWiotNS1pQ-bV93XHmlhkv3KrhAFhynKBXlE8lGqnYO56lEUzJR0&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;319&quot; data-original-width=&quot;595&quot; height=&quot;344&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEi89Hp1_lX-68sf-QowG9AA_rYdCo-6G0sZvs7l6R_OSiWFy42ICKriZzN5-cmR1b5BJyJ_CAgetyBOqoufTQW8wWVXDcdTEKXnXUagMnjODyZuISfStUkBFGApiot-eDzo6yFKZYoLwWiotNS1pQ-bV93XHmlhkv3KrhAFhynKBXlE8lGqnYO56lEUzJR0=w640-h344&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;Storing culling results in a RenderTexture&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Creating Rice-Plant-Like Grass Blades&lt;/h3&gt;&lt;div&gt;&lt;p class=&quot;&quot; data-end=&quot;388&quot; data-start=&quot;43&quot;&gt;Capturing the look and feel of rice plants was the most important goal of the entire system. I used randomly generated curve parameters to give each grass blade a naturally curved shape. This procedural approach produces non-repetitive, non-linear blade forms, recreating the layered and swaying beauty of rice fields as they move with the wind.&lt;/p&gt;
&lt;p class=&quot;&quot; data-end=&quot;599&quot; data-start=&quot;390&quot;&gt;To prevent the grass from looking too chaotic, I integrated a &lt;strong data-end=&quot;469&quot; data-start=&quot;452&quot;&gt;Voronoi Clamp&lt;/strong&gt; algorithm, which automatically clusters the blades into visually coherent patches—achieving a natural “growing in clumps” effect.&lt;/p&gt;&lt;p class=&quot;&quot; data-end=&quot;599&quot; data-start=&quot;390&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgCmsW0K0i4IW5LNHcFZOE78pPOh_2BUgG2OtL6lAaCUwSQrETLMzChaU5DOZc2Ai0BqVhe7yp7_HrhbstAyvNg-IDXRp18j0OAS5U-HwNTNnXKCvOtkOZmJkVTD9KtDjTnttpdh__n4MjjIgboVN2kexhyM_vOeW_3DiHf7JbB2F5gdL1HWA5mbmHsvcuR&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;425&quot; data-original-width=&quot;786&quot; height=&quot;346&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgCmsW0K0i4IW5LNHcFZOE78pPOh_2BUgG2OtL6lAaCUwSQrETLMzChaU5DOZc2Ai0BqVhe7yp7_HrhbstAyvNg-IDXRp18j0OAS5U-HwNTNnXKCvOtkOZmJkVTD9KtDjTnttpdh__n4MjjIgboVN2kexhyM_vOeW_3DiHf7JbB2F5gdL1HWA5mbmHsvcuR=w640-h346&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;div style=&quot;text-align: center;&quot;&gt;A case of being extremely close to the clamp center point&lt;/div&gt;&lt;/span&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;p class=&quot;&quot; data-end=&quot;599&quot; data-start=&quot;390&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgkJQtIFwxJhbybuPUUuMp7rQKYDoF46MrPEWOYhCDL8efMRYKw4dWF97SYKFDu7n1YG-y7U-jhsXbU0JyF2VAXxYCyaJ0S3VF72fovx_M7oFmAeyZZ8oipOZ00tgmCXAVdY1KHiTloETgM4s1hdkRxRxg3ljyjjdjyP3mkWOgXya_FJM_5Qu_zKRQmVxQh&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;319&quot; data-original-width=&quot;595&quot; height=&quot;344&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgkJQtIFwxJhbybuPUUuMp7rQKYDoF46MrPEWOYhCDL8efMRYKw4dWF97SYKFDu7n1YG-y7U-jhsXbU0JyF2VAXxYCyaJ0S3VF72fovx_M7oFmAeyZZ8oipOZ00tgmCXAVdY1KHiTloETgM4s1hdkRxRxg3ljyjjdjyP3mkWOgXya_FJM_5Qu_zKRQmVxQh=w640-h344&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;Pre-generated Voronoi Clamp texture&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;h3 class=&quot;&quot; data-end=&quot;38&quot; data-start=&quot;0&quot;&gt;Dynamic Simulation of Natural Wind&lt;/h3&gt;&lt;p class=&quot;&quot; data-end=&quot;599&quot; data-start=&quot;390&quot;&gt;
&lt;/p&gt;&lt;p class=&quot;&quot; data-end=&quot;435&quot; data-start=&quot;40&quot;&gt;The swaying of the grass isn’t just a simple animation—it simulates the global behavior of a natural wind field. By integrating wind parameters with the dynamic curves of each grass blade, I achieved a natural effect where wind intensity changes over time and space, resulting in varied swaying patterns across different areas. This brings the scene to life with a more dynamic and organic feel.&lt;/p&gt;&lt;p class=&quot;&quot; data-end=&quot;435&quot; data-start=&quot;40&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhIK0Le7zkVINVb8OpvAKhEsI3bmBPKTyaRRgaa3msLK70JkNWAMz5-EJKWqf3yoDd5x3vtBJbF1ssJ2fDbUc7h8BErViVF0JTZxyQYfDYLlIR3UCDpGa1uY7teUJTrMpzSx-21kvCwgt-TUGtkICwak8aI6vX3wp4doCV5mgXLSbEIBQdNgzjOcF7kUowZ&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;302&quot; data-original-width=&quot;536&quot; height=&quot;360&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhIK0Le7zkVINVb8OpvAKhEsI3bmBPKTyaRRgaa3msLK70JkNWAMz5-EJKWqf3yoDd5x3vtBJbF1ssJ2fDbUc7h8BErViVF0JTZxyQYfDYLlIR3UCDpGa1uY7teUJTrMpzSx-21kvCwgt-TUGtkICwak8aI6vX3wp4doCV5mgXLSbEIBQdNgzjOcF7kUowZ=w640-h360&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;Demonstration of Randomized Wind Integrated with Curve Parameters&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;h3 class=&quot;&quot; data-end=&quot;47&quot; data-start=&quot;0&quot;&gt;Performance-Oriented GPU Culling Techniques&lt;/h3&gt;&lt;p class=&quot;&quot; data-end=&quot;435&quot; data-start=&quot;40&quot;&gt;
&lt;/p&gt;&lt;p class=&quot;&quot; data-end=&quot;325&quot; data-start=&quot;49&quot;&gt;In scenes with a large number of grass blades, performance management is crucial. I implemented GPU-based frustum culling and mask filter techniques to ensure that only visible areas are rendered. This significantly reduces rendering costs and improves frame rate performance.&lt;/p&gt;&lt;p class=&quot;&quot; data-end=&quot;325&quot; data-start=&quot;49&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgrW5ANgzUY4Ya8zkSFVw5UPDPvAVIg1eg0cWvJ2ocRT1IVHbpO62mNDsroEaWH7PfLKavcIgMesnexjFForPpQCwbl5uM8b6Qoos8IpY5Yi4sQkxUqfsO1J6NC3rW3wINka-d8wL5hJ8zS4c_21bh7sldYDN1Tn_lBOZ2hxO4_lNdN1ytVrF4fc3BgOuHM&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;468&quot; data-original-width=&quot;786&quot; height=&quot;382&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgrW5ANgzUY4Ya8zkSFVw5UPDPvAVIg1eg0cWvJ2ocRT1IVHbpO62mNDsroEaWH7PfLKavcIgMesnexjFForPpQCwbl5uM8b6Qoos8IpY5Yi4sQkxUqfsO1J6NC3rW3wINka-d8wL5hJ8zS4c_21bh7sldYDN1Tn_lBOZ2hxO4_lNdN1ytVrF4fc3BgOuHM=w640-h382&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;Only display grass within the view frustum and areas with MaskFilterMap values.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;p class=&quot;&quot; data-end=&quot;325&quot; data-start=&quot;49&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhMoh-9V8R96_1YuTjnp_0IJRdvev0leOICrGVIjvR5RvOZQmcAge2oBtLdHDONFj3I4zKewAwmUqErjsZoX0nLr0pBzdreriny-ljOnDx2CC2Knc8glG8NmLnznlilfklk6cX9YPD5aeCHzOPWN2y3ssYal9XNokrQJWDWcfVbbCROAE_t9lGdn2cVA7tp&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;319&quot; data-original-width=&quot;597&quot; height=&quot;342&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhMoh-9V8R96_1YuTjnp_0IJRdvev0leOICrGVIjvR5RvOZQmcAge2oBtLdHDONFj3I4zKewAwmUqErjsZoX0nLr0pBzdreriny-ljOnDx2CC2Knc8glG8NmLnznlilfklk6cX9YPD5aeCHzOPWN2y3ssYal9XNokrQJWDWcfVbbCROAE_t9lGdn2cVA7tp=w640-h342&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;Mask Filter map&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;h3 class=&quot;&quot; data-end=&quot;36&quot; data-start=&quot;0&quot;&gt;Brush Tools Designed for Artists&lt;/h3&gt;&lt;p class=&quot;&quot; data-end=&quot;344&quot; data-start=&quot;38&quot;&gt;&lt;strong data-end=&quot;62&quot; data-start=&quot;38&quot;&gt;GPUGrassBladePainter&lt;/strong&gt; is a brush-based toolset that allows artists to intuitively paint grass masks—wherever you paint, grass grows. Beyond that, it supports automatic alignment to surface height, even on non-Terrain objects, allowing grass to naturally conform to the ground without manual adjustments.&lt;/p&gt;&lt;p class=&quot;&quot; data-end=&quot;325&quot; data-start=&quot;49&quot;&gt;

&lt;/p&gt;&lt;p class=&quot;&quot; data-end=&quot;562&quot; data-start=&quot;346&quot;&gt;To further enhance the editing workflow, GPU-based brush functionality is integrated, enabling real-time visual feedback. This &quot;what you see is what you get&quot; approach greatly improves editing efficiency and fluidity.&lt;/p&gt;&lt;p class=&quot;&quot; data-end=&quot;562&quot; data-start=&quot;346&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjrdVSE1lEaiUQLd4nRzV6JXY06w1s2JDbFN5SYTxlhSk-d7OX1tqx78yy3i4fmRqhpcoPbl1xkSycrpo-_nJtK1ZbSxkJW8BEOAzqUYjtuE6P2RCGBIMAQz_qJC4ZW_I0qLifxJFK3Imlhh-KFX6nvlecKfJh8OZchOv0_pK2LfiHFLVk_iFxA3rzB7YbN&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;426&quot; data-original-width=&quot;786&quot; height=&quot;346&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjrdVSE1lEaiUQLd4nRzV6JXY06w1s2JDbFN5SYTxlhSk-d7OX1tqx78yy3i4fmRqhpcoPbl1xkSycrpo-_nJtK1ZbSxkJW8BEOAzqUYjtuE6P2RCGBIMAQz_qJC4ZW_I0qLifxJFK3Imlhh-KFX6nvlecKfJh8OZchOv0_pK2LfiHFLVk_iFxA3rzB7YbN=w640-h346&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;div style=&quot;text-align: center;&quot;&gt;Using GPU-based brushes to improve grass painting smoothness.&lt;/div&gt;&lt;/span&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;h3 class=&quot;&quot; data-end=&quot;44&quot; data-start=&quot;0&quot;&gt;Character Interaction and Grass Response&lt;/h3&gt;&lt;p class=&quot;&quot; data-end=&quot;562&quot; data-start=&quot;346&quot;&gt;
&lt;/p&gt;&lt;p class=&quot;&quot; data-end=&quot;331&quot; data-start=&quot;46&quot;&gt;To bring more life to the scene, I added character interaction functionality. When a character walks through the grass, the blades dynamically bend and react in real time. This deformation is fully handled on the GPU, ensuring that it doesn&#39;t impact the performance of the main thread.&lt;/p&gt;&lt;p class=&quot;&quot; data-end=&quot;331&quot; data-start=&quot;46&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhkZYrAAU8cq42-69hAEj1OtQBf1GDJppk1SG3NXmSozqsLFm-3ryI8s1r8LmHZNp2GAk4wJhxqPqfeqqTzcFDDc265UBZMwTGX_eQ_CMU2AyfZ1Fd6PCUkFOKpviBZFrV4qdyVBrRuHE8Ishdw2Zu-bYX2uwu3IzeIy9sAGiWV4RgEsFsw_CvmkJb2c1aS&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;330&quot; data-original-width=&quot;588&quot; height=&quot;360&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhkZYrAAU8cq42-69hAEj1OtQBf1GDJppk1SG3NXmSozqsLFm-3ryI8s1r8LmHZNp2GAk4wJhxqPqfeqqTzcFDDc265UBZMwTGX_eQ_CMU2AyfZ1Fd6PCUkFOKpviBZFrV4qdyVBrRuHE8Ishdw2Zu-bYX2uwu3IzeIy9sAGiWV4RgEsFsw_CvmkJb2c1aS=w640-h360&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;div style=&quot;text-align: center;&quot;&gt;Character Push Interaction Demonstration&lt;/div&gt;&lt;/span&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;h3 class=&quot;&quot; data-end=&quot;56&quot; data-start=&quot;0&quot;&gt;Conclusion: Bringing Console-Quality Grass to Mobile&lt;/h3&gt;&lt;p class=&quot;&quot; data-end=&quot;396&quot; data-start=&quot;58&quot;&gt;The core goal of this system is to enable developers to achieve near-console-quality grass rendering on mobile platforms. Whether you&#39;re building a stylized meadow, an open-world environment, or looking to deliver smooth and responsive grass interactions, &lt;strong data-end=&quot;338&quot; data-start=&quot;314&quot;&gt;GPUGrassBladePainter&lt;/strong&gt; offers a powerful balance of performance and flexibility.&lt;/p&gt;&lt;p class=&quot;&quot; data-end=&quot;331&quot; data-start=&quot;46&quot;&gt;

&lt;/p&gt;&lt;p class=&quot;&quot; data-end=&quot;591&quot; data-start=&quot;398&quot;&gt;This project is both a tribute to &lt;em data-end=&quot;451&quot; data-start=&quot;432&quot;&gt;Ghost of Tsushima&lt;/em&gt; and a challenge taken on from the perspective of a mobile developer. I hope it inspires you to craft your own immersive natural landscapes.&lt;/p&gt;&lt;p class=&quot;&quot; data-end=&quot;591&quot; data-start=&quot;398&quot;&gt;Demo Video：&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;484&quot; src=&quot;https://www.youtube.com/embed/cc0KXVC6YsE&quot; width=&quot;582&quot; youtube-src-id=&quot;cc0KXVC6YsE&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;p class=&quot;&quot; data-end=&quot;591&quot; data-start=&quot;398&quot;&gt;The Asset link:&lt;br /&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/tools/painting/gpugrassbladepainter-316417&quot;&gt;GPUGrassBladePainter&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/5044820703376207686/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/a-tribute-to-ghost-of-tsushima-creating.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/5044820703376207686'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/5044820703376207686'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/a-tribute-to-ghost-of-tsushima-creating.html' title='A Tribute to Ghost of Tsushima: Creating a High-Quality, Mobile-Friendly GPU Grass System'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEgiRyopR6_a45cC3rKJwGnEcF-8mfBriclxHR0V4PVLwN6bcZH2D9sUAb5dJTZ_SLwBP9-rPRuSYdAO8OGi9H8KRYefzoskGs-XpjR8BNPER67wXeIG-MRoNNjBrw8oqo3dw72mNIUIS3XOoFzB3e6_gBykMpbEqhXsialFdKo9u2xJD5_m1uzf4MLCKTnF=s72-w640-h350-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-601801927633927241</id><published>2025-05-12T14:41:00.000+08:00</published><updated>2025-05-12T14:41:11.369+08:00</updated><title type='text'>DepthEdgeFlow — Geometry-Free Soft Edge FX for Unity URP</title><content type='html'>&lt;p&gt;Simulate shoreline motion and fog boundaries using depth reconstruction and noise, without any vertex displacement, tessellation, or raymarching. Optimized for mobile platforms.&lt;/p&gt;&lt;p&gt;Simulating shoreline transitions in Unity often involves vertex displacement or mesh tessellation —&lt;br /&gt;which can be expensive or even impractical on mobile platforms.&lt;/p&gt;&lt;p&gt;That’s what led me to design a screen-space alternative I now call **DepthEdgeFlow** —&lt;br /&gt;a technique born from my ongoing effort to bring geometry-free solutions to real-time mobile graphics.&lt;/p&gt;&lt;p&gt;Let’s take a look at how it works, starting from a common shoreline scenario.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhNtqkc_vUmjOXf3UwFsfXHbQPFnhL9OndmJd_rJkgeecDwCwUE6hdtNIndyzUtv9VXJdj4_E9EAzl1s969vWAsCzAih7knWTXSxI01kWQAtu4RGsy1Bfhmhxw-AE0LJQhPHDt0ahxAb9fXfmA2-aaqufMytY1P1ISWJ4PdadModsHUaLoKQvUtTu6Ats1H&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;420&quot; data-original-width=&quot;786&quot; height=&quot;342&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhNtqkc_vUmjOXf3UwFsfXHbQPFnhL9OndmJd_rJkgeecDwCwUE6hdtNIndyzUtv9VXJdj4_E9EAzl1s969vWAsCzAih7knWTXSxI01kWQAtu4RGsy1Bfhmhxw-AE0LJQhPHDt0ahxAb9fXfmA2-aaqufMytY1P1ISWJ4PdadModsHUaLoKQvUtTu6Ats1H=w640-h342&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Traditional oceans have a situation where the triangle count is even higher.&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/span&gt;Let us first examine the traditional approach to simulating water surfaces.&lt;/div&gt;&lt;div&gt;This typically involves geometric tessellation to approximate wave motion.&lt;/div&gt;&lt;div&gt;A common implementation displaces vertex positions directly within the Vertex Shader, producing an oscillating surface that mimics wave dynamics.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;However, this technique has inherent limitations:&lt;/div&gt;&lt;div&gt;When the mesh lacks sufficient vertex density, the surface deformation appears overly rigid, resulting in a “block-like” movement that resembles gelatin — visually unrealistic and lacking fine detail.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;Conversely, increasing vertex density to achieve higher visual fidelity&lt;/div&gt;&lt;div&gt;can significantly impact performance on mobile platforms,&lt;/div&gt;&lt;div&gt;often causing frame rate degradation before the water surface even enters view.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;These issues are further amplified in large-scale oceanic environments,&lt;/div&gt;&lt;div&gt;where the computational cost scales poorly with area coverage.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Let’s take a look at the top-down view scenario.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEg7cGywVbjteQywHfeWDs4GySVw5cMT4pEl5Cv_TgJjRsb6htQ-rwOn-qvUec7EATKZToU8fRxcYmrALAH1qibRR0gMgCLkTrPE0T7c30gXn4RFjGyY-56sqibopUctfuRzPTwG1Vt5XUJicf135jkT6xIrd6lvvLkar8JQRWEZkFvl3UoecwFb-hjtsZP-&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;430&quot; data-original-width=&quot;786&quot; height=&quot;350&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEg7cGywVbjteQywHfeWDs4GySVw5cMT4pEl5Cv_TgJjRsb6htQ-rwOn-qvUec7EATKZToU8fRxcYmrALAH1qibRR0gMgCLkTrPE0T7c30gXn4RFjGyY-56sqibopUctfuRzPTwG1Vt5XUJicf135jkT6xIrd6lvvLkar8JQRWEZkFvl3UoecwFb-hjtsZP-=w640-h350&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Traditional sea in a GodView scenario.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;From this perspective, the only area where water surface undulation is actually visible is the narrow zone near the shoreline. The large central area of the water shows little visual variation, and players typically won’t pay much attention to it either.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;So we started asking ourselves:&lt;/div&gt;&lt;div&gt;Is there a way to preserve only the detailed changes near the shore,&lt;/div&gt;&lt;div&gt;while skipping unnecessary simulation for the rest of the water surface?&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Let’s start by watching the video below.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;456&quot; src=&quot;https://www.youtube.com/embed/MsYVogXwPxI&quot; width=&quot;550&quot; youtube-src-id=&quot;MsYVogXwPxI&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;div&gt;You’ll notice subtle vertical motion along the shoreline —&lt;/div&gt;&lt;div&gt;the water appears to ripple as if it’s being displaced by actual geometry.&lt;/div&gt;&lt;div&gt;But here’s the twist: there’s no tessellation or vertex deformation involved.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;If you’ve implemented water shaders before, you might be familiar with a common technique:&lt;/div&gt;&lt;div&gt;reconstructing world position from the depth texture,&lt;/div&gt;&lt;div&gt;then comparing it to the water level to control transparency near the shore.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;In this case, we simply added a bit of noise to the reconstructed height,&lt;/div&gt;&lt;div&gt;introducing minor fluctuations that create the illusion of dynamic movement.&lt;/div&gt;&lt;div&gt;This adds depth and natural detail — with virtually no extra cost.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;What you just saw in the video is the result of this entire process.&lt;/div&gt;&lt;div&gt;I call this approach &lt;b&gt;Depth Edge Flow&lt;/b&gt;.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Next, let’s take a look at the implementation details of this approach.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In &lt;span style=&quot;background-color: #eeeeee;&quot;&gt;DepthEdgeFlow.hlsl&lt;/span&gt;, there&#39;s a function called &lt;span style=&quot;background-color: #eeeeee;&quot;&gt;CalculateSoftEdgeFlowWeight&lt;/span&gt;. We simply need to pass in the &lt;span style=&quot;background-color: #eeeeee;&quot;&gt;NDC coordinates&lt;/span&gt;, &lt;span style=&quot;background-color: #eeeeee;&quot;&gt;positionWS&lt;/span&gt;, &lt;span style=&quot;background-color: #eeeeee;&quot;&gt;DepthParams&lt;/span&gt;, and &lt;span style=&quot;background-color: #eeeeee;&quot;&gt;PerlinNoiseParams&lt;/span&gt; from the Fragment Shader to compute the &lt;span style=&quot;background-color: #eeeeee;&quot;&gt;SoftEdgeFlowWeight&lt;/span&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;half &lt;/span&gt;&lt;span class=&quot;hljs-title.function.invoke&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;CalculateSoftEdgeFlowWeight&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;(float2 positionCS2, float3 positionWS,    &lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    //&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #836c28; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;x&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;:range, &lt;/span&gt;&lt;span class=&quot;hljs-attr&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #836c28; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;y&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;:power, &lt;/span&gt;&lt;span class=&quot;hljs-attr&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #836c28; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;z&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;:offest surfaceHeight&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    float3 depthParams,&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    //&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #836c28; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;x&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;:worldScale, noiseSpeed, rangeRatio&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    float3 perlinNoiseParams)&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;{&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    float2 offestXZ = positionWS.xz * perlinNoiseParams.x + &lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;        _Time.y * perlinNoiseParams.y;&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #aa0d91; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; surfaceHeight = positionWS.y + &lt;/span&gt;&lt;span class=&quot;hljs-title.function.invoke&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;Snoise&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;(offestXZ) * &lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;        perlinNoiseParams.z - depthParams.z;&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #aa0d91; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-title.function.invoke&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;GetSoftEdgeFlowWeight&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;(positionCS2, surfaceHeight, &lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;        depthParams.x, depthParams.y);    &lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;If you have your own noise implementation, you can also directly call &lt;span style=&quot;background-color: #eeeeee;&quot;&gt;GetSoftEdgeFlowWeight&lt;/span&gt; and pass in the computed height value.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;hljs-function&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;half &lt;span class=&quot;hljs-title&quot; style=&quot;box-sizing: inherit; color: #1c00cf;&quot;&gt;GetSoftEdgeFlowWeight&lt;/span&gt;&lt;span class=&quot;hljs-params&quot; style=&quot;box-sizing: inherit; color: #5c2699;&quot;&gt;(float2 positionCS2, &lt;span class=&quot;hljs-type&quot; style=&quot;box-sizing: inherit;&quot;&gt;float&lt;/span&gt; surfaceHeight,&lt;br style=&quot;box-sizing: inherit;&quot; /&gt;    &lt;span class=&quot;hljs-type&quot; style=&quot;box-sizing: inherit;&quot;&gt;float&lt;/span&gt; depthRange, &lt;span class=&quot;hljs-type&quot; style=&quot;box-sizing: inherit;&quot;&gt;float&lt;/span&gt; depthPow)&lt;/span&gt;&lt;br style=&quot;box-sizing: inherit;&quot; /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;{&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    float3 groundPosition = &lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;ComputeWorldSpacePositionFromDepth&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;(&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;        positionCS2);&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-type&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; depthRatio = &lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;saturate&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;((surfaceHeight - groundPosition.y) &lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;        / depthRange);&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    depthRatio = &lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;1.0&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; - &lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;pow&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;1.0&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; - depthRatio, depthPow);&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #aa0d91; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; depthRatio;&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;As for the &lt;span style=&quot;background-color: #eeeeee;&quot;&gt;ComputeWorldSpacePositionFromDepth&lt;/span&gt; part, it calls Unity&#39;s built-in &lt;span style=&quot;background-color: #eeeeee;&quot;&gt;ComputeWorldSpacePosition&lt;/span&gt; function, which already handles the inverse projection from depth to world space. We just need to provide the NDC coordinates, depth value, and &lt;span style=&quot;background-color: #eeeeee;&quot;&gt;UNITY_MATRIX_I_VP&lt;/span&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;hljs-function&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;float3 &lt;span class=&quot;hljs-title&quot; style=&quot;box-sizing: inherit; color: #1c00cf;&quot;&gt;ComputeWorldSpacePositionFromDepth&lt;/span&gt;&lt;span class=&quot;hljs-params&quot; style=&quot;box-sizing: inherit; color: #5c2699;&quot;&gt;(float2 positionCS2)&lt;/span&gt;&lt;br style=&quot;box-sizing: inherit;&quot; /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;{&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-comment&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #007400; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;//float2 destPositionCS = positionCS;&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span class=&quot;hljs-meta&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #643820; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot; style=&quot;box-sizing: inherit; color: #aa0d91;&quot;&gt;if&lt;/span&gt; UNITY_REVERSED_Z&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-type&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; depth = &lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;LoadSceneDepth&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;(positionCS2.xy);&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span class=&quot;hljs-meta&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #643820; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot; style=&quot;box-sizing: inherit; color: #aa0d91;&quot;&gt;else&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-comment&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #007400; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;// Adjust z to match NDC for OpenGL&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-type&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; depth = &lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;lerp&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;(UNITY_NEAR_CLIP_VALUE, &lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;, &lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;LoadSceneDepth&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;(positionCS2.xy));&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span class=&quot;hljs-meta&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #643820; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot; style=&quot;box-sizing: inherit; color: #aa0d91;&quot;&gt;endif&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    float2 positionSS = positionCS2 * _ScreenSize.zw;&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #aa0d91; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;ComputeWorldSpacePosition&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;(positionSS, depth, UNITY_MATRIX_I_VP);&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;As for the actual call to CalculateSoftEdgeFlowWeight, I&#39;ll use the AreaFog Fragment Shader as an example. It&#39;s easy to see that this part simply calculates an alpha transition weight called SoftEdgeFlowWeight.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;hljs-function&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;half4 &lt;span class=&quot;hljs-title&quot; style=&quot;box-sizing: inherit; color: #1c00cf;&quot;&gt;FragmentProgram&lt;/span&gt;&lt;span class=&quot;hljs-params&quot; style=&quot;box-sizing: inherit; color: #5c2699;&quot;&gt;(VertexOutput input)&lt;/span&gt; : SV_Target&lt;br style=&quot;box-sizing: inherit;&quot; /&gt;{&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;   &lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    real3 normalWS = &lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;real3&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;0.0&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;1.0&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;0.0&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;);&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    half softEdgeFlowWeight = &lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;CalculateSoftEdgeFlowWeight&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;(&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;        input.positionCS.xy, input.positionWS,&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;float3&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;(_Depth, _DepthPow, _OffestHeight),&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;float3&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;(_PerlinWorldScale, _PerlinNoiseSpeed, _PerlinRnageRatio));&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    half maskWeight = &lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;SAMPLE_TEXTURE2D&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;(_MaskMap, sampler_MaskMap,&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;        input.baseUV).r;&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span class=&quot;hljs-meta&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #643820; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot; style=&quot;box-sizing: inherit; color: #aa0d91;&quot;&gt;ifdef&lt;/span&gt; VIEW_SOFT_EDGE_FLOW&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #aa0d91; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;half4&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;(softEdgeFlowWeight, &lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;0.0&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;0.0&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;1.0&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;);&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span class=&quot;hljs-meta&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #643820; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot; style=&quot;box-sizing: inherit; color: #aa0d91;&quot;&gt;else&lt;/span&gt;    &lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #aa0d91; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;half4&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;(_Color, softEdgeFlowWeight * maskWeight);&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span class=&quot;hljs-meta&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #643820; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot; style=&quot;box-sizing: inherit; color: #aa0d91;&quot;&gt;endif&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEh_QJfvzHxIspb0EUjRBw6YnO2MBkX7Ed-gpan9bDK6U1ZMMSB_niOQhbAD_5PahDGkoAtLhz_4OyDp5kdkx6svUdKep5ZMZ1UHZQp90OLWMadjt57k4un8o4uNogf-4ZGmDD7pQ0l_G3fTMr9NpgvL7NWSFVsP16pkixXwmxJ0R3_gwP-O0GskfnSOCrr2&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;422&quot; data-original-width=&quot;786&quot; height=&quot;344&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEh_QJfvzHxIspb0EUjRBw6YnO2MBkX7Ed-gpan9bDK6U1ZMMSB_niOQhbAD_5PahDGkoAtLhz_4OyDp5kdkx6svUdKep5ZMZ1UHZQp90OLWMadjt57k4un8o4uNogf-4ZGmDD7pQ0l_G3fTMr9NpgvL7NWSFVsP16pkixXwmxJ0R3_gwP-O0GskfnSOCrr2=w640-h344&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;However, &lt;span style=&quot;background-color: #eeeeee;&quot;&gt;SoftEdgeFlowWeight&lt;/span&gt; doesn’t necessarily have to be used for alpha transitions.&lt;/div&gt;&lt;div&gt;For example, in the case of a water shoreline, what we want is a refraction distortion effect.&lt;/div&gt;&lt;div&gt;In this case, we use the &lt;b&gt;URP’s After Opaque pass&lt;/b&gt;, and apply noise-based distortion based on depth to blend with the background.&lt;/div&gt;&lt;div&gt;The water itself remains an &lt;b&gt;opaque&lt;/b&gt; object.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;half4 &lt;/span&gt;&lt;span class=&quot;hljs-title.function&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;FragmentProgram&lt;/span&gt;&lt;span class=&quot;hljs-params&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;(VertexOutput input)&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; : SV_Target&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;{   &lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    InputData inputData;&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    half3 normalTS;&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    InitializeInputData(input, inputData, normalTS);&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-type&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;half&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-variable&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #3f6e74; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;softEdgeFlowWeight&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-operator&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; CalculateSoftEdgeFlowWeight(&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;        input.positionCS.xy, input.positionWS,&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;        float3(_Depth, _DepthPow, _OffestHeight),&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;        float3(_PerlinWorldScale, _PerlinNoiseSpeed, _PerlinRnageRatio));&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-type&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;half3&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-variable&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #3f6e74; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;diffuse&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-operator&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; lerp(_ShallowColor, _DeepColor, softEdgeFlowWeight);&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-type&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;half4&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-variable&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #3f6e74; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;specularGloss&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-operator&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; half4(&lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;1.0&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;1.0&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;1.0&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;1.0&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;);&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-type&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;half&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-variable&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #3f6e74; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;smoothness&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-operator&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;0.5&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;;&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-type&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;half3&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-variable&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #3f6e74; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;emission&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-operator&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; half3(&lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;);&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-type&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;half&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-variable&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #3f6e74; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;alpha&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-operator&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;1.0&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;;&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-type&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;half3&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-variable&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #3f6e74; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;lightResult&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-operator&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; UniversalFragmentBlinnPhong(inputData, diffuse, &lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;        specularGloss, smoothness, emission, &lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;1.0&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;, normalTS).rgb;&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-comment&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #007400; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;//Handling refraction blending&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-type&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;half2&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-variable&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #3f6e74; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;distortionScreenUV&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-operator&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; input.positionSS.xy / input.positionSS.w + &lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;        _RefractionDistortion * softEdgeFlowWeight * inputData.normalWS.xz;&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-type&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #5c2699; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;half3&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-variable&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #3f6e74; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;sceneColor&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;hljs-operator&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; SampleSceneColor(distortionScreenUV);&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #aa0d91; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt; half4(lerp(sceneColor, lightResult, softEdgeFlowWeight), &lt;/span&gt;&lt;span class=&quot;hljs-number&quot; style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #1c00cf; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;1.0&lt;/span&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;);&lt;/span&gt;&lt;br style=&quot;background-color: #f9f9f9; box-sizing: inherit; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot; /&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #242424; font-family: source-code-pro, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 14px; letter-spacing: -0.308px; white-space: pre;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgndITlJG0HmAjYQOXCVyNe7D5m-bPmZGZF4KnnQ9YrlXCFimmyIPoFTA_bKfGHr2Lby12n2IYgdGzYucDfk4VY4CpMmkVXof7kp_lnRz3-u0imiAWfl5duijXXhK9xopsx0vBZbj4wf6eiNVUonVmjjDi1fLB3pK9QJ8udNkewvul6kgIaE_rmPONHUCaB&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;419&quot; data-original-width=&quot;786&quot; height=&quot;342&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgndITlJG0HmAjYQOXCVyNe7D5m-bPmZGZF4KnnQ9YrlXCFimmyIPoFTA_bKfGHr2Lby12n2IYgdGzYucDfk4VY4CpMmkVXof7kp_lnRz3-u0imiAWfl5duijXXhK9xopsx0vBZbj4wf6eiNVUonVmjjDi1fLB3pK9QJ8udNkewvul6kgIaE_rmPONHUCaB=w640-h342&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The related video is as follows:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;458&quot; src=&quot;https://www.youtube.com/embed/_a9CZXP_7Bs&quot; width=&quot;552&quot; youtube-src-id=&quot;_a9CZXP_7Bs&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;div&gt;DepthEdgeFlow is simple, efficient, and flexible —&lt;/div&gt;&lt;div&gt;ideal for situations where visual fidelity matters but resources are limited.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;If you find ways to expand or adapt it for your own use case,&lt;/div&gt;&lt;div&gt;I’d love to see what you come up with.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: white; color: #242424; font-family: source-serif-pro, Georgia, Cambria, &amp;quot;Times New Roman&amp;quot;, Times, serif; font-size: 20px; letter-spacing: -0.06px;&quot;&gt;### 🔗 GitHub Open Source&lt;br /&gt;&lt;/span&gt;You can try DepthEdgeFlow yourself — the shader and sample scenes are open source:&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: white; color: #242424; font-family: source-serif-pro, Georgia, Cambria, &amp;quot;Times New Roman&amp;quot;, Times, serif; font-size: 20px; letter-spacing: -0.06px;&quot;&gt;&lt;a href=&quot;https://github.com/AkilarLiao/DepthEdgeFlow&quot;&gt;AkilarLiao/DepthEdgeFlow&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Includes demo scenes, shader code, and URP integration steps.&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/tools/painting/fakevolumefogpainter-274645&quot;&gt;FakeVolumeFogPainter&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/tools/painting/fastriverpainter-272846&quot;&gt;FastRiverPainter&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/vfx/shaders/fftocean-288846&quot;&gt;FFTOcean&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/vfx/shaders/infinitystylizedwater-282596&quot;&gt;InfinityStylizedWater&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/vfx/shaders/inifityquadocean-258528&quot;&gt;InifityQuadOcean&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/601801927633927241/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/depthedgeflow-geometry-free-soft-edge.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/601801927633927241'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/601801927633927241'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/depthedgeflow-geometry-free-soft-edge.html' title='DepthEdgeFlow — Geometry-Free Soft Edge FX for Unity URP'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEhNtqkc_vUmjOXf3UwFsfXHbQPFnhL9OndmJd_rJkgeecDwCwUE6hdtNIndyzUtv9VXJdj4_E9EAzl1s969vWAsCzAih7knWTXSxI01kWQAtu4RGsy1Bfhmhxw-AE0LJQhPHDt0ahxAb9fXfmA2-aaqufMytY1P1ISWJ4PdadModsHUaLoKQvUtTu6Ats1H=s72-w640-h342-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-2489622106160649440</id><published>2025-05-12T14:17:00.005+08:00</published><updated>2025-05-12T14:17:52.128+08:00</updated><title type='text'>Optimizing Performance through InstanceCollector</title><content type='html'>&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjewSh40LX1j4-Vj6dH3m7rA3F0H2JiMKPcAVkE_5sdVcDu3RbZia6zDysqqiszxdopb8r1596HDiH1kB6fjVn0yeGwauk12-nhlsqAoIuSOE0vQywcVgyk13YQqlElLMznADDDjxm13HalKkRyOQdfwmZBkFaKx1RnhsvLp7Gjhr_kDz4UXbN0nTVUSiED&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;400&quot; data-original-width=&quot;786&quot; height=&quot;326&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjewSh40LX1j4-Vj6dH3m7rA3F0H2JiMKPcAVkE_5sdVcDu3RbZia6zDysqqiszxdopb8r1596HDiH1kB6fjVn0yeGwauk12-nhlsqAoIuSOE0vQywcVgyk13YQqlElLMznADDDjxm13HalKkRyOQdfwmZBkFaKx1RnhsvLp7Gjhr_kDz4UXbN0nTVUSiED=w640-h326&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;As SRP Batch technology has advanced, Unity’s automatic instancing feature is often disabled, leading to performance degradation when rendering a large number of objects.&lt;span&gt;&lt;/span&gt;&lt;/p&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhx0BoAftai7XpbR4P48ctsrzmLpACDQ_FCKEu8RB-bdq1vvxZ_IQicI8awX_yUEGa3SSOQBSmGHq_TpaUTiJB6k4lwcaFGod4DT8XBjVQUN_P4eb_3FDP4vEQi-2Dgnyux_83NYtlRMlWvmJSVO4WVhmKOkD3w9CgevzItS05Jy7DnYZ78SHY0nGzFW43M&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;288&quot; data-original-width=&quot;512&quot; height=&quot;360&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhx0BoAftai7XpbR4P48ctsrzmLpACDQ_FCKEu8RB-bdq1vvxZ_IQicI8awX_yUEGa3SSOQBSmGHq_TpaUTiJB6k4lwcaFGod4DT8XBjVQUN_P4eb_3FDP4vEQi-2Dgnyux_83NYtlRMlWvmJSVO4WVhmKOkD3w9CgevzItS05Jy7DnYZ78SHY0nGzFW43M=w640-h360&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;While manual instancing (such as using Graphics.DrawMeshInstanced) offers better performance, it is typically limited to rendering large amounts of single-type objects and lacks support for full-scene optimization.&lt;p&gt;&lt;/p&gt;&lt;p&gt;since Graphics.DrawMeshInstanced requires a pre-passed matrix array, a typical approach is to divide the scene into a grid, with each cell corresponding to a matrix array. Culling is then performed based on the bounds of each cell, or by using QuadTree or OctTree structures. However, this method does not perform per-instance culling, resulting in the rendering of more objects than with SRP batching, which can lead to poor performance.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiZbabrQF0fMSnm2qssG_7IBawza-uE94I-dF0GUS9A1TUl1sDKVz4wQZ4mgk_lPTKokxdJ6hD0YSqz5pewHHSgcv85oL3-T_LaD64E_iRkRzdSHkkHCWRnlpj6xYT3TxfZxCdbCHRK8KlzgvjPMsECcsN7P9ElDXapaPrpGbaDnwA05857CL_96tpi9Kin&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;494&quot; data-original-width=&quot;786&quot; height=&quot;402&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiZbabrQF0fMSnm2qssG_7IBawza-uE94I-dF0GUS9A1TUl1sDKVz4wQZ4mgk_lPTKokxdJ6hD0YSqz5pewHHSgcv85oL3-T_LaD64E_iRkRzdSHkkHCWRnlpj6xYT3TxfZxCdbCHRK8KlzgvjPMsECcsN7P9ElDXapaPrpGbaDnwA05857CL_96tpi9Kin=w640-h402&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;There is no per-instance culling.&lt;p&gt;&lt;/p&gt;&lt;p&gt;In addition, since the grid cutting is predetermined, if the camera zooms in and out, it can lead to an unsuitable size for the grid, resulting in an excessive number of draw calls when zooming out. Unless batching can be performed, this approach also prevents the number of arrays from being predetermined, and such processing can severely impact performance.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEi-SozRk5u7k0C_VTlFAyYdZNzL0cYWBrDcKMBguz6MGR_B_pvUWvGXjepBZws7ZoGve_4JJVjTWFJHVniUtwQksL51Y3HQAl_Phf8Zv_N-WXMWm9tZfER3D_JjNKich43UnSWJevtICe-a3CJGroF_Aw92iH9ARxV9nzD7ZblKV4Qqg2cXIyAQj9IGNOB-&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;525&quot; data-original-width=&quot;786&quot; height=&quot;428&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEi-SozRk5u7k0C_VTlFAyYdZNzL0cYWBrDcKMBguz6MGR_B_pvUWvGXjepBZws7ZoGve_4JJVjTWFJHVniUtwQksL51Y3HQAl_Phf8Zv_N-WXMWm9tZfER3D_JjNKich43UnSWJevtICe-a3CJGroF_Aw92iH9ARxV9nzD7ZblKV4Qqg2cXIyAQj9IGNOB-=w640-h428&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;There are no batch cell draw calls.&lt;p&gt;&lt;/p&gt;&lt;p&gt;The unique advantage of InstanceCollector is its ability to optimize GPU instancing across the entire scene. It not only effectively collects instance objects but also leverages Unity’s Job System for precise culling calculations, ensuring optimal rendering performance. If the culling is not accurate enough, the performance might even be worse than SRP Batch, highlighting the core strength of InstanceCollector’s technology.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiS9_C4_Hq6yi_K7QLLVxuv3JlBcediuq1LnIVguvzcGeoPQ2lVQG8dl-9SN_gjXy4QKRGP0C3n1rIpR3ymzF2uT-kIRW_6Umwn6Xy710OU_5bQXpvzwktU_HGnY26Y5WyDXfwzS3GtaWDOn6RHfBGdo22y7FmxZ_sLKaqNGimFfFw2Iw8eIvcqdZVvbBKp&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;537&quot; data-original-width=&quot;786&quot; height=&quot;438&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiS9_C4_Hq6yi_K7QLLVxuv3JlBcediuq1LnIVguvzcGeoPQ2lVQG8dl-9SN_gjXy4QKRGP0C3n1rIpR3ymzF2uT-kIRW_6Umwn6Xy710OU_5bQXpvzwktU_HGnY26Y5WyDXfwzS3GtaWDOn6RHfBGdo22y7FmxZ_sLKaqNGimFfFw2Iw8eIvcqdZVvbBKp=w640-h438&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Per-instance culling allows for one draw call in the batch.&lt;p&gt;&lt;/p&gt;&lt;p&gt;Generally speaking, compared to DrawMeshInstanced, DrawMeshInstancedIndirect offers better performance because it gathers matrix data in the compute shader. However, the biggest issue arises when the object count is zero. Since the CPU cannot determine the number of objects, it still calls the rendering API, and it seems like additional data is being sent to the GPU. When there are many different types of objects, this may lead to a performance drop. Therefore, to handle instance collections for multiple object types, we choose to use DrawMeshInstanced along with culling via the job system.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEi4dTPwGMv4WUj0r74IteVmW4_nKE5wEU8vN2FhzOzwK96K4k6H6fhrMwEK9NlkVlpL5QCxihxj6T-0N8NDe7BLmEVyUzYkMUyEZw329FvOQr5dcjMsnuZ-ilCafWzhgT8vHw1VDhj8gPwr8QqIfwUIgROpb4Q1iSwdl8xvGzDRrdTft2SqLsheLDgeSWbA&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;442&quot; data-original-width=&quot;786&quot; height=&quot;360&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEi4dTPwGMv4WUj0r74IteVmW4_nKE5wEU8vN2FhzOzwK96K4k6H6fhrMwEK9NlkVlpL5QCxihxj6T-0N8NDe7BLmEVyUzYkMUyEZw329FvOQr5dcjMsnuZ-ilCafWzhgT8vHw1VDhj8gPwr8QqIfwUIgROpb4Q1iSwdl8xvGzDRrdTft2SqLsheLDgeSWbA=w640-h360&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Example of Using DrawMeshInstancedIndirect for a Single Type of Object.&lt;p&gt;&lt;/p&gt;&lt;p&gt;Instance Collector uses the Job System (without occupying the main thread) to dynamically collect instances and supports multiple types of objects. Below is the related feature list.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Precise per-instance culling.&lt;/li&gt;&lt;li&gt;Dynamic instance collection using the Job System, without occupying the main thread’s resources.&lt;/li&gt;&lt;li&gt;Supports multiple types of instance objects, and even full-scene GPU instancing.&lt;/li&gt;&lt;li&gt;Provides a matrix array output tool to speed up scene loading and reduce memory usage.&lt;/li&gt;&lt;li&gt;Without using SSBO, it has better cross-platform compatibility.&lt;/li&gt;&lt;li&gt;For hardware that doesn’t support GPU instancing, the rendering process will fall back to using.&lt;/li&gt;&lt;li&gt;Simple instance object setup process (just add the Instancer script and enable “Enable GPU Instancing” in the material).&lt;/li&gt;&lt;li&gt;Compatible with SRP Batcher.&lt;/li&gt;&lt;li&gt;Customizable maximum visible object count for each type, adjustable based on project memory and performance requirements.&lt;/li&gt;&lt;li&gt;Support URP and Built-in render pipeline.&lt;/li&gt;&lt;/ol&gt;Relevant videos are as follows:&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;494&quot; src=&quot;https://www.youtube.com/embed/B_AJUXFOpPw&quot; width=&quot;595&quot; youtube-src-id=&quot;B_AJUXFOpPw&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;You can find it here:&lt;br /&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/tools/utilities/instancecollector-296669&quot;&gt;InstanceCollector&lt;/a&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/2489622106160649440/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/optimizing-performance-through.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/2489622106160649440'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/2489622106160649440'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/optimizing-performance-through.html' title='Optimizing Performance through InstanceCollector'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEjewSh40LX1j4-Vj6dH3m7rA3F0H2JiMKPcAVkE_5sdVcDu3RbZia6zDysqqiszxdopb8r1596HDiH1kB6fjVn0yeGwauk12-nhlsqAoIuSOE0vQywcVgyk13YQqlElLMznADDDjxm13HalKkRyOQdfwmZBkFaKx1RnhsvLp7Gjhr_kDz4UXbN0nTVUSiED=s72-w640-h326-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-3757252024708920741</id><published>2025-05-12T14:08:00.005+08:00</published><updated>2025-05-12T14:08:59.723+08:00</updated><title type='text'>MobileSSAO adds MRTTerrainLit shader</title><content type='html'>&lt;p&gt;It is well known that Unity’s Terrain has a high number of polygons, and rendering shadows requires an additional pass. On top of that, running the official SSAO post-processing effect, especially using the DepthNormal mode, requires a total of three rendering passes, which significantly impacts performance.&lt;/p&gt;&lt;p&gt;MobileSSAO recently added the MRTTerrainLit shader feature, which uses the MRT processing workflow to allow Terrain to render only once (excluding shadow rendering) while achieving SSAO effects with the DepthNormal mode. Below are the relevant reference images.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEigW42qawPiDqCMe2kAs5qclln-BekohTeD2IQaH8PM8RXRaWP5lbTd86SEsJO-bW2uUUUYDe6GSJft2z9VoNDLlV6RL84UWx6VtvY_I3Np4ZJPcHADVtZyFV4hT7l78mTFujCzW6bWfPKJ0NwpmDW0xsQjkaoDTe2iDM2fsuKzlxih-LEe36sq7nxLaglJ&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;503&quot; data-original-width=&quot;786&quot; height=&quot;410&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEigW42qawPiDqCMe2kAs5qclln-BekohTeD2IQaH8PM8RXRaWP5lbTd86SEsJO-bW2uUUUYDe6GSJft2z9VoNDLlV6RL84UWx6VtvY_I3Np4ZJPcHADVtZyFV4hT7l78mTFujCzW6bWfPKJ0NwpmDW0xsQjkaoDTe2iDM2fsuKzlxih-LEe36sq7nxLaglJ=w640-h410&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;span&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/span&gt;Unity terrain with SSAO applied.&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEg2_nqi6uiZ8hW3dOIkKZuV9lj6SV4MGmVllgl7tdKPPtY8vJ96SS2qzp8SN-pI6-nqaMiRqieYZOlH9IR9cZD4zTZb_LTyEQMfWY9Ay1UQQZGc_fBQnc0oKm4oZHn-ZTMzpSZhlRFXeaio_D1N81eEUw-RZElXK46gQLa0_k5j2-Yojwu6l696nPfz25nL&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;503&quot; data-original-width=&quot;786&quot; height=&quot;410&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEg2_nqi6uiZ8hW3dOIkKZuV9lj6SV4MGmVllgl7tdKPPtY8vJ96SS2qzp8SN-pI6-nqaMiRqieYZOlH9IR9cZD4zTZb_LTyEQMfWY9Ay1UQQZGc_fBQnc0oKm4oZHn-ZTMzpSZhlRFXeaio_D1N81eEUw-RZElXK46gQLa0_k5j2-Yojwu6l696nPfz25nL=w640-h410&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Unity terrain without SSAO applied.&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEg4DEv1vvN7f6AEQDEgUXsDFCAUX7GchBLHFPPuHbeUmxOp84H4IxdRVB6W9HgX9OZb-7jifPFKAl734Nuvdkj5f-VGRLk4qsNfuSQyPoK-YQ_NvzzCB3cSbpKqDosE_wdFOwV0YQRr5UiHSjIQb5BgW5pw1KQXzgjXZZBclxMVa2hHCIhbFOy5yW4voj8V&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;683&quot; data-original-width=&quot;786&quot; height=&quot;557&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEg4DEv1vvN7f6AEQDEgUXsDFCAUX7GchBLHFPPuHbeUmxOp84H4IxdRVB6W9HgX9OZb-7jifPFKAl734Nuvdkj5f-VGRLk4qsNfuSQyPoK-YQ_NvzzCB3cSbpKqDosE_wdFOwV0YQRr5UiHSjIQb5BgW5pw1KQXzgjXZZBclxMVa2hHCIhbFOy5yW4voj8V=w640-h557&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Unity terrain applies MobileSSAO’s triangle count.&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiCdDNWPraKJU_7ZbidGbP8HH5zTV2DhvZ3ArAQcXQT608pyQqXnQa6n2QhVAU60zYoxqHp-r-1fZn4YVdtGuzmDOpJ07lgYnlFi8T3md235rST37u6o6NonGMTG9DBLnelG3PS1coyyXbZkjcaV4LQ8tYUilA-NP9pGblbm6w8KmZ_ET7dTjx_nSOLLZ04&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;517&quot; data-original-width=&quot;786&quot; height=&quot;420&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiCdDNWPraKJU_7ZbidGbP8HH5zTV2DhvZ3ArAQcXQT608pyQqXnQa6n2QhVAU60zYoxqHp-r-1fZn4YVdtGuzmDOpJ07lgYnlFi8T3md235rST37u6o6NonGMTG9DBLnelG3PS1coyyXbZkjcaV4LQ8tYUilA-NP9pGblbm6w8KmZ_ET7dTjx_nSOLLZ04=w640-h420&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Relevant videos are as follows:&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;546&quot; src=&quot;https://www.youtube.com/embed/DNpyqhLVqWQ&quot; width=&quot;658&quot; youtube-src-id=&quot;DNpyqhLVqWQ&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;You can find it here:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/vfx/shaders/fullscreen-camera-effects/mobilessao-286085&quot;&gt;MobileSSAO&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/3757252024708920741/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/mobilessao-adds-mrtterrainlit-shader.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/3757252024708920741'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/3757252024708920741'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/mobilessao-adds-mrtterrainlit-shader.html' title='MobileSSAO adds MRTTerrainLit shader'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEigW42qawPiDqCMe2kAs5qclln-BekohTeD2IQaH8PM8RXRaWP5lbTd86SEsJO-bW2uUUUYDe6GSJft2z9VoNDLlV6RL84UWx6VtvY_I3Np4ZJPcHADVtZyFV4hT7l78mTFujCzW6bWfPKJ0NwpmDW0xsQjkaoDTe2iDM2fsuKzlxih-LEe36sq7nxLaglJ=s72-w640-h410-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-5363298939989206826</id><published>2025-05-12T14:01:00.002+08:00</published><updated>2025-05-12T14:01:24.713+08:00</updated><title type='text'>An Overview of FFTOcean</title><content type='html'>&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgt6tX0E_fvWR7acDhOohRd1sOCgazSIW9Gui5qr7ZMf7tfAptVnBkyaBSIxM_UdeeKqeFHmEmqp59yPYettLSQbOjtCBs_8vbtnJ2uYKHWYYKhIa3VpIFpN-W20ehw6gjqB8Ezl2fiNyOonI9C8W1HXKsRFIW4EE4W4S1dN1h3X5lHeib0KzBVIfC2_e2v&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;443&quot; data-original-width=&quot;786&quot; height=&quot;360&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgt6tX0E_fvWR7acDhOohRd1sOCgazSIW9Gui5qr7ZMf7tfAptVnBkyaBSIxM_UdeeKqeFHmEmqp59yPYettLSQbOjtCBs_8vbtnJ2uYKHWYYKhIa3VpIFpN-W20ehw6gjqB8Ezl2fiNyOonI9C8W1HXKsRFIW4EE4W4S1dN1h3X5lHeib0KzBVIfC2_e2v=w640-h360&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;b&gt;FFTOcean&lt;/b&gt; is one of the most complex challenges I’ve faced in my game development career. This project involves multiple technical aspects, including ocean simulation, Fourier transform algorithms, projected grids, rendering pipeline planning, and water lighting simulation. In particular, improper handling of the MRT texture rendering and RenderPass operation flow can lead to performance issues. The main goal of this project is to ensure smooth operation on mobile platforms.&lt;span&gt;&lt;/span&gt;&lt;/p&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEihJnlZVSYmuE_HPXf-FMjrWqPAcuCo7uzgGbACOzvCn5Ep7HtonvLblt6dmnVVU2E584KTD6rMjD8k2Dv36iJbsBi0p9jRgP-0VgdjmiO7j904j7mQpC1BTpmop8LsIS1gymvaO-hbrcC1ygZLv0C2NgYW6cI52KkxrhqdFOQOMToWZ44Z_35bcbJ_p_tj&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;490&quot; data-original-width=&quot;786&quot; height=&quot;398&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEihJnlZVSYmuE_HPXf-FMjrWqPAcuCo7uzgGbACOzvCn5Ep7HtonvLblt6dmnVVU2E584KTD6rMjD8k2Dv36iJbsBi0p9jRgP-0VgdjmiO7j904j7mQpC1BTpmop8LsIS1gymvaO-hbrcC1ygZLv0C2NgYW6cI52KkxrhqdFOQOMToWZ44Z_35bcbJ_p_tj=w640-h398&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;From left to right, and top to bottom, the first four images are displacement maps, followed by two slope maps, and the final one is a Jacobian foam map. The entire process uses Fourier transform to calculate the RenderTexture results.&lt;p&gt;&lt;/p&gt;&lt;p&gt;To support ScreenSpaceDecals, it’s necessary to generate depth textures for the ocean. Considering that this project will run on platforms supporting OpenGLES 3.0 and above, we adopted the CopyDepthPass approach. However, since this method causes issues when running in the Editor (which uses its own independent RenderTexture), we implemented AppendDepthPass to address the problem.&lt;/p&gt;&lt;p&gt;Another noteworthy aspect is the integration of planar reflection textures in the distance. The rendering result transitions to the reflection based on the camera’s distance, achieving a seamless blend with the sky at far distances and avoiding visible edge lines.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiGeKF-_7gTUcf5FBADebAJdomjAj7CVt7lAJyVZlsxLokCwMTCaEC-dRfcpQxxAYgyd7dTh4kLs6DBULiACzsD2FXsHm2Uw8L0WsDdLIBq1O6QQ6oEUN7XTDK0iAxz4lbdZUUMAfKYjalwxp9L3_J5RXC6UCnuwcbBs6uEb_xs1H9_M1xgMGfWBDupG9CI&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;472&quot; data-original-width=&quot;786&quot; height=&quot;384&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiGeKF-_7gTUcf5FBADebAJdomjAj7CVt7lAJyVZlsxLokCwMTCaEC-dRfcpQxxAYgyd7dTh4kLs6DBULiACzsD2FXsHm2Uw8L0WsDdLIBq1O6QQ6oEUN7XTDK0iAxz4lbdZUUMAfKYjalwxp9L3_J5RXC6UCnuwcbBs6uEb_xs1H9_M1xgMGfWBDupG9CI=w640-h384&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;In the image above, there is no visible edge line where the ocean surface meets the skybox.&lt;p&gt;&lt;/p&gt;&lt;p&gt;As for the lighting effects, standard BRDF is used with no particular differences. However, it’s worth mentioning that sub-surface scattering is simulated here, resulting in a gem-like green tint visible on the water surface.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEj1m59lrHXwVohnwriuHdyhoGC5b_H9V1Y7-NCDHSPOSg4eBiG7hDffZUKbwkHCes5QI838pdkMV1Jt9zFfX7vjLZsGrKxAA8-x7HKrnXHGcDUxo1dk1Zp6won0x_ej_9ya-uEy-5cVa1WW4Z42xOdwaRyjiFhYKhSnZ7dQUjddu3kDOZOkT8W_FlL62Jh9&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;468&quot; data-original-width=&quot;786&quot; height=&quot;382&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEj1m59lrHXwVohnwriuHdyhoGC5b_H9V1Y7-NCDHSPOSg4eBiG7hDffZUKbwkHCes5QI838pdkMV1Jt9zFfX7vjLZsGrKxAA8-x7HKrnXHGcDUxo1dk1Zp6won0x_ej_9ya-uEy-5cVa1WW4Z42xOdwaRyjiFhYKhSnZ7dQUjddu3kDOZOkT8W_FlL62Jh9=w640-h382&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Additionally, a special mode called Infinity Quad Mode is provided. This mode uses only four vertices to render the entire infinite water body. It can be beneficial for hardware sensitive to vertex count or scenes with excessive triangle numbers, potentially improving performance.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEidBDFyd75DeFBd1Zzyrma1O7BfU2DHgl9Jwc3WqsKFx9E4abhSBot1v8qyP1PsNsaX6UPSVcEUkxRZSB0bBh6J1NHchGgPWeH3Cni8_frUN14aE3OKT4g67MYErZVVht85yxEhZf9OWGATp9uDKfmYUEOLkRT-0aoznYMym47HQePMIUWz97Cjy5puCASk&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;510&quot; data-original-width=&quot;786&quot; height=&quot;416&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEidBDFyd75DeFBd1Zzyrma1O7BfU2DHgl9Jwc3WqsKFx9E4abhSBot1v8qyP1PsNsaX6UPSVcEUkxRZSB0bBh6J1NHchGgPWeH3Cni8_frUN14aE3OKT4g67MYErZVVht85yxEhZf9OWGATp9uDKfmYUEOLkRT-0aoznYMym47HQePMIUWz97Cjy5puCASk=w640-h416&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Throughout the implementation process, Ceto and CRest provided a wealth of reference material. You can find their GitHub links below:&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/Scrawk/Ceto&quot;&gt;Scrawk/Ceto&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/wave-harmonic/crest&quot;&gt;wave-harmonic/crest&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Relevant videos are as follows:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;452&quot; src=&quot;https://www.youtube.com/embed/DWAhEnzlO8U&quot; width=&quot;545&quot; youtube-src-id=&quot;DWAhEnzlO8U&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;Actual performance on mobile devices:&lt;div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;473&quot; src=&quot;https://www.youtube.com/embed/5aEkRxTb1VA&quot; width=&quot;568&quot; youtube-src-id=&quot;5aEkRxTb1VA&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;div&gt;You can find it here:&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/vfx/shaders/fftocean-288846&quot;&gt;FFTOcean&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/5363298939989206826/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/an-overview-of-fftocean.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/5363298939989206826'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/5363298939989206826'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/an-overview-of-fftocean.html' title='An Overview of FFTOcean'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEgt6tX0E_fvWR7acDhOohRd1sOCgazSIW9Gui5qr7ZMf7tfAptVnBkyaBSIxM_UdeeKqeFHmEmqp59yPYettLSQbOjtCBs_8vbtnJ2uYKHWYYKhIa3VpIFpN-W20ehw6gjqB8Ezl2fiNyOonI9C8W1HXKsRFIW4EE4W4S1dN1h3X5lHeib0KzBVIfC2_e2v=s72-w640-h360-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-2586630126962725336</id><published>2025-05-12T13:46:00.004+08:00</published><updated>2025-05-12T13:47:06.751+08:00</updated><title type='text'>An Overview of GodViewFakeDOF</title><content type='html'>&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhfk_MLF52JjKkUCX6GH95lLdtdol5pBMcSsXEkJO9LtMMvY1m69tkOXymJPfBCkw_uYXr6auNVvN_sYiQcA-AQUBuU93mShd4ybpTHZ4jymyJTh9trxgADBn1J1Lv0G92Kq2RqLvtyDt7hN0BTq7nRl2Vr2qdzaE-eVAtMpEv51_Pg_VSZkkvjKJpcoAWk&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;784&quot; data-original-width=&quot;445&quot; height=&quot;640&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhfk_MLF52JjKkUCX6GH95lLdtdol5pBMcSsXEkJO9LtMMvY1m69tkOXymJPfBCkw_uYXr6auNVvN_sYiQcA-AQUBuU93mShd4ybpTHZ4jymyJTh9trxgADBn1J1Lv0G92Kq2RqLvtyDt7hN0BTq7nRl2Vr2qdzaE-eVAtMpEv51_Pg_VSZkkvjKJpcoAWk=w363-h640&quot; width=&quot;363&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;The concept for GodViewFakeDOF originated from the challenge I faced while developing a war strategy game, where I needed to implement a fog effect based on height considerations. However, the project requirements specified that fog should not affect any content in the center of the screen, regardless of altitude. To meet this requirement, I used a mask texture to filter out the fog effect from the center of the screen. Later, when trying to apply DOF effects for a GodView perspective, I found that the top-down view resulted in overly uniform depth values, making the DOF effect less pronounced. Building on my previous experience with height-based fog effects, I developed GodViewFakeDOF specifically for this type of perspective.&lt;span&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Traditional DOF techniques are based on depth data for processing. However, alpha-blended objects lack depth information, which makes them challenging to handle. Even if depth is output using alpha test depth pass, edge transition issues still arise, especially with alpha-blended effects, which are completely unmanageable.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhwSY5Qc8KvzgA90-froLTKx1hyWdo4WugaQsvW1X6yJr6EKNtDWvNweV8CyEnIcqNGtY6jJKvxt1CKFwWDiKfzXC_FGbOZDxlwTrWoX1yexOGVe7pyzgtzmUBwEZLVlOTQvkj1NRm2ZSihcDJXX2CoVq1fPb2qMJGnW5ZclCpElhGv1LStrPcwpf1o6yyw&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;388&quot; data-original-width=&quot;786&quot; height=&quot;316&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhwSY5Qc8KvzgA90-froLTKx1hyWdo4WugaQsvW1X6yJr6EKNtDWvNweV8CyEnIcqNGtY6jJKvxt1CKFwWDiKfzXC_FGbOZDxlwTrWoX1yexOGVe7pyzgtzmUBwEZLVlOTQvkj1NRm2ZSihcDJXX2CoVq1fPb2qMJGnW5ZclCpElhGv1LStrPcwpf1o6yyw=w640-h316&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgZh-IwEBbWygwKPawcDqhukoWyUdSDgBCKy99WqAogwuBO7Asl_H4EA6jyOCpyLh7NzC3NuOiwToCp5yNFqgAI0MjwaEdY6oeEmjxpNyaWVfcD2WSEPsTb_9mosHZLOEns_YoWe-q8oDinkPV-id2LjurpbYEQzoG1WLkYg_YNucYpPjUx4INcdien3ZtQ&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;414&quot; data-original-width=&quot;786&quot; height=&quot;338&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgZh-IwEBbWygwKPawcDqhukoWyUdSDgBCKy99WqAogwuBO7Asl_H4EA6jyOCpyLh7NzC3NuOiwToCp5yNFqgAI0MjwaEdY6oeEmjxpNyaWVfcD2WSEPsTb_9mosHZLOEns_YoWe-q8oDinkPV-id2LjurpbYEQzoG1WLkYg_YNucYpPjUx4INcdien3ZtQ=w640-h338&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Let me explain the entire processing procedure:&lt;/div&gt;&lt;div&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Create a ScreenSpaceBlurPass and run it before performing post-processing.&lt;/li&gt;&lt;li&gt;Write the related Dual-Kaswase Blur Shader.&lt;/li&gt;&lt;li&gt;Use UV (0.5, 0.0) as the center point to calculate the blur ratio based on screen distance.&lt;/li&gt;&lt;li&gt;If a Mask texture is used, apply the Mask data directly.&lt;/li&gt;&lt;/ol&gt;Since depth data is not used and only screen UV distance data is applied, it supports alpha-blended objects.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhW0tl6-5Nd3iP-iVUmwacf3RG8O4P6zIaTivvjt6eOHMIB1uQPfiCTgOvcWxZewwaSqHD1bIogJseJvtyQnDbd8ypOB3CFjYRkgs-aS9stH8-E_srdCRFU1q0jZ-Pv9xGeobAeDFkHv_5-hsOCkvDLFBjmOy8qlbbasmpcexLs5oyfZO8hF8o_7jJSwH9J&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;799&quot; data-original-width=&quot;632&quot; height=&quot;640&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhW0tl6-5Nd3iP-iVUmwacf3RG8O4P6zIaTivvjt6eOHMIB1uQPfiCTgOvcWxZewwaSqHD1bIogJseJvtyQnDbd8ypOB3CFjYRkgs-aS9stH8-E_srdCRFU1q0jZ-Pv9xGeobAeDFkHv_5-hsOCkvDLFBjmOy8qlbbasmpcexLs5oyfZO8hF8o_7jJSwH9J=w507-h640&quot; width=&quot;507&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The related videos are as follows:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;454&quot; src=&quot;https://www.youtube.com/embed/ifkgpDcZuws&quot; width=&quot;545&quot; youtube-src-id=&quot;ifkgpDcZuws&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;You can find it here:&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/vfx/shaders/fullscreen-camera-effects/godviewfakedof-289580&quot;&gt;GodViewFakeDOF&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/2586630126962725336/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/an-overview-of-godviewfakedof.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/2586630126962725336'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/2586630126962725336'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/an-overview-of-godviewfakedof.html' title='An Overview of GodViewFakeDOF'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEhfk_MLF52JjKkUCX6GH95lLdtdol5pBMcSsXEkJO9LtMMvY1m69tkOXymJPfBCkw_uYXr6auNVvN_sYiQcA-AQUBuU93mShd4ybpTHZ4jymyJTh9trxgADBn1J1Lv0G92Kq2RqLvtyDt7hN0BTq7nRl2Vr2qdzaE-eVAtMpEv51_Pg_VSZkkvjKJpcoAWk=s72-w363-h640-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-6983611557938493939</id><published>2025-05-12T13:38:00.002+08:00</published><updated>2025-05-12T13:38:13.862+08:00</updated><title type='text'>An Overview of SpriteRendererNAV</title><content type='html'>&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhiu88wLb4DD0ScOGxG6NTh6gjeJRbtk7Gcmfktu7bwhnP6ZAtJCS1ne0xnhQlTe5Gf8_uvq8peov0i1_sGAICEBk--6DX7u9i8oOPEZswrBCZGMMWewhPPnLF14Svwk1eMSEmeczlFTcKiE76XFvOKfJD6KVYC11jWQQkO1U636F6-EinCgtyg1ZmwaMbb&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;524&quot; data-original-width=&quot;786&quot; height=&quot;426&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhiu88wLb4DD0ScOGxG6NTh6gjeJRbtk7Gcmfktu7bwhnP6ZAtJCS1ne0xnhQlTe5Gf8_uvq8peov0i1_sGAICEBk--6DX7u9i8oOPEZswrBCZGMMWewhPPnLF14Svwk1eMSEmeczlFTcKiE76XFvOKfJD6KVYC11jWQQkO1U636F6-EinCgtyg1ZmwaMbb=w640-h426&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;The motivation behind developing SpriteRendererNAV stemmed from the desire to recreate the NAVMesh pathfinding tool, originally developed for a 2D scene editor in Cocos2d-X, within Unity. Additionally, users have expressed the need for a quick development tool that includes pathfinding functionality for 2D scenes, but they do not want to use the traditional A* algorithm, as it often creates a grid-like appearance unless the grid is made extremely fine, which can severely impact performance. As a result, I embarked on the development of the SpriteRendererNAV asset.&lt;span&gt;&lt;/span&gt;&lt;/p&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;The related videos for the custom Cocos2d-x scene editor:&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;476&quot; src=&quot;https://www.youtube.com/embed/mHBs4qNin5Q&quot; width=&quot;572&quot; youtube-src-id=&quot;mHBs4qNin5Q&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;The entire process is not very complicated. The following are the steps involved:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Add the World quad mesh to MeshBuildSources.&lt;/li&gt;&lt;li&gt;Create Mesh colliders for all SpriteRenderers.&lt;/li&gt;&lt;li&gt;Use Physics.BoxCast to scan all Colliders in the scene and generate the corresponding block line data.&lt;/li&gt;&lt;li&gt;Based on the block line data, create a block line mesh and add it to MeshBuildSources.&lt;/li&gt;&lt;li&gt;Create NavMeshData, use NavMeshBuilder.UpdateNavMeshData, and pass in MeshBuildSources.&lt;/li&gt;&lt;li&gt;Finally, add the generated NavMeshData to the NavMesh.&lt;/li&gt;&lt;/ol&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjBebRt2xGwoBH4vucKcb_RuKmhqW1F1WQ7WI6IWfjJl8Q7wlHqGL1mfRXNjrthGB59YPrPYUSFFZxc_nc_KomM7_ispvgmX2uYYoJOgCuoVgDvkH3O9uIZ6iCBxxlCmHKDqa1-QB1OWHLTKFNpbci9ARQTJRKGeJg_uAqtSekVGR2qMhpFzxiCI0j3wZms&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;449&quot; data-original-width=&quot;786&quot; height=&quot;366&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjBebRt2xGwoBH4vucKcb_RuKmhqW1F1WQ7WI6IWfjJl8Q7wlHqGL1mfRXNjrthGB59YPrPYUSFFZxc_nc_KomM7_ispvgmX2uYYoJOgCuoVgDvkH3O9uIZ6iCBxxlCmHKDqa1-QB1OWHLTKFNpbci9ARQTJRKGeJg_uAqtSekVGR2qMhpFzxiCI0j3wZms=w640-h366&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;For pathfinding testing, the PathCalculator script is provided to directly call NavMesh.CalculatePath and add NavMeshObstacle (Dynamic Obstacle) for pathfinding tests.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The related videos：&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; class=&quot;BLOG_video_class&quot; height=&quot;459&quot; src=&quot;https://www.youtube.com/embed/ZLepaGAdVJ4&quot; width=&quot;552&quot; youtube-src-id=&quot;ZLepaGAdVJ4&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;You can find it here:&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/tools/behavior-ai/spriterenderernav-289815&quot;&gt;SpriteRendererNAV&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/6983611557938493939/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/an-overview-of-spriterenderernav.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/6983611557938493939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/6983611557938493939'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/an-overview-of-spriterenderernav.html' title='An Overview of SpriteRendererNAV'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEhiu88wLb4DD0ScOGxG6NTh6gjeJRbtk7Gcmfktu7bwhnP6ZAtJCS1ne0xnhQlTe5Gf8_uvq8peov0i1_sGAICEBk--6DX7u9i8oOPEZswrBCZGMMWewhPPnLF14Svwk1eMSEmeczlFTcKiE76XFvOKfJD6KVYC11jWQQkO1U636F6-EinCgtyg1ZmwaMbb=s72-w640-h426-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6481831191748590297.post-6638288210491699833</id><published>2025-05-12T13:31:00.000+08:00</published><updated>2025-05-12T13:31:09.646+08:00</updated><title type='text'>The Solution to Excessive Terrain Mesh Data</title><content type='html'>&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjcet8u8h2EKkUJvCNa25QXkT3chBbLNL0LA9P--yKWK7VABDmxiRZZ8vWZ4-i18gEln6r1sxmGu7NQLTRbbt1tACzqyWWzMsVWUJLFctYCvUB-EslRp555-c7ec2WfnA9qIJN7S7K0T3N4_KPtI40pNQveATZQxF1jl2028P1Jpl-Oz1Lw1B72mxkxXIJL&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img data-original-height=&quot;524&quot; data-original-width=&quot;786&quot; height=&quot;426&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjcet8u8h2EKkUJvCNa25QXkT3chBbLNL0LA9P--yKWK7VABDmxiRZZ8vWZ4-i18gEln6r1sxmGu7NQLTRbbt1tACzqyWWzMsVWUJLFctYCvUB-EslRp555-c7ec2WfnA9qIJN7S7K0T3N4_KPtI40pNQveATZQxF1jl2028P1Jpl-Oz1Lw1B72mxkxXIJL=w640-h426&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;When dealing with Terrain, tools typically offer deformation capabilities, allowing users to adjust its undulations. However, these functionalities often limit adjustments to height alone, constraining the representation of the terrain. Consequently, this can lead to texture stretching issues. Therefore, to address these issues, people often need to add additional terrain features. However, doing so may introduce transition problems at the boundaries between the Terrain and the added features.&lt;span&gt;&lt;/span&gt;&lt;/p&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;When dealing with rivers or lakes, terrain tools are typically used to excavate the water bodies. However, if we focus on the details of their edges, it is often necessary to provide additional shoreline meshes for processing. This can also result in transition issues at the boundaries between the terrain and the water bodies.&lt;/p&gt;&lt;p&gt;Since the current designs are all based on mobile platforms, with a predominant top-down perspective, the terrain essentially appears flat. Hence, the idea arose to use mountains and riverbeds to create variations in elevation. The biggest remaining challenge then becomes how to handle the transitions at the edges effectively.&lt;/p&gt;&lt;p&gt;Furthermore, for mobile platforms, a large amount of grid data can potentially lead to performance issues. Through the aforementioned approach, we can address this problem. Essentially, the terrain is treated as a quad, with higher grid data only applied to mountains and riverbeds within the visible range.&lt;/p&gt;&lt;p&gt;Another common issue with Terrain is the repetitive tiling of textures. All the aforementioned related problems have been addressed in FastScene, where you can find solutions to them.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://assetstore.unity.com/packages/tools/terrain/fastscene-277478&quot;&gt;FastScene&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://makedreamvsogre.blogspot.com/feeds/6638288210491699833/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/the-solution-to-excessive-terrain-mesh.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/6638288210491699833'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6481831191748590297/posts/default/6638288210491699833'/><link rel='alternate' type='text/html' href='http://makedreamvsogre.blogspot.com/2025/05/the-solution-to-excessive-terrain-mesh.html' title='The Solution to Excessive Terrain Mesh Data'/><author><name>SmallBurger</name><uri>http://www.blogger.com/profile/05331049252498881199</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEjcet8u8h2EKkUJvCNa25QXkT3chBbLNL0LA9P--yKWK7VABDmxiRZZ8vWZ4-i18gEln6r1sxmGu7NQLTRbbt1tACzqyWWzMsVWUJLFctYCvUB-EslRp555-c7ec2WfnA9qIJN7S7K0T3N4_KPtI40pNQveATZQxF1jl2028P1Jpl-Oz1Lw1B72mxkxXIJL=s72-w640-h426-c" height="72" width="72"/><thr:total>0</thr:total></entry></feed>