tag:blogger.com,1999:blog-56269058741148039232024-03-18T00:21:41.067-07:00FX ThinkingNotes and thoughts about FX techniques.Unknownnoreply@blogger.comBlogger16125tag:blogger.com,1999:blog-5626905874114803923.post-85709529325490014402022-01-09T14:51:00.005-08:002022-01-09T14:57:01.030-08:00Cigarette Smoke - Part 3 - Houdini 19<div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="303" src="https://www.youtube.com/embed/ODKeCxGylqU" width="470" youtube-src-id="ODKeCxGylqU"></iframe></div><div class="separator" style="clear: both; text-align: center;"><br /></div><p>Since a lot of people are asking me about this setup I thought it would be a good idea to update the setup for H19.</p><p>The logic is pretty much identical to part 1 and 2. The differences are the following:</p><p></p><ul style="text-align: left;"><li>Pyro sim : I'm using a more modern Pyro workflow.</li><li>Cigarette Smoke Setup : I simplified the logic a bit. More specifically I ditched the Smooth SOP inside the Solver, and replaced it with a way faster Point Wrangle solution.<br /></li><li>Render : using now Karma XPU ! It's so ... so much faster than Mantra. The volume renders just fine in Karma XPU without any shader assignment. I have very briefly and unsuccessfully tried to assign a volume MaterialX Volume shader following <a href="https://www.sidefx.com/docs/houdini/solaris/materialx.html#volume-materials">these instructions</a>. If anybody succeeds please let me know !</li><li>Trivia : I've added a couple of fun advection experiments :</li><ul><li>advecting a grid to see what happens to the UVs (spoiler : nothing good)</li><li>advecting rows and lines of the same grid.</li></ul></ul><div><br /></div><div>Please find the hip file <a href="https://drive.google.com/file/d/1k6SabZ9Ygc1UNmTe1mNdl14ap6fBJldV/view?usp=sharing">here</a> and let me know if you've questions or suggestion.</div><div><br /></div><p></p>Unknownnoreply@blogger.com2Sunnyvale, CA, USA37.36883 -122.03634969.0585961638211572 -157.1925996 65.679063836178841 -86.8800996tag:blogger.com,1999:blog-5626905874114803923.post-40578988454550322222020-03-30T22:09:00.002-07:002021-06-05T07:18:25.095-07:00Corona Virus Houdini Graph Tools<div style="text-align: justify;">
If you want to skip all the bla bla part and go to the gimme the stuff part, click <a data-saferedirecturl="https://www.google.com/url?q=https://drive.google.com/file/d/1FLar2ABdP-WpSLrkXmM1mVdw0zoisePr/view?usp%3Dsharing&source=gmail&ust=1585716337262000&usg=AFQjCNE1AIPG8CdpwhG0_uyfOK-K4xLUjg" href="https://drive.google.com/file/d/1FLar2ABdP-WpSLrkXmM1mVdw0zoisePr/view?usp=sharing" target="_blank">HERE</a>.</div>
<div style="text-align: justify;">
</div>
<div style="text-align: justify;">
In
this crazy time of uncertainty and tragedy caused by Covid-19, the real
and only question to answer is really this : when is this going to end ?</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
I
am not an epidemiologist or a data analyst , so please take all
that follows with a grain of salt and keeping in mind I'm just a geek in
search for answers and I worked on this little project for a couple of
days, killing some of the time I've plenty of in these days.</div>
<div style="text-align: justify;">
</div>
<div style="text-align: justify;">
We know that Corona virus (any virus maybe ?) grows exponentially. But what does this really mean ? I
found a few answers watching these videos that I strongly recommend,
this time not only to learn something new, but also to help giving some
sense to immense amount of data concerning this pandemic.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
How To Tell If We're Beating COVID-19</div>
<div style="text-align: justify;">
<a data-saferedirecturl="https://www.google.com/url?q=https://www.youtube.com/watch?v%3D54XLXg4fYsc&source=gmail&ust=1585716337262000&usg=AFQjCNEq95bt_VmqgPN3E6D6MI2fKwU5FQ" href="https://www.youtube.com/watch?v=54XLXg4fYsc" target="_blank">https://www.youtube.com/watch?<wbr></wbr>v=54XLXg4fYsc</a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Exponential Growth and Epidemics</div>
<div style="text-align: justify;">
<a data-saferedirecturl="https://www.google.com/url?q=https://www.youtube.com/watch?v%3DKas0tIxDvrg%26t%3D354s&source=gmail&ust=1585716337262000&usg=AFQjCNGUB0pIse4aRpCGq8CR9T7TmbtwGg" href="https://www.youtube.com/watch?v=Kas0tIxDvrg&t=354s" target="_blank">https://www.youtube.com/watch?<wbr></wbr>v=Kas0tIxDvrg&t=354s</a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The
first of the two videos is plotting the data in a way that you don't
generally find online on Covid-19 statistics web sites: not only it
plots data in a logarithmic scale, <u>but it doesn't plot it against time</u>.</div>
<div style="text-align: justify;">
<u> </u></div>
<div style="text-align: justify;">
Long
story short, if I am not mistaken, to convert a regular value-time
graph into a new-total (not sure how this type of graphs is names,
please let me know if you do), this is how you do it:</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
X axis = log10 (total value X)</div>
<div style="text-align: justify;">
Y axis = log10 (total value today - total value yesterday)</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Watching
the video it seems like the data plotted this way show clearly when the
virus is about to start switching from an exponential growth ... to a
Sigmoid or Logistic Curve</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
More about Exponential Growth vs Logistic Curve here :</div>
<div style="text-align: justify;">
<a data-saferedirecturl="https://www.google.com/url?q=https://www.youtube.com/watch?v%3DKas0tIxDvrg&source=gmail&ust=1585716337262000&usg=AFQjCNFPJhoXRWXxA9gO5PUukjNYCqZdcQ" href="https://www.youtube.com/watch?v=Kas0tIxDvrg" target="_blank">https://www.youtube.com/watch?<wbr></wbr>v=Kas0tIxDvrg</a></div>
<div style="text-align: justify;">
</div>
<div style="text-align: justify;">
Days ago a friend of mine sent me a link to this github archive:</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<i><span>JSON time-series of coronavirus cases (confirmed, deaths and recovered) per country - updated daily </span>since 2020-1-22.</i> </div>
<div style="text-align: justify;">
<a data-saferedirecturl="https://www.google.com/url?q=https://github.com/pomber/covid19&source=gmail&ust=1585716337262000&usg=AFQjCNHW2pmfG4HUFCCt6_YFrtgsJdW2cQ" href="https://github.com/pomber/covid19" target="_blank">https://github.com/pomber/<wbr></wbr>covid19</a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Click on the actual data set below to take a look.</div>
<div style="text-align: justify;">
<a data-saferedirecturl="https://www.google.com/url?q=https://pomber.github.io/covid19/timeseries.json&source=gmail&ust=1585716337262000&usg=AFQjCNFPJnESzzt_kWCbixkgCoxFzwpLqQ" href="https://pomber.github.io/covid19/timeseries.json" target="_blank">https://pomber.github.io/<wbr></wbr>covid19/timeseries.json</a> </div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://1.bp.blogspot.com/-Vt2ojwbp7cY/XoLLs575KMI/AAAAAAABaqQ/FD7WY6pnxr8fO1DKKlnmw729oQxBqEeBgCEwYBhgLKs0DAMBZVoAhDIJTu4l07bGDw_cQlr-3ygolhJ4kklFLdbpUaKQrSG7gMRfcbqb9t1SkO8eqGrfuSeTU6aLez8GUeb9LwkIxJwK1L92pEc6a3jVqDX_bhHHUuPp_BJN9ydeACjbcW__lz-Xmusx1Wvc9Dzk1di63fPl8WvFsS20YSiFydJ7dBf1KB7MqRMVKo_gK36TYGha_uXsFHNzfztU5g2jiVvEsqqZ6iAfvt7nGHgXMqBwC58-kW-B7tqkVsh7AFbeYUob51QhJr7SLyjBzp_EJPs6biVGi7-9qzjgwjg9nCbd2hp4PiRxKg66YVkg3Uilb6JdYiKy6e3EpTHavWHwhG2Je9PZm5iCLF--e0YsBLDvF5k9mSsYtswBVOyLRYpnT5Oyf-rioI3wD4JDy27YH5zKu_xBK0uG9FlnMEdQBOQyBno11nj9UANAT1GV25Y5xbwuWtve5_2Nc_ouv7qAgPLoprp7UQ13RF7m6gaEtaa7IQTWg76SJ0iCkHeZsN89-xFnPrdoiFV6ne39L6k3_DN2y45uaWPFEMGqllXkQSrqCgiQXjD1J1G2Q57yDTecNQGyGHiS6AaHQALHc_YWI5VBH8F3xuWz_nzIwq5yL9AU/s1600/Selection_026.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="835" data-original-width="635" height="640" src="https://1.bp.blogspot.com/-Vt2ojwbp7cY/XoLLs575KMI/AAAAAAABaqQ/FD7WY6pnxr8fO1DKKlnmw729oQxBqEeBgCEwYBhgLKs0DAMBZVoAhDIJTu4l07bGDw_cQlr-3ygolhJ4kklFLdbpUaKQrSG7gMRfcbqb9t1SkO8eqGrfuSeTU6aLez8GUeb9LwkIxJwK1L92pEc6a3jVqDX_bhHHUuPp_BJN9ydeACjbcW__lz-Xmusx1Wvc9Dzk1di63fPl8WvFsS20YSiFydJ7dBf1KB7MqRMVKo_gK36TYGha_uXsFHNzfztU5g2jiVvEsqqZ6iAfvt7nGHgXMqBwC58-kW-B7tqkVsh7AFbeYUob51QhJr7SLyjBzp_EJPs6biVGi7-9qzjgwjg9nCbd2hp4PiRxKg66YVkg3Uilb6JdYiKy6e3EpTHavWHwhG2Je9PZm5iCLF--e0YsBLDvF5k9mSsYtswBVOyLRYpnT5Oyf-rioI3wD4JDy27YH5zKu_xBK0uG9FlnMEdQBOQyBno11nj9UANAT1GV25Y5xbwuWtve5_2Nc_ouv7qAgPLoprp7UQ13RF7m6gaEtaa7IQTWg76SJ0iCkHeZsN89-xFnPrdoiFV6ne39L6k3_DN2y45uaWPFEMGqllXkQSrqCgiQXjD1J1G2Q57yDTecNQGyGHiS6AaHQALHc_YWI5VBH8F3xuWz_nzIwq5yL9AU/s640/Selection_026.png" width="484" /></a></div>
<div style="text-align: justify;">
<br /><span id="goog_1549042006"></span><span id="goog_1549042007"></span></div>
<div style="text-align: justify;">
So I decided to create a tool to plot these curves in Houdini, grabbing the data directly from the net.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<b>Disclaimer :</b></div>
<div style="text-align: justify;">
<b>I
am not responsible for the data contained in the above mentioned
repository. I am just reading it and visualizing it using Houdini. It is very likely that at some point, hopefully soon (and hopefully for a good reason), this repository will stop being updated. </b></div>
<div style="text-align: justify;">
</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The code to download the data from the github and store it in points is really short. See for yourself.</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://1.bp.blogspot.com/-JMhUjp4jMnk/XoLLs2rC74I/AAAAAAABaqI/uyOUIbAxp8AZ_URzFqBcZdV0jEzu_BYPgCEwYBhgLKs0DAMBZVoBB-ehcX_wtmbyUC1D5xsK537jL_pZ1fPgEfUfgIokyWf_VSj9VM1fhhB05pBsRkDxNdmAyktuAwDlp3tKXLA7sUecyxywnPmqGvVX8L1qeJd98Ap9mO8Y0jNc7kADRX4LpgX7tHXyyd_a4Y0j4v54goQKgtb9wcAnEKlgfXprVtXcbo2pQA4PPBr68P_cWuLKrywL_cgzRNwQJvkN6f_Y3ymUYflhYs0P7E1FKDl9licLoWF6-Md68_iaC8yjfG4v4iUceZTw07psZoOS9jp_iEU-xC_POojIacl-5GuqMZITFnSVHMzOc6y9K3cLilIGhYWX7MoqcpNMIH-a5tkAnXZ1jl3ytB1XwVm-U_DCEC7dzvQHDpaSv3fdNBAAUzezq_ArcGab5tTLs1FhUL_CzartucSQKZ8Sf85-6XWS1ByOGRgMPGEP3qgTaXBPGiEUHyeS_A8PTOjP5D4JUQbd8vQosu3DL08RL0roxnCJf5YanYb0vuH56fPxsm3ZBQRBzZA6Y7hlSRyQTS26bvkSF1Pj_zeW8EGWgNT-BvNHt5waoDNzxIj_L-EUJiUuq-KubUKN_2mUFzlraWax2mg523ubVICXsRsYw0ZyL9AU/s1600/Selection_027.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1008" data-original-width="740" height="640" src="https://1.bp.blogspot.com/-JMhUjp4jMnk/XoLLs2rC74I/AAAAAAABaqI/uyOUIbAxp8AZ_URzFqBcZdV0jEzu_BYPgCEwYBhgLKs0DAMBZVoBB-ehcX_wtmbyUC1D5xsK537jL_pZ1fPgEfUfgIokyWf_VSj9VM1fhhB05pBsRkDxNdmAyktuAwDlp3tKXLA7sUecyxywnPmqGvVX8L1qeJd98Ap9mO8Y0jNc7kADRX4LpgX7tHXyyd_a4Y0j4v54goQKgtb9wcAnEKlgfXprVtXcbo2pQA4PPBr68P_cWuLKrywL_cgzRNwQJvkN6f_Y3ymUYflhYs0P7E1FKDl9licLoWF6-Md68_iaC8yjfG4v4iUceZTw07psZoOS9jp_iEU-xC_POojIacl-5GuqMZITFnSVHMzOc6y9K3cLilIGhYWX7MoqcpNMIH-a5tkAnXZ1jl3ytB1XwVm-U_DCEC7dzvQHDpaSv3fdNBAAUzezq_ArcGab5tTLs1FhUL_CzartucSQKZ8Sf85-6XWS1ByOGRgMPGEP3qgTaXBPGiEUHyeS_A8PTOjP5D4JUQbd8vQosu3DL08RL0roxnCJf5YanYb0vuH56fPxsm3ZBQRBzZA6Y7hlSRyQTS26bvkSF1Pj_zeW8EGWgNT-BvNHt5waoDNzxIj_L-EUJiUuq-KubUKN_2mUFzlraWax2mg523ubVICXsRsYw0ZyL9AU/s640/Selection_027.png" width="468" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Once you run this code in a Python SOP, you get ALL the data from the repository stored on attributes in points.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
To be more precise, I created one point for each Json item. Each point stores the data in the following attributes:</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<i>i@__rona__confirmed</i></div>
<div style="text-align: justify;">
<i>s@__rona__country</i></div>
<div style="text-align: justify;">
<i>s@__rona__date</i></div>
<div style="text-align: justify;">
<i>i@__rona_deaths</i></div>
<div style="text-align: justify;">
<i>i@__rona_recovered</i></div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://1.bp.blogspot.com/-0W6vKB16kTk/XoLLs0rFUWI/AAAAAAABaq8/d9dF3yLnzQATy66GHVaVYqqHaa5g_s2MgCEwYBhgLKs0DAMBZVoABT57vwrq4k6Qr6L6gmkK8rcs7e_wtPVFRHH6Q45y0gH9lrJZ3tgX8G6HbqlRmixe4PAnd3i_cFiMKLoQyqtgOBkV04hv1adeSj7d4VJxvYwmwTTKQfY7qDbjrApTR1kZR3VJU9k2u3QXFz6tOR088F-FE3mecuZXlccbk5Gg_j2EOLufKCh0aibMluo13eZwpu5STZwFBCVtZk6hUfUTXL9OLj_I4LqURC_geiMaAVvfo5daOD6aKIMEEhxJFJILOkh4gZNhCgMpZqH58msWUMD_J9P2oFqyWcycEv2o3NjfiHEF7w3KFend1B7j7eMsRhK28DMGNZYor9FlPU0MGd15HvBOUle8wA_5A6fBCWoQbEY4EzY2pq7_1PU7b43xQHbm6Sx_FKIbRqUO_52lmF9R4Vu-Xic1dBhAIX8QXC6He3n08jspMUmURVwmTKYv0UReTh1JPPRqD2_GEKvbcW2O3l-CCJLhunCO7adoeDeA8GAyoXKT7DDKFhxsBlYVvZSerO7ZoLzf3x1Z2V9CzDH4ULCLwcTgkqOX1w5Ih4L1IoAd8zKE5iWhhL3l2sMSgZlIgGITUON76DgMG31k45l5MIyCmbDsw7ZyL9AU/s1600/Selection_028.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="783" data-original-width="845" height="592" src="https://1.bp.blogspot.com/-0W6vKB16kTk/XoLLs0rFUWI/AAAAAAABaq8/d9dF3yLnzQATy66GHVaVYqqHaa5g_s2MgCEwYBhgLKs0DAMBZVoABT57vwrq4k6Qr6L6gmkK8rcs7e_wtPVFRHH6Q45y0gH9lrJZ3tgX8G6HbqlRmixe4PAnd3i_cFiMKLoQyqtgOBkV04hv1adeSj7d4VJxvYwmwTTKQfY7qDbjrApTR1kZR3VJU9k2u3QXFz6tOR088F-FE3mecuZXlccbk5Gg_j2EOLufKCh0aibMluo13eZwpu5STZwFBCVtZk6hUfUTXL9OLj_I4LqURC_geiMaAVvfo5daOD6aKIMEEhxJFJILOkh4gZNhCgMpZqH58msWUMD_J9P2oFqyWcycEv2o3NjfiHEF7w3KFend1B7j7eMsRhK28DMGNZYor9FlPU0MGd15HvBOUle8wA_5A6fBCWoQbEY4EzY2pq7_1PU7b43xQHbm6Sx_FKIbRqUO_52lmF9R4Vu-Xic1dBhAIX8QXC6He3n08jspMUmURVwmTKYv0UReTh1JPPRqD2_GEKvbcW2O3l-CCJLhunCO7adoeDeA8GAyoXKT7DDKFhxsBlYVvZSerO7ZoLzf3x1Z2V9CzDH4ULCLwcTgkqOX1w5Ih4L1IoAd8zKE5iWhhL3l2sMSgZlIgGITUON76DgMG31k45l5MIyCmbDsw7ZyL9AU/s640/Selection_028.png" width="640" /></a></div>
<div style="text-align: justify;">
<i> </i></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
I wrapped this python script into an HDA named ...</div>
<div style="text-align: justify;">
<br /></div>
<h2 style="text-align: justify;">
Rona Import Data </h2>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://1.bp.blogspot.com/-w5UjqtTg7w4/XoLLtGWkl3I/AAAAAAABarE/8PX8u6TgL6MgU0NZgyyFQW--1S4APYiWwCEwYBhgLKs0DAMBZVoAKuCUWtnjEV77shhV3qAekwVp87tK6U2DlTrROg1lHaQlmfql40Gf9xZ7gWyQoYVrmWmeS0qkMynZQPJF24JSHaAB0eQTBvJLr_SYUwus7eMug2ET9p8Yfdnae8qL6U0P3-Ohn_dCtr4xg5RqNHGqKJzFqHk84r3kIjMsYOTyol9_KTKAT3K6ZZXV9B3bjCfSMHeXTbanV6nCYZ-cc2Hkcn6t1ktxBVUVISmSNcq8dQNAIbnEJo_3K_ZHIQzyxENlSmMdWVW0-PpUknyksyCK4u3_ChVzqq98BFfrdM8erRT-VtJ7rLQy5G2-HY3omi9m4rIOqsXA4e7OUJc3KxhcHEA_wdbafTPXsYuyr-yh15uqmwp5DVig-iCE5ozBkywfleTv2bymn-AGt7r4bUj7WpkAZPQBdDYIwWzeR_WER8ImVZXoosYi1wMB6ZRNgWecLSstZloF2rDvSAFF8FlFt4dnRFNJ-CcZ52hJJlZxNsLgnksNwwZ9UF-AP9yf8ethny6WoXuyVDih0o2FsoZxGFquI-sCZjLzg_Q_qR3ue6E4ePIFX21eiotdHGogPNIkEC7IE9llZC9a2MiwTgZMsYZ5z5f-EDKMw-5yL9AU/s1600/Selection_029.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="363" data-original-width="853" height="170" src="https://1.bp.blogspot.com/-w5UjqtTg7w4/XoLLtGWkl3I/AAAAAAABarE/8PX8u6TgL6MgU0NZgyyFQW--1S4APYiWwCEwYBhgLKs0DAMBZVoAKuCUWtnjEV77shhV3qAekwVp87tK6U2DlTrROg1lHaQlmfql40Gf9xZ7gWyQoYVrmWmeS0qkMynZQPJF24JSHaAB0eQTBvJLr_SYUwus7eMug2ET9p8Yfdnae8qL6U0P3-Ohn_dCtr4xg5RqNHGqKJzFqHk84r3kIjMsYOTyol9_KTKAT3K6ZZXV9B3bjCfSMHeXTbanV6nCYZ-cc2Hkcn6t1ktxBVUVISmSNcq8dQNAIbnEJo_3K_ZHIQzyxENlSmMdWVW0-PpUknyksyCK4u3_ChVzqq98BFfrdM8erRT-VtJ7rLQy5G2-HY3omi9m4rIOqsXA4e7OUJc3KxhcHEA_wdbafTPXsYuyr-yh15uqmwp5DVig-iCE5ozBkywfleTv2bymn-AGt7r4bUj7WpkAZPQBdDYIwWzeR_WER8ImVZXoosYi1wMB6ZRNgWecLSstZloF2rDvSAFF8FlFt4dnRFNJ-CcZ52hJJlZxNsLgnksNwwZ9UF-AP9yf8ethny6WoXuyVDih0o2FsoZxGFquI-sCZjLzg_Q_qR3ue6E4ePIFX21eiotdHGogPNIkEC7IE9llZC9a2MiwTgZMsYZ5z5f-EDKMw-5yL9AU/s400/Selection_029.png" width="400" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
This guy needs no parameters. All it does is download the latest data on the github repo. All you need to do is drop the node.</div>
<div style="text-align: justify;">
Now we can play with the data.</div>
<div style="text-align: justify;">
<br /></div>
<h2 style="text-align: justify;">
<b>Rona Filter</b><b> </b></h2>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://1.bp.blogspot.com/-ZD86Lqupi5s/XoLLtf71pyI/AAAAAAABarM/AslskLcx3ugP9TZ-dpUvTmICx9iFC5STACEwYBhgLKs0DAMBZVoAwH6J-TN039ekNyu46iYczI1UkNQjEdd_ZKANB3lSctSEVMINB4snHQHCWAajOi4qeKjePuCbCAOCD1mhShrdF05M5c4rjHUhhzU4qqaFa_MAmmyrosfEZKZo6uQ-wm05Gr59s1I-HyquNeGKGwd1L6_A1wyBeha7_fBlge4CXqEAfUPpZvifAXvFIPDI3NNQzPEgNIA-v6KHEUl_TshsefqBC7Is7Fyrm6qx1mGdt_P3g1C2FxWYCqySewKGDsS_m5sOfMqrjuH_8BqsMH-F5H6KuHJEzvojH7AX4j_0oWMl22B1P8eGtOPeIBTGFIaHrXF8HgC5_u1VS7WklBV4mCoSKdReh_DexwMqGnKHQxsgQk3mk0TiwUr4K_hiyWV0kOoB_hfKY0IIGuM8cc26bQURjwnkG4KS4rmO9Y0dxb92E4-j48Wx_NX3LPDWf1i2da_i8UQORoq-9h7uoy_qC8C1qj6fUTCiG4rTliJeVRtfWjrMfNu0RO8R_f8Wi_h3kv0w9GWNzY8_1DidPz4pNKIMxZJ3iequnIdKu2zWUWawN50Esaw9aH6tPPAud-RmNh9_QkOxEhAke8ijFi_tRIaJIexaaJwowpZ2L9AU/s1600/Selection_030.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="156" data-original-width="944" height="104" src="https://1.bp.blogspot.com/-ZD86Lqupi5s/XoLLtf71pyI/AAAAAAABarM/AslskLcx3ugP9TZ-dpUvTmICx9iFC5STACEwYBhgLKs0DAMBZVoAwH6J-TN039ekNyu46iYczI1UkNQjEdd_ZKANB3lSctSEVMINB4snHQHCWAajOi4qeKjePuCbCAOCD1mhShrdF05M5c4rjHUhhzU4qqaFa_MAmmyrosfEZKZo6uQ-wm05Gr59s1I-HyquNeGKGwd1L6_A1wyBeha7_fBlge4CXqEAfUPpZvifAXvFIPDI3NNQzPEgNIA-v6KHEUl_TshsefqBC7Is7Fyrm6qx1mGdt_P3g1C2FxWYCqySewKGDsS_m5sOfMqrjuH_8BqsMH-F5H6KuHJEzvojH7AX4j_0oWMl22B1P8eGtOPeIBTGFIaHrXF8HgC5_u1VS7WklBV4mCoSKdReh_DexwMqGnKHQxsgQk3mk0TiwUr4K_hiyWV0kOoB_hfKY0IIGuM8cc26bQURjwnkG4KS4rmO9Y0dxb92E4-j48Wx_NX3LPDWf1i2da_i8UQORoq-9h7uoy_qC8C1qj6fUTCiG4rTliJeVRtfWjrMfNu0RO8R_f8Wi_h3kv0w9GWNzY8_1DidPz4pNKIMxZJ3iequnIdKu2zWUWawN50Esaw9aH6tPPAud-RmNh9_QkOxEhAke8ijFi_tRIaJIexaaJwowpZ2L9AU/s640/Selection_030.png" width="640" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Self explanatory.</div>
<div style="text-align: justify;">
The parameter "Country (Regex)" accepts a regular expression string.</div>
<div style="text-align: justify;">
Example:</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<i>.*</i> means 'don't filter anything', it's like don't using this node at all |:)</div>
<div style="text-align: justify;">
<i>Italy</i> will keep only data about Italy</div>
<div style="text-align: justify;">
<i>(Italy|US|Spain)</i> will keep data from Italy and US</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Use this node immediately after a Rona Import SOP. Or don't use it at all if you want to keep all countries.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<h2 style="text-align: justify;">
Rona Plot</h2>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://1.bp.blogspot.com/-hQXcE5qemZQ/XoLLtzYgOdI/AAAAAAABarU/1s6obVM-FOQ61nXJ1525pVZJLXwd4S62wCEwYBhgLKs0DAMBZVoBgcuY0Vv-cmp8wPaSOxMisdJd0uyOpYqU6LNJprlhEC2ZNx1bY9o5kYA2fjL06WEVNFLXy17NpGoZVln-iKNiI5eTjW6SETCMWePdqTvNqcrkcJVkihB8XNXt7qXgKlY6v9T9ByucyQd0hMJm5t6XG1LETdnh5BSXwC__IiWG5oevyh4y-idnf3CcLQEGpCxRTZJbE5vOMJIo1WqGBr3IBTiw9xGS4j4vVai8qf4oXU7RoROAy0g-1y-huw1UIItWNrdF6A7gIE0ziCoqqEoSUYTnyKr_IqBmSEr3Mg2G5Zr-rTn4yiBGvRpVE2_Ym0h-9aq42PAzWZhfwvsPa0IF6-SU_TDn3fuEsVEs25ErR2zmR_seEl_YFQHuR8JUN5awYR89Hn-Q58raX1wxdag54dPV-9SbLv7Dx25yiKmXLpYdATivw3nlXbhKKP0lxp-zZS9hocM6OLeql3Pj9s7U_YlIeJDbcxCu47ZGquehRMbYNh4XSCj4ggK_U98vzw1vS98fmR6IaCcj2UrcXBzDV964vfvrOSq6LvK-JpNpY4ru3-OUzHvDh_S5axEWDtNwaS853Mn0Ot0Hn3epr7LJDeBKPPBabboIwt52L9AU/s1600/Selection_031.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="234" data-original-width="913" height="164" src="https://1.bp.blogspot.com/-hQXcE5qemZQ/XoLLtzYgOdI/AAAAAAABarU/1s6obVM-FOQ61nXJ1525pVZJLXwd4S62wCEwYBhgLKs0DAMBZVoBgcuY0Vv-cmp8wPaSOxMisdJd0uyOpYqU6LNJprlhEC2ZNx1bY9o5kYA2fjL06WEVNFLXy17NpGoZVln-iKNiI5eTjW6SETCMWePdqTvNqcrkcJVkihB8XNXt7qXgKlY6v9T9ByucyQd0hMJm5t6XG1LETdnh5BSXwC__IiWG5oevyh4y-idnf3CcLQEGpCxRTZJbE5vOMJIo1WqGBr3IBTiw9xGS4j4vVai8qf4oXU7RoROAy0g-1y-huw1UIItWNrdF6A7gIE0ziCoqqEoSUYTnyKr_IqBmSEr3Mg2G5Zr-rTn4yiBGvRpVE2_Ym0h-9aq42PAzWZhfwvsPa0IF6-SU_TDn3fuEsVEs25ErR2zmR_seEl_YFQHuR8JUN5awYR89Hn-Q58raX1wxdag54dPV-9SbLv7Dx25yiKmXLpYdATivw3nlXbhKKP0lxp-zZS9hocM6OLeql3Pj9s7U_YlIeJDbcxCu47ZGquehRMbYNh4XSCj4ggK_U98vzw1vS98fmR6IaCcj2UrcXBzDV964vfvrOSq6LvK-JpNpY4ru3-OUzHvDh_S5axEWDtNwaS853Mn0Ot0Hn3epr7LJDeBKPPBabboIwt52L9AU/s640/Selection_031.png" width="640" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
This is where the fun starts.</div>
<div style="text-align: justify;">
And ends too, pretty much, really.</div>
<div style="text-align: justify;">
This node plots all the data incoming in the input (curves only, no axis, text etc).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The output of this node are polylines with prim attributes:</div>
<div style="text-align: justify;">
prim attr <i>s@__rona_country </i>( a string indicating the country ) <i><br /></i></div>
<div style="text-align: justify;">
prim attr <i>s@__rona_data</i> (can be "deaths", "recovered" or "confirmed")</div>
<div style="text-align: justify;">
prim attr <i>i@__rona_graph</i> (can be 0 for time-value graph, and 1 for trend graph)</div>
<div style="text-align: justify;">
prim attr <i>i@__rona_graph_scale</i> (graph Y scale)</div>
<div style="text-align: justify;">
</div>
<div style="text-align: justify;">
<b>Parameters:</b></div>
<div style="text-align: justify;">
<b>Data</b> : you can choose between</div>
<ul style="text-align: justify;">
<li>Deaths - total number of deaths accumulated per day</li>
<li>Recovered - total number of recovered accumulated per day</li>
<li>Confirmed - total number of confirmed cases accumulated per day</li>
</ul>
<div style="text-align: justify;">
<b>Graph</b>: you can choose between</div>
<ul style="text-align: justify;">
<li>Time-Linear - the good old time-value we're used to</li>
<li>Trend-Log - the graph I mentioned earlier. Will show if things are going better or not really.</li>
<li><b>Scale</b> : sometimes the y values are huge. Sadly. We need to scale the Y axis to fit it in the screen. </li>
</ul>
<div style="text-align: justify;">
<b>Filter - Strength</b> : daily values are pretty erratic. Use some filter to smooth the curves and let the meaning emerge.</div>
<div style="text-align: justify;">
<b>Filter - Filter Quality</b> : the higher this value, the more the smoothed curve will stay close to the original curve. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Use this node immediately after a <b>Rona Import SOP</b>, or a <b>Rona Filter SOP</b>.</div>
<div style="text-align: justify;">
<br /></div>
<h2 style="text-align: justify;">
<b>Rona Decorators</b></h2>
<div style="text-align: justify;">
This node<b> </b>takes care of adding axis, text, etc to the curves. Cause without metrics nothing makes sense.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
NOTE :</div>
<div style="text-align: justify;">
I lied. The TREND graph actually doesn't really need metrics, since the meaningful part is the shape of the curve.</div>
<div style="text-align: justify;">
And since I didn't really know what units to write in there.</div>
<div style="text-align: justify;">
And I have been a little lazy, I admit.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
ANOTHER NOTE:</div>
<div style="text-align: justify;">
You can merge together many curves upstream before feeding this node, just make sure the curves are coming from the same graph typs (linear or log) and they all have the same scale. </div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://1.bp.blogspot.com/-5PtakFQK_u0/XoLLvF9ywXI/AAAAAAABarI/tWhe9kiLHh8z63dKg13TC5nYhPa5nmxRgCEwYBhgLKs0DAMBZVoAoV6DXjqgRr0JgmxgTbKJU8KkbVYwknoA1YDRNtutZfdiLgU_ojn2Hy6C-qGYgH2vaZ8GEG8VrFMpMRBDj0B7URYs_vBguqvWjn_yEt7A11nr4wfbWgiM0AE-JFLzplmx3pnHm1Qmq_c6kjbR7z01T6vit8v21AzlRSQXWIuxiDpkJ7fZNxkOjEz4N9fkaAx3juLTCU7M9rxThfajJQhKdFAZTwxyWvtG231-PqbK-0ft8ThM1DQHsM35h8rRGSD2eANegjFo09KVVijaPzTgXrnRWzL04nGC6lTCmS_qGoxs0W2NDBAWsv01E-c23gyBjtwdDWSy9w-HfLAWQYAMHtDtuKyiXklkG9Q1mi_-3__8ktMQ6ewam08V7iZ2X8n1oen__DpxXZV6M60rC6ybozNmV5dfq8LXoWRmpBvjiamO2ZfWglOxVuPJhHPzFfB7A6vNeU2yp50XLgHypp9gA7QsjQNa7DwvdcZ-z4yszSEdh4tSpDOzsYh0nmkczd9tQUAlLm5fQtYrfFVsv6-7G3bMlD65coGjqZLbIB9npFfpSmUnbXU5vmlQ7c9JjFmfIlw_0D7NDgIITGznW5jQOX9spOEol8wgwyJ2L9AU/s1600/Selection_034.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="314" data-original-width="1212" height="164" src="https://1.bp.blogspot.com/-5PtakFQK_u0/XoLLvF9ywXI/AAAAAAABarI/tWhe9kiLHh8z63dKg13TC5nYhPa5nmxRgCEwYBhgLKs0DAMBZVoAoV6DXjqgRr0JgmxgTbKJU8KkbVYwknoA1YDRNtutZfdiLgU_ojn2Hy6C-qGYgH2vaZ8GEG8VrFMpMRBDj0B7URYs_vBguqvWjn_yEt7A11nr4wfbWgiM0AE-JFLzplmx3pnHm1Qmq_c6kjbR7z01T6vit8v21AzlRSQXWIuxiDpkJ7fZNxkOjEz4N9fkaAx3juLTCU7M9rxThfajJQhKdFAZTwxyWvtG231-PqbK-0ft8ThM1DQHsM35h8rRGSD2eANegjFo09KVVijaPzTgXrnRWzL04nGC6lTCmS_qGoxs0W2NDBAWsv01E-c23gyBjtwdDWSy9w-HfLAWQYAMHtDtuKyiXklkG9Q1mi_-3__8ktMQ6ewam08V7iZ2X8n1oen__DpxXZV6M60rC6ybozNmV5dfq8LXoWRmpBvjiamO2ZfWglOxVuPJhHPzFfB7A6vNeU2yp50XLgHypp9gA7QsjQNa7DwvdcZ-z4yszSEdh4tSpDOzsYh0nmkczd9tQUAlLm5fQtYrfFVsv6-7G3bMlD65coGjqZLbIB9npFfpSmUnbXU5vmlQ7c9JjFmfIlw_0D7NDgIITGznW5jQOX9spOEol8wgwyJ2L9AU/s640/Selection_034.png" width="640" /></a></div>
<div style="text-align: justify;">
<b><br /></b></div>
<div style="text-align: justify;">
<b>Parameters:</b></div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://1.bp.blogspot.com/-rwGf5gZ4AAs/XoLLuPIWMKI/AAAAAAABarU/Hb7J2eOr56A5q2DHd832RDsRBH65KJ67ACEwYBhgLKs0DAMBZVoA07Jr6-0hzbavu_GqCOvPuW9cfep3hg5orWzw0UHdhKCZXQGr3LAQcZq-mi0NLnELlxtE9mKLudztMz-FfRxhVrmw1jOSFwUh8tr7eSrWP40w3QseLXP9eHHqaLBJPZLHiq8xXKBetdWu6Gxe0gNMAC4wcocr3wbSxtv0_b3dVHbTu7TO4GQd8MnBfeumpj_YlNKan5JFzKYUR8CFgk94FPieLaCoC686Yc8MRwfs-jkgePMNPgvVL_aK9pFTdOMgOJMOZ6T44CyD3oqvt0J3ZWP-5LpFDt65SEpgaXhhYJX0XVsFiupavidm6wyq6GaELEHxqwy9euSMayt-CLhAy6_PgCgwIk0Y_D7gEPsbgg-vQmZwdO1fRzwynJZQ616sIkGnEJSXyhIyEXF9DQMJ24c7_eeJaECEkRX6vd8v8BidG2zygv2vC0bM90hIcr6XOwAXTczTkv_6C2jZLipnq8jhzsf0eWGHr6n6Za_YdK_Jb_0-mCPHnjQlGhl61ieKzV05J4GSv8_10rXXzfQKRhjoEUh64hy4my75blxjbMFkC4jIy8V8xx8Lfpsni34gLz1J4R-n9yLIRMRCO1zublkroTCvkQ98w3p2L9AU/s1600/Selection_032.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1319" data-original-width="1508" height="558" src="https://1.bp.blogspot.com/-rwGf5gZ4AAs/XoLLuPIWMKI/AAAAAAABarU/Hb7J2eOr56A5q2DHd832RDsRBH65KJ67ACEwYBhgLKs0DAMBZVoA07Jr6-0hzbavu_GqCOvPuW9cfep3hg5orWzw0UHdhKCZXQGr3LAQcZq-mi0NLnELlxtE9mKLudztMz-FfRxhVrmw1jOSFwUh8tr7eSrWP40w3QseLXP9eHHqaLBJPZLHiq8xXKBetdWu6Gxe0gNMAC4wcocr3wbSxtv0_b3dVHbTu7TO4GQd8MnBfeumpj_YlNKan5JFzKYUR8CFgk94FPieLaCoC686Yc8MRwfs-jkgePMNPgvVL_aK9pFTdOMgOJMOZ6T44CyD3oqvt0J3ZWP-5LpFDt65SEpgaXhhYJX0XVsFiupavidm6wyq6GaELEHxqwy9euSMayt-CLhAy6_PgCgwIk0Y_D7gEPsbgg-vQmZwdO1fRzwynJZQ616sIkGnEJSXyhIyEXF9DQMJ24c7_eeJaECEkRX6vd8v8BidG2zygv2vC0bM90hIcr6XOwAXTczTkv_6C2jZLipnq8jhzsf0eWGHr6n6Za_YdK_Jb_0-mCPHnjQlGhl61ieKzV05J4GSv8_10rXXzfQKRhjoEUh64hy4my75blxjbMFkC4jIy8V8xx8Lfpsni34gLz1J4R-n9yLIRMRCO1zublkroTCvkQ98w3p2L9AU/s640/Selection_032.png" width="640" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
</div>
<div style="text-align: justify;">
</div>
<div style="text-align: justify;">
<br /></div>
<h2 style="text-align: justify;">
Plots Examples</h2>
<div style="text-align: justify;">
<span style="font-weight: normal;">These are all contained in the example hip file (link at the end of this article).</span></div>
<div style="text-align: justify;">
<span style="font-weight: normal;"> </span></div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://1.bp.blogspot.com/-ITIG_UDqp9w/XoLLvqJ8ZcI/AAAAAAABarM/doa4VwEyZ2kwK2ciQ1th-SlTATBLY6A_QCEwYBhgLKs0DAMBZVoBsxPaCa--0qgRmhZMlWUs-3Bvn5StJBkVumNzzf8F-9_hxrSz0t-4C91i7ON-ONEYlyLLo86gUUXQxOaXPKohlJ2I0T2sMf_wezAyK5ixPGm8BiXaXiIbGdRb0D9YItN6W666SZ2canOJ9XvA6FRW33W6ssyUhXsQas-DcNjWetU4ZClKMmTx50JVBu-HR5SHTlfp1YUPGrWDQUgh2QvKPwk5Y_HCrx8tbpYI7wCzqzad9Ibh_BJmYzvaVdWrRRd9tj2hXhlkjc-IwqrIOh732OrBtZpvFudJ7jYh_DlQ_ZwKxAdvUC2NAw_kuO2o7X5xGj4O5-RIYlUHmrizH9XPX_I4pvuJy90vyrvTA-6M9y6Kg8JLq78nPG1_UE4R7sR4AGCZm96lUAdm0BOLPNBKZ_I6FcoE1jPnYSC6DTMYd7YFAkwn06KaIV0uLtQJBoYavUaDit8XmQdLVtsOTHp11bCUnGNF55E7exI-zCZdcIuKSFKemp_xMfotNfjPVZ6ayO9lTlV0mwfmPL5zCPwID4coM8zkG8dmIHOJ4Nkkc_nsyJxhTqxtblfK76ak-PRRN06SfCj5FopssCjy1qYJDumglMwPzYWkw852L9AU/s1600/Selection_035.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="820" data-original-width="1600" height="328" src="https://1.bp.blogspot.com/-ITIG_UDqp9w/XoLLvqJ8ZcI/AAAAAAABarM/doa4VwEyZ2kwK2ciQ1th-SlTATBLY6A_QCEwYBhgLKs0DAMBZVoBsxPaCa--0qgRmhZMlWUs-3Bvn5StJBkVumNzzf8F-9_hxrSz0t-4C91i7ON-ONEYlyLLo86gUUXQxOaXPKohlJ2I0T2sMf_wezAyK5ixPGm8BiXaXiIbGdRb0D9YItN6W666SZ2canOJ9XvA6FRW33W6ssyUhXsQas-DcNjWetU4ZClKMmTx50JVBu-HR5SHTlfp1YUPGrWDQUgh2QvKPwk5Y_HCrx8tbpYI7wCzqzad9Ibh_BJmYzvaVdWrRRd9tj2hXhlkjc-IwqrIOh732OrBtZpvFudJ7jYh_DlQ_ZwKxAdvUC2NAw_kuO2o7X5xGj4O5-RIYlUHmrizH9XPX_I4pvuJy90vyrvTA-6M9y6Kg8JLq78nPG1_UE4R7sR4AGCZm96lUAdm0BOLPNBKZ_I6FcoE1jPnYSC6DTMYd7YFAkwn06KaIV0uLtQJBoYavUaDit8XmQdLVtsOTHp11bCUnGNF55E7exI-zCZdcIuKSFKemp_xMfotNfjPVZ6ayO9lTlV0mwfmPL5zCPwID4coM8zkG8dmIHOJ4Nkkc_nsyJxhTqxtblfK76ak-PRRN06SfCj5FopssCjy1qYJDumglMwPzYWkw852L9AU/s640/Selection_035.png" width="640" /></a></div>
<div style="text-align: justify;">
Filters Italy, Spain and France and plot the deceased data over time.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://1.bp.blogspot.com/-I81X5sr9-pk/XoLLwSB0awI/AAAAAAABarQ/ud-HWpW_5SEfkcnVPbtd6j4AlQfsiuRZQCEwYBhgLKs0DAMBZVoAk-scUxUKZUW86ZnMK07xNZTM_i8-aHjJP7D2GtEnvuhb8zetgxhCuESXpckXH2IEGDgY-_vr5aooGHNq3zaW77_AtviQAA9rzOfWkp0aWnvvlXe70p_1OqHCWiCpivJ0TBhi8dxRnDPh9rckZKlbmaeB8zklnhCWqPefmtc6jsMVU0dwGiBBVrnuEJSPOgSXzgLdNWa7ifEKiULp16nYGMOs65a_N-a7NDITwJqJMwc5FymwJ_AvEv1BmGjtDtcBjXvKts1sSUP1SdNoRAISIvsgoV57n5iKw0AEn3j4HEltTfBvCG6TaS5DF46KLOiBuo-_FLkyoQ7IBI9tx5liM2FN9shqpLUrbawd1hIhVEo579BMElU_7x2gJe3riotV9QVVg4ckwn2uSpj4LFC2tsKfvrk4n11HF5fmpFpZUjTQXaSXDq_-CQg2JDhLqYSZyJbo5jjyhixdq0OgTIktwjPERjpsS9y2u4SkhFT7BzY2DMBZxLvQQJHH-Esd_bouCYkjI8KOrUKSPymErOaQR6yHS7aHb0quvUv7fUaqfb3AkkD4q--YE2dgXco8lzRH6rS793GWNh-BuLVj6xqqXTp3PRPaRKB0wjJ6L9AU/s1600/Selection_036.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="673" data-original-width="1600" height="268" src="https://1.bp.blogspot.com/-I81X5sr9-pk/XoLLwSB0awI/AAAAAAABarQ/ud-HWpW_5SEfkcnVPbtd6j4AlQfsiuRZQCEwYBhgLKs0DAMBZVoAk-scUxUKZUW86ZnMK07xNZTM_i8-aHjJP7D2GtEnvuhb8zetgxhCuESXpckXH2IEGDgY-_vr5aooGHNq3zaW77_AtviQAA9rzOfWkp0aWnvvlXe70p_1OqHCWiCpivJ0TBhi8dxRnDPh9rckZKlbmaeB8zklnhCWqPefmtc6jsMVU0dwGiBBVrnuEJSPOgSXzgLdNWa7ifEKiULp16nYGMOs65a_N-a7NDITwJqJMwc5FymwJ_AvEv1BmGjtDtcBjXvKts1sSUP1SdNoRAISIvsgoV57n5iKw0AEn3j4HEltTfBvCG6TaS5DF46KLOiBuo-_FLkyoQ7IBI9tx5liM2FN9shqpLUrbawd1hIhVEo579BMElU_7x2gJe3riotV9QVVg4ckwn2uSpj4LFC2tsKfvrk4n11HF5fmpFpZUjTQXaSXDq_-CQg2JDhLqYSZyJbo5jjyhixdq0OgTIktwjPERjpsS9y2u4SkhFT7BzY2DMBZxLvQQJHH-Esd_bouCYkjI8KOrUKSPymErOaQR6yHS7aHb0quvUv7fUaqfb3AkkD4q--YE2dgXco8lzRH6rS793GWNh-BuLVj6xqqXTp3PRPaRKB0wjJ6L9AU/s640/Selection_036.png" width="640" /></a></div>
<div style="text-align: justify;">
Filters the 5 countries with the largest number of deceased and plots the data over time.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://1.bp.blogspot.com/-q7YlsJWu7YA/XoLLw7k3Y4I/AAAAAAABarQ/20DK79owBQMzu3Yf5cdEBq48GVqRitsLgCEwYBhgLKs0DAMBZVoDj9Rew7gr6K3fCaowiYppclx8BZ3MpdzoajQ5HqYwBUEiMaJZQsI3iR_ntTZiFrZmTI_QxkG8tHf3Xmiqft06VLOvga2ILza85DZ0GSiuFYYLo6KyIygvwalCctQ12SP1SBUuoEIwJ4oZphDukCgXHe8tmYPDKjrtFI3w47bb2CKTXdIl8cuxRU0aE6n_dDUBF49qclShiDGpRoh7hBiNPfaH8E2dCsBqlPgMMBh8adRACp5nucmj67xtuMwJfLSJBlzmAFku-bxbIZH7MGXkcLIiTxOr7S9xWC2NYkICRiez2mEgnwR9OyTMhYLXDusvQnSlwI_RdQkR17owy_4mMSZHbGMNZYNrxqS2qIUEK25suIO2GEgqGqeCcJNKt54vgDANaxWt_ZOC7Ur9Z3ajJt05nc4iwBZ-JHc_tsxaLe9-pgVjYE0XP9T50ZuuUEBaF219Un67N7c7_NzXVqB3e8lRMq9vjNfhySXmmcAWp6U6JAJTzyDCIpp6Eg8AUKi9OkfpBCu92ohdV6oHIspv1NPCcsQyHhoKgSypfgYjnTXyg6nj65oVbj3Bu79N1u_hEqqrxbz-MueAultYGEvFLAwvDpA3VplowoZ6L9AU/s1600/Selection_037.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1250" data-original-width="1600" height="498" src="https://1.bp.blogspot.com/-q7YlsJWu7YA/XoLLw7k3Y4I/AAAAAAABarQ/20DK79owBQMzu3Yf5cdEBq48GVqRitsLgCEwYBhgLKs0DAMBZVoDj9Rew7gr6K3fCaowiYppclx8BZ3MpdzoajQ5HqYwBUEiMaJZQsI3iR_ntTZiFrZmTI_QxkG8tHf3Xmiqft06VLOvga2ILza85DZ0GSiuFYYLo6KyIygvwalCctQ12SP1SBUuoEIwJ4oZphDukCgXHe8tmYPDKjrtFI3w47bb2CKTXdIl8cuxRU0aE6n_dDUBF49qclShiDGpRoh7hBiNPfaH8E2dCsBqlPgMMBh8adRACp5nucmj67xtuMwJfLSJBlzmAFku-bxbIZH7MGXkcLIiTxOr7S9xWC2NYkICRiez2mEgnwR9OyTMhYLXDusvQnSlwI_RdQkR17owy_4mMSZHbGMNZYNrxqS2qIUEK25suIO2GEgqGqeCcJNKt54vgDANaxWt_ZOC7Ur9Z3ajJt05nc4iwBZ-JHc_tsxaLe9-pgVjYE0XP9T50ZuuUEBaF219Un67N7c7_NzXVqB3e8lRMq9vjNfhySXmmcAWp6U6JAJTzyDCIpp6Eg8AUKi9OkfpBCu92ohdV6oHIspv1NPCcsQyHhoKgSypfgYjnTXyg6nj65oVbj3Bu79N1u_hEqqrxbz-MueAultYGEvFLAwvDpA3VplowoZ6L9AU/s640/Selection_037.png" width="640" /></a></div>
<div style="text-align: justify;">
Filters the 8 countries with the largest number of deceased, plots the data and creates a grid layout.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
</div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://1.bp.blogspot.com/-5PkZd9YKNdQ/XoLLxYhsiBI/AAAAAAABarU/DwnIWclx32kOwwYZmxYWJHRZI9VyurY5ACEwYBhgLKs0DAMBZVoDVzgGgRIQ8jtniNuOInPUllJWv9poXXOuOqDjP3gZe_x8xXWnD7PYehD8otPzqVPfikzTt5vpGuPEV5W_BjRe0BrKoDJ6Ng1RsIN2qeZE-XBLVpvPsp4tvwYEmFkeUTgFKlKk8dvs9k4wgACVAqwFuFAOUG_a1wcuXK1QJ6en8zuJeGL22XttBk27BJXLg0I-R8doYYLDtacYFZYanSIHdLg-iy7YMhSEsUzU1tCIAdAzgkrNXLjw1_66LtOF_YMDfIlEjtDgkM20RwBK_t1pwopBKjAx7mXWdnLfNPnYZyU9NxwkRAFZbYbPucjkzRTv2H18D8WhPxDjzcN6VuChurnX3TZ_LFK3Ze718v6qH7nzqKQtnL8P6iCCMm5YyuSs8WdVqKS6VjV6QhafwcTQco_EFHnLg5PKqy_p80ujXuU_FNAa92HLsalzuXlilwyFVIULSgaxKY8G7mTtTOVZXgV9uijJ_8nU3Fs2HRLQVEvzo_mdfqCsdK6iWcyVjLT6kCWisLa4SOdxn5nyQd2fL2huYcDH8S3Nl8NZPbRNh6pjmLMaLlzal40W7Zrhkc6or07QLkPONgduO3V2QnEasOHyMAi9lUSUwtZ6L9AU/s1600/Selection_038.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="809" data-original-width="1600" height="322" src="https://1.bp.blogspot.com/-5PkZd9YKNdQ/XoLLxYhsiBI/AAAAAAABarU/DwnIWclx32kOwwYZmxYWJHRZI9VyurY5ACEwYBhgLKs0DAMBZVoDVzgGgRIQ8jtniNuOInPUllJWv9poXXOuOqDjP3gZe_x8xXWnD7PYehD8otPzqVPfikzTt5vpGuPEV5W_BjRe0BrKoDJ6Ng1RsIN2qeZE-XBLVpvPsp4tvwYEmFkeUTgFKlKk8dvs9k4wgACVAqwFuFAOUG_a1wcuXK1QJ6en8zuJeGL22XttBk27BJXLg0I-R8doYYLDtacYFZYanSIHdLg-iy7YMhSEsUzU1tCIAdAzgkrNXLjw1_66LtOF_YMDfIlEjtDgkM20RwBK_t1pwopBKjAx7mXWdnLfNPnYZyU9NxwkRAFZbYbPucjkzRTv2H18D8WhPxDjzcN6VuChurnX3TZ_LFK3Ze718v6qH7nzqKQtnL8P6iCCMm5YyuSs8WdVqKS6VjV6QhafwcTQco_EFHnLg5PKqy_p80ujXuU_FNAa92HLsalzuXlilwyFVIULSgaxKY8G7mTtTOVZXgV9uijJ_8nU3Fs2HRLQVEvzo_mdfqCsdK6iWcyVjLT6kCWisLa4SOdxn5nyQd2fL2huYcDH8S3Nl8NZPbRNh6pjmLMaLlzal40W7Zrhkc6or07QLkPONgduO3V2QnEasOHyMAi9lUSUwtZ6L9AU/s640/Selection_038.png" width="640" /></a></div>
<div style="text-align: justify;">
Trend plot.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
If you're interested you can download the OTLs and an example Hip file from <a data-saferedirecturl="https://www.google.com/url?q=https://drive.google.com/file/d/1FLar2ABdP-WpSLrkXmM1mVdw0zoisePr/view?usp%3Dsharing&source=gmail&ust=1585716337262000&usg=AFQjCNE1AIPG8CdpwhG0_uyfOK-K4xLUjg" href="https://drive.google.com/file/d/1FLar2ABdP-WpSLrkXmM1mVdw0zoisePr/view?usp=sharing" target="_blank">HERE</a>.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Leave comments , opinions and suggestions.</div>
<div style="text-align: justify;">
Thank you for reading.</div>
<div style="text-align: justify;">
</div>
Unknownnoreply@blogger.com0Sunnyvale, CA, USA37.36883 -122.036349637.166990500000004 -122.35907309999999 37.5706695 -121.7136261tag:blogger.com,1999:blog-5626905874114803923.post-10560325621827790392019-03-06T18:20:00.030-08:002021-07-11T22:26:33.204-07:00OpenCL Notes<div style="text-align: justify;">
<span style="font-size: small; font-weight: normal;">If you're interested in OpenCL please please watch <a href="https://vimeo.com/241568199" target="_blank">Houdini 16.5 Masterclass | OpenCL by Jeff Lait</a>. </span></div>
After that, I'm sure you'll find most of the stuff I'll write in this post redundant. I'm writing this post mostly as a note to myself with code snippets (cause my memory sucks) and I thought you guys might find it useful as well. <br /> <br /> On top of that, I'd like to have your opinion. Please correct me if I say or write something wrong, or you know a better way. I'm really looking forward to learn. <br /><br /> I'll keep updating this same post with new code snippets periodically, so stay tuned.<div>
<div style="text-align: justify;">
<br />
<h3>
<span style="font-size: x-large;"><b>VECTOR ATTRIBUTES</b></span></h3>
<br />
Ok let's start with an example:</div>
<div style="text-align: justify;">
I want to move all the points in my geometry up , in the positive y direction , by 1 unit.</div>
<div style="text-align: justify;">
<br />
<b>This is how you do it in VEX ...</b></div>
<ul>
<li style="text-align: justify;">drop a Point Wrangle node</li>
<li style="text-align: justify;">connect your geometry to input 1</li>
<li style="text-align: justify;">write the following code in the parameter VEXpression:</li>
</ul>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://3.bp.blogspot.com/-3W0D0PnSNfg/XH7Q2AdrGqI/AAAAAAABH1Y/Wd2pTVM6u7cYPzilw46kg7MTnNdTsZNfgCLcBGAs/s1600/Screen%2BShot%2B2019-03-05%2Bat%2B11.40.57%2BAM.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="75" data-original-width="168" src="https://3.bp.blogspot.com/-3W0D0PnSNfg/XH7Q2AdrGqI/AAAAAAABH1Y/Wd2pTVM6u7cYPzilw46kg7MTnNdTsZNfgCLcBGAs/s1600/Screen%2BShot%2B2019-03-05%2Bat%2B11.40.57%2BAM.png" /></a></div>
<div style="text-align: justify;">
<br /></div>
</div>
<div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<b>... now in OpenCL:</b></div>
</div>
<div>
<br />
<ul>
<li style="text-align: justify;">drop an OpenCL node</li>
<li style="text-align: justify;">go in the bindings Tab and set it up as follows. The purpose of this tab is make geometry attributes available to the GPU. Differently from Wrangle nodes, in order to access the attributes you've to bind whatever attribute you want to read / write to a variable. In this case we bound the attribute P to the openCL variable P ( I could have chosen a different name, like for instance 'ciccio').<br /><b>It's important to note that the OpenCL node ny default will loop over every element of the first parameter appearing in the Binding Tab (as specified in the 'Options' Tab) . So , in this case, will loop over P (<u>just like a Point Wrangle</u>) !</b></li>
</ul>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://1.bp.blogspot.com/-tj-BvswcEmc/XH7SW5SUrFI/AAAAAAABH1k/oKM6GZBvDwkLZTrXIbt0OGYXcqxQkJc_QCLcBGAs/s1600/Screen%2BShot%2B2019-03-05%2Bat%2B11.46.43%2BAM.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="348" data-original-width="716" height="308" src="https://1.bp.blogspot.com/-tj-BvswcEmc/XH7SW5SUrFI/AAAAAAABH1k/oKM6GZBvDwkLZTrXIbt0OGYXcqxQkJc_QCLcBGAs/s640/Screen%2BShot%2B2019-03-05%2Bat%2B11.46.43%2BAM.png" width="640" /></a></div>
<div>
<ul>
<li style="text-align: justify;">go in the Kernel Tab and check the parameter 'Use Code Snippet'. This will allow you to write code directly on this node, opposed to point the node to a file containing the code.</li>
<li style="text-align: justify;">now press the button "Generate Kernel". A window will pop up with some code. Copy the content of this window, close it and paste it in the parameter 'Kernel Code'.</li>
<li style="text-align: justify;">now modify the code as follows:</li>
</ul>
</div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://4.bp.blogspot.com/-kg1pK2QKusk/XH7N9J7WQQI/AAAAAAABH1M/vEO9tu6cIQQImbRYfD5W5gqNRKFkA_7rQCLcBGAs/s1600/Screen%2BShot%2B2019-03-05%2Bat%2B11.28.10%2BAM.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="220" data-original-width="369" height="190" src="https://4.bp.blogspot.com/-kg1pK2QKusk/XH7N9J7WQQI/AAAAAAABH1M/vEO9tu6cIQQImbRYfD5W5gqNRKFkA_7rQCLcBGAs/s320/Screen%2BShot%2B2019-03-05%2Bat%2B11.28.10%2BAM.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
You should see your geometry moving up by 1 unit.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Let's translate the OpenCL code above in English:</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<span face=""arial" , "helvetica" , sans-serif">kernel void KernelName ( int P_length, global float * P )</span></div>
<div class="separator" style="clear: both; text-align: justify;">
<i>Dear Houdini, please define a kernel function that I'll submit to the GPU if I press CTRL+Enter. Along with this function I'll deliver two pieces of data : one is a long list of numbers (float *) that is coming from the input connection (global) and it's called <b>P</b> (between me and you, this is a list of 3 numbers at a time, cause it's a list of vectors, but you don't have to worry about that for now). The other one is just an integer number (int), named <b>P_length</b> and will contain the length of the list P, so we know when we run out of elements.</i></div>
<div class="separator" style="clear: both; text-align: justify;">
<i><br /></i></div>
<blockquote class="tr_bq" style="clear: both; text-align: justify;">
<span style="font-family: inherit;">{</span></blockquote>
<div class="separator" style="clear: both; text-align: justify;">
<i>...cool, let's start looping over <b>P</b> using <b>idx</b> as index</i></div>
<div class="separator" style="clear: both; text-align: justify;">
<i><br /></i></div>
<blockquote class="tr_bq" style="clear: both; text-align: justify;">
<span face=""arial" , "helvetica" , sans-serif">int idx = get_global_id(0);</span> </blockquote>
<blockquote class="tr_bq" style="clear: both; text-align: justify;">
<span face=""arial" , "helvetica" , sans-serif">if (idx >= P_length)</span> </blockquote>
<blockquote class="tr_bq" style="clear: both; text-align: justify;">
<span face=""arial" , "helvetica" , sans-serif"> return;</span></blockquote>
<div class="separator" style="clear: both; text-align: justify;">
<i>To start, kindly put the index of the data you're currently looping over in the variable <b>idx</b>. I know you'll be increasing idx at every iteration, but please if you run out of elements to loop over (if (idx>=P_length)...) , just stop (return) so we don't get one of those annoying crashes.</i></div>
<div class="separator" style="clear: both; text-align: justify;">
<i><br /></i></div>
<blockquote class="tr_bq" style="clear: both; text-align: justify;">
<span face=""arial" , "helvetica" , sans-serif">float3 pos = vload3(idx, P);</span></blockquote>
<div class="separator" style="clear: both; text-align: justify;">
<i>... fetch the current vector <b>P</b> in a variable called <b>pos</b> ...</i></div>
<div class="separator" style="clear: both; text-align: justify;">
<i><br /></i></div>
<blockquote class="tr_bq" style="clear: both; text-align: justify;">
<span face=""arial" , "helvetica" , sans-serif">pos.y += 1;</span></blockquote>
<div class="separator" style="clear: both; text-align: justify;">
<i>... now take whatever number was in the component y of the vector <b>pos</b> , and add 1 to it. Then store it back into <b>pos</b>.</i></div>
<div class="separator" style="clear: both; text-align: justify;">
<i><br /></i></div>
<blockquote class="tr_bq" style="clear: both; text-align: justify;">
<span face=""arial" , "helvetica" , sans-serif">vstore3 (pos, idx, P);</span></blockquote>
<div class="separator" style="clear: both; text-align: justify;">
<i>... and finally replace the current <b>P</b> with the content of <b>pos</b>.</i></div>
<div class="separator" style="clear: both; text-align: justify;">
<i><br /></i></div>
<blockquote class="tr_bq" style="clear: both; text-align: justify;">
<span style="font-family: inherit;">}</span></blockquote>
<div class="separator" style="clear: both; text-align: justify;">
<i>... let's go to the next element in P and start a new loop iteration.</i></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<h3>
<b><span style="font-size: large;"><br /></span></b></h3>
<h3>
<b><span style="font-size: x-large;">ARRAY ATTRIBUTES</span></b></h3>
</div>
<div>
<b>Example:</b></div>
<div>
I want to put the number -100 in the 3rd element of the array attribute <b>A</b> attached to point with @ptnum 14.</div>
<div>
<br /></div>
<div><b>
In VEX:</b></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<a href="https://3.bp.blogspot.com/-btKZdgNUo04/XH8sJmMDy-I/AAAAAAABH2U/nuGLilJSLr8rfwamlEir36PmbC5sSG7NgCLcBGAs/s1600/Screen%2BShot%2B2019-03-05%2Bat%2B6.10.29%2BPM.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="40" data-original-width="156" src="https://3.bp.blogspot.com/-btKZdgNUo04/XH8sJmMDy-I/AAAAAAABH2U/nuGLilJSLr8rfwamlEir36PmbC5sSG7NgCLcBGAs/s1600/Screen%2BShot%2B2019-03-05%2Bat%2B6.10.29%2BPM.png" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<b><br /></b></div>
<div class="separator" style="clear: both; text-align: left;"><b>
In OpenCL:</b></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<a href="https://4.bp.blogspot.com/-FcdBJmpvjeE/XH8svqEGynI/AAAAAAABH2c/CwkLyc4jpv0G7yYX5TOutyk5U85xt2-IQCLcBGAs/s1600/Screen%2BShot%2B2019-03-05%2Bat%2B6.12.59%2BPM.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="249" data-original-width="309" src="https://4.bp.blogspot.com/-FcdBJmpvjeE/XH8svqEGynI/AAAAAAABH2c/CwkLyc4jpv0G7yYX5TOutyk5U85xt2-IQCLcBGAs/s1600/Screen%2BShot%2B2019-03-05%2Bat%2B6.12.59%2BPM.png" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Why ?</div>
<div class="separator" style="clear: both; text-align: left;">
Well, let's pretend we have this array point attribute named <b>A</b> :</div>
<div>
<div>
<br /></div>
<div>
in Vex the syntax would be </div>
<div>
<b><span face=""arial" , "helvetica" , sans-serif">i[]@A</span></b></div>
<div>
<b><span face=""arial" , "helvetica" , sans-serif"><br /></span></b></div>
<div>
<span style="font-family: inherit;">and this is how we're used to see this data in the Geometry Spreadsheet</span><span style="font-family: inherit;">:</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div class="separator" style="clear: both;">
<a href="https://3.bp.blogspot.com/-o5oN6teOsSQ/XH8W_650T0I/AAAAAAABH1w/rvFO11B0E-A1QXOvqGjJTk1s3yqJttx9ACLcBGAs/s1600/Screen%2BShot%2B2019-03-05%2Bat%2B4.40.11%2BPM.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="183" data-original-width="366" height="160" src="https://3.bp.blogspot.com/-o5oN6teOsSQ/XH8W_650T0I/AAAAAAABH1w/rvFO11B0E-A1QXOvqGjJTk1s3yqJttx9ACLcBGAs/s320/Screen%2BShot%2B2019-03-05%2Bat%2B4.40.11%2BPM.png" width="320" /></a></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
in OpenCL all the arrays (one per point) are concatenated and since each array can potentially have an arbitrary length, we need a way to identify where the array for each point begins, in this long list. That's exactly what <b>A_index</b> is for.</div><div><br /></div><div>A_index[ptnum] contains the beginning of the array for the point ptnum.</div><div><br /></div><div>So, the code to find the beginning of the array for the point 14 is the following:</div>
<div>
<br /></div>
<div>
<b><i>int mem_loc = A_index[14]</i></b></div>
<div>
<b><br /></b></div>
<div>
and then count 3 elements from that memory location, like so :</div>
<div>
<br /></div>
<div>
<b><i>int element = A[mem_loc+3]</i></b></div>
</div>
<div><b><br /></b></div><div>Furthermore, if you want to know the length of the array for the point <i>ptnum</i>, this is the code:</div><div><br /></div><div>
<span style="background-color: white; color: #222222; font-family: Arial, Helvetica, sans-serif; font-size: small;"><b><i>arraylength = A_index[ptnum+1] - A_index[ptnum]</i></b></span><b><br /></b>
<b><br /></b></div>
<div>
<h3>
<b><span style="font-size: x-large;">VOLUMES</span></b></h3>
<div>
Let's say we have a <i>vector</i> grid called "vel".</div>
<div>
First off, make sure this is a <u>Volume Grid</u> (not a VDB). (If you find a way to pass VDBs into OpenCL please I'd love to know how).</div>
<div>
In other words we're going to need 3 float grids (<b>vel.x, vel.y, vel.z</b>).</div>
<div>
In the example below, I chose to add the Voxel Resolution, because I wanted to know weather or not I was outside of the grid boundaries, but it's not strictly necessary.</div>
<div>
<b><span style="font-size: large;"><br /></span></b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-0D0rD_eCyaQ/XRVdYKZhhyI/AAAAAAABK-0/vHn_YZlx92g5ZurOLEJv1L_VhmOLg3xFgCLcBGAs/s1600/Screenshot%2Bfrom%2B2019-06-27%2B17-20-25.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="336" data-original-width="490" height="219" src="https://1.bp.blogspot.com/-0D0rD_eCyaQ/XRVdYKZhhyI/AAAAAAABK-0/vHn_YZlx92g5ZurOLEJv1L_VhmOLg3xFgCLcBGAs/s320/Screenshot%2Bfrom%2B2019-06-27%2B17-20-25.png" width="320" /></a></div>
<br /><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div>
When you import a volume grid , for instance in this case it's called 'vel.x', this will result in this code in the Kernel:<br />
<br />
...<br />
<i>int <b>velx_stride_x</b>, </i><br />
<i>int <b>velx_stride_y</b>, </i><br />
<i>int <b>velx_stride_z</b>, </i><br />
<i>int <b>velx_stride_offset</b>, </i><br />
<i>global float * <b>velx</b> ,</i><br />
...<br />
<br />
If you select "Voxel Resolution" for the grid 'vel.x', this will result in this code in the Kernel:<br />
<br />
...<br />
<i>int <b>velx_res_x</b>, </i><br />
<i>int <b>velx_res_y</b>, </i><br />
<i>int <b>velx_res_z</b>, </i><br />
<i>...</i><br />
<br />
<div>
If you select "Volume Transform to Voxel" for the grid 'vel.x', this will result in this code in the Kernel:</div>
<div>
<br /></div>
<div>
...</div>
<i>float16 <b>velx_xformtovoxel</b>, </i><br />
...<br />
(note this is a 4x4 matrix)<br />
<br />
<br />
<span style="font-size: large;"><b>Access voxels from a world position</b></span><br />
If you want to find the 3 voxel indices starting from a world position <b>P</b>, here's how (this was kindly described in the Masterclass mentioned above).<br />
<br />
// first off we need to fetch pos from the P attribute ....<br />
<i>float3 pos = vload3(idx,P);</i><br />
<br />
// now this suuuuper weird formula to find the volume indice<br />
<i>float4 voxelpos = pos.x * velx_xformtovoxel.lo.lo + pos.y * velx_xformtovoxel.lo.hi + pos.z * velx_xformtovoxel.hi.lo + 1 * velx_xformtovoxel.hi.hi;</i><br />
<br />
// and of course the indices must be integers so ...<br />
<div style="text-align: justify;">
// NOTE : if you are not 100% sure P is within the grid boundaries you better modify this code to make sure you're not trying to fetch a non existing voxel (Houdini might very easily crash if you do). In this case let's pretend I'm sure P is within the volume boundaries.</div>
<i>int x = (int)(floor(voxelpos.x));</i><br />
<i>int y = (int)(floor(voxelpos.y));</i><br />
<i>int z = (int)(floor(voxelpos.z));</i><br />
<br />
// and finally we can access the content of the voxel with indices (x,y,z)<br />
<i>float vx = velx[ velx_stride_offset + velx_stride_x * x + velx_stride_y * y + velz_stride_z * z ];</i></div><div><i><br /></i></div><h1 style="text-align: left;"><b><span style="font-size: x-large;">Detail Attributes</span></b></h1><div><b>Important :</b></div><div>You need to create the detail attribute that you want to populate in advance, meaning before the OpenCL node, using Attribute Create SOP or Attribute Wrangle SOP.</div><div><br /></div><div><br /></div><div><h3><u><span style="font-size: small;">detail int/float attribute</span></u></h3></div><div><b>VEX</b></div><div><i>i@myattrib1 = 12;</i></div><div><i>f@myattrib2 = 23.4;</i></div><div><br /></div><div><b>OpenCL</b></div><div><a href="https://lh3.googleusercontent.com/-4cAsfeC7IVQ/YOvBIiFG8dI/AAAAAAABpR8/qcaCeKAMMbcSSZf0_veXyQHI0-TafEEEwCLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="436" data-original-width="713" height="196" src="https://lh3.googleusercontent.com/-4cAsfeC7IVQ/YOvBIiFG8dI/AAAAAAABpR8/qcaCeKAMMbcSSZf0_veXyQHI0-TafEEEwCLcBGAsYHQ/image.png" width="320" /></a></div><div><br /><br /></div><h3 style="text-align: left;"><span style="font-size: small;"><br /></span></h3><h3 style="text-align: left;"><span style="font-size: small;"><br /></span></h3><h3 style="text-align: left;"><span style="font-size: small;"><br /></span></h3><br /><b><br /></b><div><b><br /></b></div><div><b><br /></b></div><div><b><br /></b></div><div><b><br /></b></div><div><b>Note:</b><br />The variable myattrib_length returns the number of elements. If this was a point attribute it would be the number of points. Since detail attributes are only one attribute per geometry, for detail attributes this variable is always 1.</div><div><br /><h3 style="text-align: left;"><u><span style="font-size: small;">detail vector attribute</span></u></h3><div><b>VEX</b></div><div><i>v@myattrib = set(12.0, 54.0, 34.0);</i></div><div><br /></div><div><b>OpenCL</b></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/-O-j1wgjNcCw/YOu_Nt4RrLI/AAAAAAABpR0/vyqyPgDGeYURJ4siPKkI3M9lfjO2ClOAwCLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="459" data-original-width="723" height="203" src="https://lh3.googleusercontent.com/-O-j1wgjNcCw/YOu_Nt4RrLI/AAAAAAABpR0/vyqyPgDGeYURJ4siPKkI3M9lfjO2ClOAwCLcBGAsYHQ/image.png" width="320" /></a></div><br /><br /></div><div><br /></div><div><br /></div><div><br /></div><div><div><br /></div>
<i><br /></i>
</div><div><i><br /></i></div><div><i><br /></i></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><h3><u><span style="font-size: small;">detail int/float array attribute</span></u></h3><div><b>Important :</b></div><div>OpenCL cannot resize an array. So the array size must be pre-allocated before the OpenCL node. You can use an Attribute Create SOP or Attribute Wrangle SOP to set the size of the array in advance.</div><div><br /></div><div><b>VEX</b></div><div><i>f[]@myattrib = {12.0, 34.2, -4.2, 3};</i></div><div><br /></div><div><b>OpenCL</b></div><div>First off make sure to initialize the detail array attribute with the desired length using , for instance, an Attribute Wrangle SOP. The code below creates an array attribute with length 4:</div><div><br /></div><div><i>f[]myattrib;</i></div><div><i>resize(@myattrib, 4);</i></div><div><i><br /></i></div><div>then you can finally append the OpenCL node:</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/-6gPJ-sd86gM/YOvLy938S5I/AAAAAAABpSQ/R6JMqC9tBDUb2xWqSVUFenRCe2WdrrdlwCLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="442" data-original-width="706" height="200" src="https://lh3.googleusercontent.com/-6gPJ-sd86gM/YOvLy938S5I/AAAAAAABpSQ/R6JMqC9tBDUb2xWqSVUFenRCe2WdrrdlwCLcBGAsYHQ/image.png" width="320" /></a></div><br /><br /></div><h3><span style="font-size: small;"><br /></span></h3><h3><span style="font-size: small;"><br /></span></h3><h3><span style="font-size: small;"><br /></span></h3><h3><span style="font-size: small;"><br /></span></h3><h3><span style="font-size: small;"><br /></span></h3><h3><span style="font-size: small;"><br /></span></h3><div><span style="font-size: small;"><br /></span></div><div><span style="font-size: small;"><br /></span></div><b>Note:</b><br />The variable myattrib_index is created in OpenCL by default every time there's an array attribute. This variable makes more sense when the array attribute is a point , vertex, or prim attribute, cause in that case every point has his own array, and in this case myattrib_index contains the beginning of the array for each point, vertex or prim. In the case of detail attributes there's only one array, hence myattrib_index is always 0, so can be ignored.<div><br /></div><div><br /></div><h3><u><span style="font-size: small;">detail string attribute</span></u></h3><div>Looking at the menu that provides the different options for the variable data type, I don't see a "string" option so I must conclude it's not supported.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/-gZBrTe6kQYI/YOvDjJrFaKI/AAAAAAABpSE/I6xOEKBafroH0dYJAqtuq3OiPUHt61d2ACLcBGAsYHQ/image.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="620" data-original-width="769" height="240" src="https://lh3.googleusercontent.com/-gZBrTe6kQYI/YOvDjJrFaKI/AAAAAAABpSE/I6xOEKBafroH0dYJAqtuq3OiPUHt61d2ACLcBGAsYHQ/image.png" width="298" /></a></div><br /><br /></div><div><br /></div><div><b><br /></b></div><div><b><br /></b></div><div><b><br /></b></div><div><b><br /></b></div><div><b><br /></b></div><div><b><br /></b></div><div><b><br /></b></div><div><br /></div><div><b><br /></b></div></div><div><i><br /></i></div><div><i><br /></i></div>
</div>Unknownnoreply@blogger.com4tag:blogger.com,1999:blog-5626905874114803923.post-44790985439027127022019-03-04T18:07:00.001-08:002019-03-05T08:44:28.678-08:00Sand without grains - PART 2 - VEX Implementation<span style="text-align: justify;">I'll assume you've read </span><a href="https://pepefx.blogspot.com/2018/03/sand-without-grains-abelian-sandpile.html" style="text-align: justify;" target="_blank">PART 1</a><span style="text-align: justify;"> so you've a good grasp of this super simple algorithm.</span><br />
<div style="text-align: justify;">
</div>
<div style="text-align: justify;">
<br />
<div style="text-align: justify;">
Oh, and in case you're not familiar with HeightFields or Vex , I recommend to watch these two amazing and life changing Masterclasses first.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<a href="https://vimeo.com/234755478" target="_blank">HeightFields Masterclass by Jeff Lait</a></div>
<div style="text-align: justify;">
<a href="https://www.youtube.com/watch?v=sz6sqZCRWCA" target="_blank">Wrangle Node Workshop by Ari Danesh</a></div>
<br /></div>
<div style="text-align: justify;">
Let's quickly recap from PART 1 the ingredients:</div>
<ul style="text-align: justify;">
<li>the ideal data structure for this algorithm is a 2 dimensional grid.</li>
<li>we need 2 grids: one to store the height, one to store the 'toppling' direction</li>
</ul>
<div style="text-align: justify;">
Now there is one data structure in Houdini that happens to be perfect for what we need :</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<b> HeightFields (HF)</b></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
For those who don't know what HeightFields are in Houdini:</div>
<div style="text-align: justify;">
A heightfield is a grid, just instead of being a polygonal grid, it's a volumetric grid. To be more precise it's a 2d Volume. Each voxel of the grid contains a floating point number that corresponds to the <b>height </b>of the visual representation of that voxel.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Let's get familiar with HF first in Houdini.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
In a SOP context drop a HeightField SOP node , set size to 100 , 100 and middle click on it to check the info.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://1.bp.blogspot.com/-4kSi60cj6l8/W_neCMNtXcI/AAAAAAABDbU/BO2g15tYxHEH0zQTySFX9o8XaGqVFV-yACLcBGAs/s1600/Selection_002.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="381" data-original-width="1472" height="164" src="https://1.bp.blogspot.com/-4kSi60cj6l8/W_neCMNtXcI/AAAAAAABDbU/BO2g15tYxHEH0zQTySFX9o8XaGqVFV-yACLcBGAs/s640/Selection_002.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
You can see the node generates 2 volumes: 'height' and 'mask'.</div>
<div style="text-align: justify;">
<i>Please note that even if there are 2 volumes here, in the view port you'll see only the 'height' volume. Mask volume is pretty much identical to Height , but is invisible and in the view port is used to color the Height volume in red for those Mask voxels bigger than 0. Mask is used to select areas of the Height volume to perform arbitrary HF operations. We will use Mask as a debugging tool.</i></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
In order to get familiar with these new concepts let's now drop a Volume Wrangle and enter the following code in the "VEXpression" field.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
</div>
<div style="text-align: justify;">
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://1.bp.blogspot.com/--ZlJGiufXKA/W_nfrpotPII/AAAAAAABDbo/RWipyufEd9kyJeSSiNXfYbZJtokdw7zowCLcBGAs/s1600/Selection_002.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="421" data-original-width="866" height="308" src="https://1.bp.blogspot.com/--ZlJGiufXKA/W_nfrpotPII/AAAAAAABDbo/RWipyufEd9kyJeSSiNXfYbZJtokdw7zowCLcBGAs/s640/Selection_002.png" width="640" /></a></div>
<br /></div>
<div style="text-align: justify;">
You'll notice that all the voxels that lay in the negative X plane (v@P.x < 0) are now 20 units taller (f@height=20). And you'll notice that all the voxels in the negative Z plane (v@P.z < 0) are now red (f@mask=1). <br />
<br />
Remember that every Wrangle node is really a loop that iterates over all the specific elements the wrangle node is set to. In the case of a Point Wrangle the loop will iterate over all the points. In our case, Volume Wrangle , the loop will iterate over all the <b>voxels</b>. We don't have to write any code for the loop to happen, we only have to write the code for what happens for each iteration. So when I say 'current voxel' what I mean is 'the voxel that is currently being looped over'.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://2.bp.blogspot.com/-6AeSnNodDX4/W_nfbsw53GI/AAAAAAABDbg/RUK-Al5zaiAxZO3dVi0mdSj1ev5oYz6IwCLcBGAs/s1600/Selection_001.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="999" data-original-width="1600" height="248" src="https://2.bp.blogspot.com/-6AeSnNodDX4/W_nfbsw53GI/AAAAAAABDbg/RUK-Al5zaiAxZO3dVi0mdSj1ev5oYz6IwCLcBGAs/s400/Selection_001.png" width="400" /></a></div>
<br />
<br /></div>
<div style="text-align: justify;">
In this example we used the voxel World position (<b>v@P</b>) to choose where to change Height or Mask. This means that if you translate or rotate the grid before the Wrangle node, the result will 'slide'.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
What if we want the result to be independent from the grid position / orientation ?<br />
Well, we'll need to use a different way to address the voxels : <b>indexing </b>!<br />
The Volume Wrangle node provides several pre-built attributes (v@P is one of those). The ones we want to use now are <b>i@ix</b>, and <b>i@iy</b> (we'll ignore i@iz since we're dealing with a 2d volume, so we'll only consider the components x and y , and always assume the z component as 0).<br />
<b>i@ix</b> and <b>i@iy</b> contain the voxel index. Just what we need.<br />
Ok let's drop a different Volume Wrangle in a new branch and write this code in the VEXpression field.<br />
<br />
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://1.bp.blogspot.com/-IovNr-ajyKU/W_nhUxoAelI/AAAAAAABDb0/iJAEUMvRfCkavu8SH2R-5YuKy3ud4g6lACLcBGAs/s1600/Selection_003.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="334" data-original-width="1005" height="212" src="https://1.bp.blogspot.com/-IovNr-ajyKU/W_nhUxoAelI/AAAAAAABDb0/iJAEUMvRfCkavu8SH2R-5YuKy3ud4g6lACLcBGAs/s640/Selection_003.png" width="640" /></a></div>
<br /></div>
<div style="text-align: justify;">
Since we set the grid to be 100x100 in the HeightField SOP node, with the code i@iy<50 I'm selecting only half of the voxels in the y component to change the Height to <b>20</b>, and only half of the voxels in the x component to change the Mask to 1.<br />
<br />
The result in the viewport did not change at all, but now the result of the Volume Wrangle is not affected by the grid location.<br />
You can test the difference between the two wrangle nodes dropping a Transform SOP for each branch before the Wrangle node, and trying to move the HeightField around.<br />
<br />
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://2.bp.blogspot.com/-KEczulS0Ap4/W_npy19TqtI/AAAAAAABDcE/9xKdwA-RzXEvnMuRjYakkLcM3dRux4X9gCLcBGAs/s1600/output.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="331" data-original-width="640" height="329" src="https://2.bp.blogspot.com/-KEczulS0Ap4/W_npy19TqtI/AAAAAAABDcE/9xKdwA-RzXEvnMuRjYakkLcM3dRux4X9gCLcBGAs/s640/output.gif" width="640" /></a></div>
<br />
Now let's try something silly, just because I want to prove a point.<br />
<br />
Let's attach a new Volume Wrangle to volumewrangle2 and let's try to color in red (using f@mask) all those voxels that are now higher.<br />
It should be easy since we just set Height to 20 for certain voxels. All we need to do is select all the voxels whose world position y is bigger than, say ... 10 ?<br />
<br />
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://1.bp.blogspot.com/-5WptYn8rhyM/W_ntFvr4BmI/AAAAAAABDcU/vvvrmsIj74sDNz7JTiIKNJxAF3eNepmSACLcBGAs/s1600/Selection_004.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="344" data-original-width="1600" height="137" src="https://1.bp.blogspot.com/-5WptYn8rhyM/W_ntFvr4BmI/AAAAAAABDcU/vvvrmsIj74sDNz7JTiIKNJxAF3eNepmSACLcBGAs/s640/Selection_004.png" width="640" /></a></div>
<br />
<br />
It didn't work !<br />
The reason is explained by the fact that we never changed the actual voxel position. We changed the data *contained* in voxels ('height'), but we never changed v@P.y (the y component of the voxel world position). Even if we wanted to change the voxel v@P.y, we wouldn't be able to do it using a Volume Wrangle node.<br />
If you think about that, it makes perfect sense : a volume is a grid. It wouldn't make sense to be able to move voxels around. If we were able to do so , we would lose the advantages that comes from being ... a grid (we'll see this advantage in a minute).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
I don't want to go too deep into HF (please make sure to watch the Masterclasses I mentioned before), but there are a couple of important facts to keep in mind:</div>
<ul>
<li><div style="text-align: justify;">
Even if you see a HeightHield appearing as a 3d object in the viewport , internally it is a 2d grid.</div>
</li>
<li><div style="text-align: justify;">
The reason cause you see a 3d object, is because the 'height' volume has a special visualizer that shows the 'height' content to visually y-displace the grid in the view port.</div>
</li>
<li style="text-align: justify;">The <b>y</b> component of a HF voxel world position *is not* 'height'.</li>
<li style="text-align: justify;">Voxels cannot be moved in space by a Volume Wrangle. Volume Wrangle can only change the data (for example 'height' or 'mask') contained in the voxel.</li>
<li style="text-align: justify;">Voxels can be moved in space with a Transform SOP or the HeightField SOP ('Center' parameter).</li>
<li style="text-align: justify;">HF voxels can be uniquely identified by 2d coordinates (x, y). In VEX this coordinate is { i@ix , i@iy , 0 }</li>
<li style="text-align: justify;">this is weird : the unit for the 'height' field is 10x bigger than Houdini Unit Length, which is 1 m by default. This means that in order to move a voxel up by 1 unit (or 1 meter) you've to set height = 0.1 (not 1).</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div style="text-align: justify;">
<br />
<br />
<br />
Ok enough about HeightFields. Let's dive into the sand algorithm.<br />
<br />
In PART 1 I named each cell 'stack'. Since we're now dealing with Volumes, I'll use the term 'voxel' to refer to the stacks.<br />
<br />
First off we need to create an environment that will allow us to play with HeightFields. We'll use the Mask volume (generated by the HeightField SOP) as a debugging tool, as we have already done previously.<br />
<br />
Cool, let's create a new Geo context, and name it "sandpile_VEX". Dive inside and create the following.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-MmhtDiXaJls/W_stvKsZp9I/AAAAAAABDgM/TLQRVuyUysI7-QPECQeIwriaIUUgi5mqwCLcBGAs/s1600/Selection_005.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="683" data-original-width="878" height="496" src="https://4.bp.blogspot.com/-MmhtDiXaJls/W_stvKsZp9I/AAAAAAABDgM/TLQRVuyUysI7-QPECQeIwriaIUUgi5mqwCLcBGAs/s640/Selection_005.png" width="640" /></a></div>
<br />
<b>heightfield1</b> node generates a 20x20 sized grid. Grid Spacing is 1, meaning that there is 1 voxel per unit. If we put 0.5 in there we would have twice the number of voxels. This parameter is a very quick way to increase the HF resolution.<br />
<br />
So we have a total of 400 voxels, and the volume indices are as follow:<br />
<br />
<b>i@ix = [ 0:19 ]</b><br />
<b>i@iy = [ 0:19 ]</b><br />
<br />
<b>heightfield_noise1</b> node modifies the 'height' field using a noise function. This is a quick way to create an initial sand distribution.<br />
In the view port you should see something similar to this.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-lNcOx3RR7eo/W_sylkGERGI/AAAAAAABDgY/wVah1ZVQWrg46_rpOPrp25Mf5iEVsG4aACLcBGAs/s1600/Selection_006.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1117" data-original-width="1178" height="378" src="https://2.bp.blogspot.com/-lNcOx3RR7eo/W_sylkGERGI/AAAAAAABDgY/wVah1ZVQWrg46_rpOPrp25Mf5iEVsG4aACLcBGAs/s400/Selection_006.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
In PART 1 we split the algorithm in 2 steps. Let's work on step 1.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<h2 style="clear: both; text-align: justify;">
STEP 1</h2>
<div>
For each voxel (or stack), find out if any of the 4 adjacent voxels is lower by a certain amount (let's say 1).</div>
<div>
<br /></div>
<div>
Drop a Volume Wrangle node and write the following code in the parameter 'VEXpression'</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-HMBYNkP4Ius/W_s-qG1ohfI/AAAAAAABDgw/7DcUZ5Ob7JsCqFOtOZdB_RYZiYmMWKokQCLcBGAs/s1600/Selection_008.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="420" data-original-width="1053" height="254" src="https://1.bp.blogspot.com/-HMBYNkP4Ius/W_s-qG1ohfI/AAAAAAABDgw/7DcUZ5Ob7JsCqFOtOZdB_RYZiYmMWKokQCLcBGAs/s640/Selection_008.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div>
<br /></div>
In the first line we set the topple_threshold. If the current voxel is taller than the adjacent voxels by a height value bigger than 'topple_threshold', it means the current voxel should topple. But don't worry, we'll use this variable later.<br />
<br />
Now, the biggest advantage of using volumes as a data structure is that I can find the adjacent voxels very very quickly. The grid position of the 'current voxel' is given by i@ix and i@iy. So if I want to access the voxel to the left, right , top or bottom of the current voxel all I need to do is add or subtract a 1 here and there.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-IxRJt1-dPP0/W_tnxgf4z2I/AAAAAAABDkc/KSZTEuecUUYnARiYxs1sqMRNv-3sKCIiwCLcBGAs/s1600/Sketch.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1215" data-original-width="1270" height="382" src="https://3.bp.blogspot.com/-IxRJt1-dPP0/W_tnxgf4z2I/AAAAAAABDkc/KSZTEuecUUYnARiYxs1sqMRNv-3sKCIiwCLcBGAs/s400/Sketch.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
The following 4 lines read the 'height' value from the 4 voxels adjacent to the current voxel.<br />
We'll ignore the voxels in the corners.<br />
<br />
Let's translate the following line in English :<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-jyV87u_Wu1I/W_tHYjkPWrI/AAAAAAABDg8/tanFactvTgAh5AfbNSyoEve5gN_ZbQy6wCLcBGAs/s1600/Selection_009.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="23" data-original-width="609" height="24" src="https://2.bp.blogspot.com/-jyV87u_Wu1I/W_tHYjkPWrI/AAAAAAABDg8/tanFactvTgAh5AfbNSyoEve5gN_ZbQy6wCLcBGAs/s640/Selection_009.png" width="640" /></a></div>
<br />
Dear Houdini, please read the content of the node attached the first input (@OpInput1), which I assume is a volume. But since there are 2 volumes flowing through that connection ('height' and 'mask') I want to be more specific : the volume I am interested in is named "height", but hey ... not the whole thing. No way. I want the content of one voxel, and more specifically the one sitting at the index ( ix+1 , iy , 0 ). Once you get that data contained in that voxel, please put it in a float variable named 'hxp'.<br />
<br />
<b>@OpInput1</b> is just a fancy way of saying "node input 0". You can write directly 0 in there, and it will still work perfectly. I prefer to use the fancy form cause it's more descriptive for my lazy brain.<br />
<br />
Ok now the variables hxp, hxn, hyp, hyn contain the height value of the 4 adjacent voxels. At the moment we're doing nothing with this data.<br />
<br />
Let's add some more Vex code:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-OUbLPgPVtFI/W_tQuzryRgI/AAAAAAABDjg/_crNwGoLqnMsdG_sGg0X1Zm2OUerW5BZwCLcBGAs/s1600/Selection_011.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="241" data-original-width="690" height="222" src="https://3.bp.blogspot.com/-OUbLPgPVtFI/W_tQuzryRgI/AAAAAAABDjg/_crNwGoLqnMsdG_sGg0X1Zm2OUerW5BZwCLcBGAs/s640/Selection_011.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<br />
The purpose of the new 4 lines is to flag which of the adjacent voxels is a candidate for the current voxel to topple towards to.<br />
<br />
Let's make an example:<br />
Let's assume the height of the current voxel (f@height) is 3.<br />
Let's assume the height of the adjacent voxel on the positive x (hxp) is 6.<br />
And we know that topple_threshold is 1.<br />
3 - 6 = -3<br />
is -3 bigger than 1 ? False . Then txp = False (or 0).<br />
Meaning, the current voxel cannot topple towards the adjacent voxel on the positive x, cause that voxel is actually taller ! Actually that voxel could topple towards the current, but that will be evaluated in some other iteration of the Volume Wrangle node loop.<br />
<br />
Another example:<br />
Let's assume the height of the current voxel (f@height) is 3.<br />
Let's assume the height of the adjacent voxel on the negative y (hyn) is 1.<br />
And we know that topple_threshold is 1.<br />
3 - 1 = 2<br />
is 2 bigger than 1 ? True . Then tyn = True (or 1).<br />
This means that the current voxel could topple towards the adjacent voxel on the negative y, cause it's lower than the threshold value.<br />
<br />
Once we collect all the values for txp, txn, typ, tyn we can put this into an array.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-petOV-As79E/W_togen66II/AAAAAAABDko/vBJEkWysyygwhEXBG6TMsVc4-nLQYegYwCLcBGAs/s1600/Selection_013.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="385" data-original-width="681" height="360" src="https://2.bp.blogspot.com/-petOV-As79E/W_togen66II/AAAAAAABDko/vBJEkWysyygwhEXBG6TMsVc4-nLQYegYwCLcBGAs/s640/Selection_013.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div>
<br /></div>
The line with hdiff[] is initializing the array to an array of 4 integers. All zeros to start.<br />
Then I'm replacing each element of the array with txp ... tyn, that we know can be only 0 or 1.<br />
<br />
Please note that in this moment we just established an important convention. Meaning that the first element in the array corresponds to the upper voxel (yp). The 3rd element of the array corresponds to the lower voxel (yn) and so on. We could have chosen a different convention of course, I just like starting from top and going clockwise. Feel free to go crazy with it, just remember to maintain the same convention later.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://1.bp.blogspot.com/-yTHw5BZUe_s/W_tcfs935vI/AAAAAAABDkM/dnY-PrCRonIBxgDNidVLpxVfDbuVbQOvQCLcBGAs/s1600/Sketch.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1379" data-original-width="1600" height="343" src="https://1.bp.blogspot.com/-yTHw5BZUe_s/W_tcfs935vI/AAAAAAABDkM/dnY-PrCRonIBxgDNidVLpxVfDbuVbQOvQCLcBGAs/s400/Sketch.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Fig 1</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
In the very last line I sum all the values contained in the array and store it in a new variable called 'topple_candidates'. This variable ultimately answers the most important question : <b>"is the current stack going to topple ?"</b></div>
<div class="separator" style="clear: both; text-align: justify;">
If this variable is zero, it means that the array is composed by 4 zeroes, which in turn means that none of the adjacent voxels is a candidate for the current stack to topple.</div>
<div class="separator" style="clear: both; text-align: justify;">
In other words, if this variable is bigger than zero, the current stack is unstable. And it's really easy now to color code those voxels using the grid 'mask'.</div>
<div class="separator" style="clear: both; text-align: justify;">
All we need to do is add the following line :</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
f@mask = topple_candidates > 0;</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Which brings the entire code to this :</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-6ewPphClM5s/W_trP1Qv0aI/AAAAAAABDk0/C-x7CvmdUJI9rvQ2n9G7emKP1SlmUtvtQCLcBGAs/s1600/Selection_014.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="434" data-original-width="687" height="404" src="https://2.bp.blogspot.com/-6ewPphClM5s/W_trP1Qv0aI/AAAAAAABDk0/C-x7CvmdUJI9rvQ2n9G7emKP1SlmUtvtQCLcBGAs/s640/Selection_014.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Now if you look at the view port, you'll probably see this :</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-hY56G_8jbro/W_tr-UgrSXI/AAAAAAABDk8/MLplMYaHLrE64MXGclKofSTeKQcjSXz7QCLcBGAs/s1600/Selection_015.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1083" data-original-width="1104" height="625" src="https://1.bp.blogspot.com/-hY56G_8jbro/W_tr-UgrSXI/AAAAAAABDk8/MLplMYaHLrE64MXGclKofSTeKQcjSXz7QCLcBGAs/s640/Selection_015.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Yes it does look like a napkin full of blood. Let's try to ignore that.</div>
<div class="separator" style="clear: both; text-align: justify;">
This is a very comforting result : note how the voxels that lie on the ground area are all white, cause none of those have adjacent voxels that are much lower. Those white voxels are stable and will not topple. The red voxels instead are on steep slopes and it makes a lot of sense that they might topple down.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
But there is one issue we need to resolve before moving to the next step: at the moment if you change the resolution of the grid ( heightfield1 -- Grid Spacing Parameter) you'll notice that the toppling areas (in red) shrink or grow. </div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-mcqJ0O5i1Yo/W_1iQLCBVFI/AAAAAAABDnQ/MDR41UDf9fQZlPkuSk7mcmEFOTP93JY5ACLcBGAs/s1600/out_2.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="480" data-original-width="480" src="https://2.bp.blogspot.com/-mcqJ0O5i1Yo/W_1iQLCBVFI/AAAAAAABDnQ/MDR41UDf9fQZlPkuSk7mcmEFOTP93JY5ACLcBGAs/s1600/out_2.gif" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
This is not what we want. We want to be able to develop our algorithm on a fast grid (say 20x20), and once we're happy with it, move to a way higher res grid without fearing that the result will change. In other words we need to make our system<b> resolution independent</b>.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
First off let's try to understand why this is happening.</div>
<div class="separator" style="clear: both; text-align: justify;">
When we change the resolution of the grid , we effectively change the size of each voxel. But the 'topple_threshold' height is always 1. Somehow we need to tie <b>grid spacing</b> with <b>topple_threshold</b>.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
when <b>grid spacing</b> becomes smaller, that means that the voxel becomes smaller, consequently even <b>topple_threshold </b>should become smaller.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
grid_spacing ---- voxel size ---- topple_threshold</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
So the parameter Grid Spacing and the variable 'topple_threshold' seem to be connected by a linear dependency. Let's just multiply those, and use the result as our new <b>'adaptive'</b> topple_threshold.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-V997NmCixc4/W_-LgXbVdxI/AAAAAAABDpI/Dkaak4HQbLwCa4LzYFs0jFKxDRp01wHXwCLcBGAs/s1600/Selection_022.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="693" data-original-width="691" height="640" src="https://1.bp.blogspot.com/-V997NmCixc4/W_-LgXbVdxI/AAAAAAABDpI/Dkaak4HQbLwCa4LzYFs0jFKxDRp01wHXwCLcBGAs/s640/Selection_022.png" width="636" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
First off I parametrized a new variable called 'topple_threshould_mult', and created a new parameter called 'grid_spacing' linked to the parameter 'grid_spacing' on the heightfield1 node.</div>
<div class="separator" style="clear: both; text-align: justify;">
At this point I can finally calculate the new 'topple_threshold' :</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<i>topple_threshold = grid_spacing * topple_threshold_mult ;</i></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Now the red areas will stay consistent even if we change the resolution, on top of that we've an additional slider to change the toppling threshold !</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-xaybK33Lppg/W_-WhynjP1I/AAAAAAABDpc/DzfuNcGB0lQ76qR-iU3vIUIRuHzCZcungCLcBGAs/s1600/output.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="480" data-original-width="469" height="640" src="https://3.bp.blogspot.com/-xaybK33Lppg/W_-WhynjP1I/AAAAAAABDpc/DzfuNcGB0lQ76qR-iU3vIUIRuHzCZcungCLcBGAs/s640/output.gif" width="624" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Switch to a more decent resolution, something that will show some cool detail. On the node "Heightfield1" set the parameter Grid Spacing to 0.05.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Great !</div>
<div class="separator" style="clear: both; text-align: justify;">
Now let's get back for a moment to the variable 'topple_candidates'.</div>
<div class="separator" style="clear: both; text-align: justify;">
We said that if this is bigger than 0 it means the current stack will topple.</div>
<div class="separator" style="clear: both; text-align: justify;">
Now the question is "in which direction will it topple ?".</div>
<div class="separator" style="clear: both; text-align: justify;">
In PART 1 I decided to choose the toppling direction <b>randomly</b>. <i>Note that I made this choice cause it's the simplest one. More elaborate solutions would be to choose the toppling direction based on a vector (which could be wind, or forces like mm .. collisions / proximity with other objects). For the sake of simplicity we'll go for the random route, but please feel free to experiment with other solutions on your own.</i></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
We need to choose one of the non-zero elements in the array randomly and store <b>the index</b> in an additional grid that we'll name 'dropdir'.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Let's abstract the problem here :</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
I have an array of 1s and 0s</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
example :</div>
<div class="separator" style="clear: both; text-align: justify;">
A = [ 0 , 1 , 0 , 1 ]</div>
<div class="separator" style="clear: both; text-align: justify;">
I want my code to *randomly* return 1 or 3 (the indices of the array that correspond to 1s).</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both;">
another example : </div>
<div class="separator" style="clear: both;">
A = [ 1 , 0 , 0 , 1 ]</div>
<div class="separator" style="clear: both;">
I want my code to *randomly* return 0 or 3 (the indices of the array that correspond to 1s).</div>
<div>
<br /></div>
<div>
<div class="separator" style="clear: both;">
another example : </div>
<div class="separator" style="clear: both;">
A = [ 0 , 0 , 1 , 0 ]</div>
<div class="separator" style="clear: both;">
I want my code to always return 2 (the only index of the array that correspond to 1).</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
A really simple solution is the following (if you find a smarter and/or faster solution please please let me know):</div>
<ol>
<li>count how many 1s are present in the array (we already have this value, it's the variable 'topple_candidates')<br />Example :<br />if the array A = [ 0 , 1 , 0 , 1 ] , topple_candidates = 2<br />if the array A = [ 1 , 0 , 1 , 1 ] , topple_candidates = 3</li>
<li>if the array A = [ 0 , 0 , 0 , 0 ] , topple_candidates = 0</li>
<li>find a random integer number between 0 and topple_candidates ( let's call it R )<br />if topple_candidates = 2 , R = 1 or 2<br />if topple_candidates = 3 , R = 1 , 2 or 3<br />if topple_candidates = 1 , R = 1</li>
<li>loop over the array until I find as many 1s as R</li>
<li>store the array index in 'dropdir'</li>
</ol>
<div>
Ok first off we need to create an additional grid named 'dropdir'. As we just mentioned earlier each voxel of this grid will contain a number between 0 and 3. This number will give an indication of the toppling direction, according to the convention established in fig.1.</div>
<div>
<br /></div>
<div>
Modify your network this way:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-SZU9Tguxm2g/W_-a4j5urkI/AAAAAAABDpo/sz_PQJ60xhYg6d5hlb5TQVvn1vMWR8trgCLcBGAs/s1600/Selection_023.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1150" data-original-width="1147" height="640" src="https://1.bp.blogspot.com/-SZU9Tguxm2g/W_-a4j5urkI/AAAAAAABDpo/sz_PQJ60xhYg6d5hlb5TQVvn1vMWR8trgCLcBGAs/s640/Selection_023.png" width="636" /></a></div>
<div>
<br /></div>
<div>
In other words I'm duplicating the field 'height', renaming it to '_toppledir' (I like to ad a leading underscore to temporary data) and merging it back with the other 2 fields 'height' and 'mask'. The 'primitive1' node will disable the visibility of this new grid. For now I disabled this node cause we want to see what happens on this grid when we start storing values. We'll hide it after we are sure it's working properly.<br />
<br />
In the view port you should see something like this<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-WDoiAp_qsGY/W_-cBTHx95I/AAAAAAABDp0/vhxztWJVQf8Y9xKu4zq-n79Pk_YS9gS2ACLcBGAs/s1600/Selection_024.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1237" data-original-width="1092" height="400" src="https://4.bp.blogspot.com/-WDoiAp_qsGY/W_-cBTHx95I/AAAAAAABDp0/vhxztWJVQf8Y9xKu4zq-n79Pk_YS9gS2ACLcBGAs/s400/Selection_024.png" width="351" /></a></div>
<br /></div>
Now if you middle click on the <b>merge1</b> node you should see 3 grids.<br />
<br />
<ul>
<li><b>height</b> - the bloody napkin, now more similar to a bloody mountain</li>
<li><b>mask</b> - not visible as a grid, but visible as red mask on the 'height' field</li>
<li><b>_toppledir</b> - a copy of the height field, but completely flat since all the voxels have a zero value.</li>
</ul>
<div>
Now modify the code in <b>volumewrangle1</b> this way:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-i0OwjhaelEo/W_-hc0vNeJI/AAAAAAABDqA/21SBYRKlf9YogmM4KzRbPAT7FRD1nOrEgCLcBGAs/s1600/Selection_025.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1040" data-original-width="866" height="640" src="https://3.bp.blogspot.com/-i0OwjhaelEo/W_-hc0vNeJI/AAAAAAABDqA/21SBYRKlf9YogmM4KzRbPAT7FRD1nOrEgCLcBGAs/s640/Selection_025.png" width="531" /></a></div>
<div>
<br /></div>
<div>
( I know , you hate me cause you can't copy and paste the code. But guess what, I did it on purpose ! 😼 I want you to write it all cause I want you to get used to code in VEX, opposed to become a copy and paste black belt. On top of that formatting code with Blogger is a real pain ... )</div>
<div>
<br /></div>
<div>
Let's quickly go over the new code.</div>
<div>
First off, the whole new code is almost entirely enclosed in a big if-statement: if topple_candidates is zero, we mark that voxel as stable ( or -1 ) and we move on to the next voxel.</div>
<div>
Otherwise ... </div>
<div>
first off we set a random number 'rnd' between 0.0 and 1.0.</div>
<div>
<br /></div>
<div>
let's focus on this line :</div>
<div>
<i>int r = 1 + floor ( rnd * topple_candidates )</i></div>
<div>
<i><br /></i></div>
<div>
The first thing to keep in mind is that the function <i>floor( a ) </i>returns the integer part of <i>a.</i> </div>
<div>
For instance :</div>
<div>
floor(0.3) will return 0</div>
<div>
floor(1.543) will return 1</div>
<div>
floor(12.543) will return 12</div>
<div>
<br /></div>
<div>
now ...</div>
<div>
(rnd * topple_candidates) is just a way to re-map a number that is in the range between 0.0 and 0.99999... to a number in a range between 0.0 and topple_candidates-1.</div>
<div>
<br /></div>
<div>
Floor will return the integer part of it, which is ... 0 - (topple_candidates-1)</div>
<div>
<br /></div>
<div>
But, remember that the purpose here is to create a random number between 1 and topple_candidates cause we want to use this number (r) as a counter ... so we just need to add 1. Hence ...</div>
<div>
<i>int r = 1 + floor ( rnd * topple_candidates )</i></div>
<div>
<i><br /></i></div>
<div>
In the for-loop below I start looping over the 4 elements of the array hdiff[]. This array will have at least one 1, otherwise we would have been kicked out of the if-statement earlier. </div>
<div>
In this loop , a variable 'c' will be increased every time we meet a 1 in the array. As soon as 'c' reaches the desired count 'r', we have found the index, and we can exit the loop (break statement).</div>
<div>
All we need to do now is store the index in '_toppledir' !</div>
<div>
<br /></div>
<div>
Now if you look at the view port you'll see something weird ...</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-kFmpesMEGLs/W_-rvj-_6DI/AAAAAAABDqM/DgWZvb-E-8UZpTT-c-p-ORUPvRZPMeV1gCLcBGAs/s1600/Selection_026.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1521" data-original-width="1320" height="640" src="https://2.bp.blogspot.com/-kFmpesMEGLs/W_-rvj-_6DI/AAAAAAABDqM/DgWZvb-E-8UZpTT-c-p-ORUPvRZPMeV1gCLcBGAs/s640/Selection_026.png" width="553" /></a></div>
<div>
<br /></div>
<div>
... and kinda beautiful.</div>
<div>
The weird looking spiky thing is the '_toppledir' grid. This grid should contain numbers between -1 and 3. In fact, if you switch to the front view , you'll notice exactly that ! </div>
<div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-3zjK3zFhHJk/XEZCnUAMiXI/AAAAAAABGJA/eTlna6bH-7cr1JsKrXsiqjAJNRPewPIXQCLcBGAs/s1600/Sketch.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1107" data-original-width="1400" height="316" src="https://2.bp.blogspot.com/-3zjK3zFhHJk/XEZCnUAMiXI/AAAAAAABGJA/eTlna6bH-7cr1JsKrXsiqjAJNRPewPIXQCLcBGAs/s400/Sketch.png" width="400" /></a></div>
</div>
<div>
<br /></div>
<div>
On top of that, if you look closely, you'll notice the following:</div>
<div>
<ul>
<li>areas corresponding to white areas in the 'height' grid are flat and at level -1 </li>
<li>areas corresponding to red areas in the 'height' grid are always higher than -1</li>
<li>if you scrub the frame in the timeline, the peaks should change randomly (cause the variable 'rnd' is dependent on @Time) , but not the ones in the white areas of the 'height' field.</li>
<li>there are a few flat areas that don't correspond to white areas of the 'height' field ! Those should correspond to slopes where there can be only one toppling direction. These areas will be flat, yes, but higher than -1 level.</li>
</ul>
<div>
Cool.</div>
</div>
<div>
The good news is that Step 1 is done !<br />
Let's rename volumewrangle1 to step1.<br />
<br />
Oh and let's store the value calculated for 'topple_threshold' in a detail attribute, on the geometry itself. This way I don't need to calculate it again in Step 2.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-LY97JtHL8Tw/XEZHbN_n4SI/AAAAAAABGJM/5fKwJ3cdtaQ18F_euo1LbnzaWsJrnb8TACLcBGAs/s1600/Sketch.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="239" data-original-width="971" height="156" src="https://4.bp.blogspot.com/-LY97JtHL8Tw/XEZHbN_n4SI/AAAAAAABGJM/5fKwJ3cdtaQ18F_euo1LbnzaWsJrnb8TACLcBGAs/s640/Sketch.png" width="640" /></a></div>
<br />
The VEX line above will use the command 'setdetailattribute' to create a detail attribute<br />
<br />
A detail attribute is simply a per-object attribute, opposed to a per-point, per-vertex, per-prim attributes.<br />
I generally use detail attributes to store values that exist for the whole object. They're super convenient cause they're kinda similar to parameters, but attached to the geometry itself, so they flow along the whole graph and you can access it from everywhere. On top of that, they're very light since there's only one per object, and the size is independent on how heavy is the geometry.<br />
Why not call them 'object attributes' instead of 'detail attributes' ? MMMmmm ... well ...<br />
<h2>
STEP 2</h2>
Let's quickly recap.<br />
At this stage we've 3 volumes:<br />
<ul>
<li><b>Height</b> : contains our sand. It's a 2d volume. Each voxel contains the height of the corresponding stack of sand.</li>
<li><b>_dropdir</b> : contains the info about which stack is stable or unstable. On top of that, for those who are unstable, it even tells us in which direction they'll topple. More specifically (check Fig.1) :</li>
<ul>
<li>-1 : stable</li>
<li>0 : topple up</li>
<li>1 : topple right</li>
<li>2 : topple down</li>
<li>3 : topple left </li>
</ul>
<li><b>Mask</b> : used only for debugging purpose. At the moment it flags red all the unstable <b>Height</b> voxels.</li>
</ul>
So, well ... we know everything. All we need to do is actually do the toppling thing.<br />
<br />
All we need to do is this:<br />
<br />
For each voxel V in the grid 'Height':<br />
<ul>
<li>find which of the 4 adjacent voxels is marked to topple towards V and increase V's Height accordingly.<br /><br />Example:</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-jHNa4N-Vqqk/XEZ3UUaCj_I/AAAAAAABGJY/6gCJjdpA4poi-KvSbZU7-Q_2j3vtTgHwwCLcBGAs/s1600/Sketch.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1404" data-original-width="1407" height="319" src="https://1.bp.blogspot.com/-jHNa4N-Vqqk/XEZ3UUaCj_I/AAAAAAABGJY/6gCJjdpA4poi-KvSbZU7-Q_2j3vtTgHwwCLcBGAs/s320/Sketch.png" width="320" /> </a></div>
<div class="separator" style="clear: both; text-align: center;">
Looking at V's 4 adjacent voxels, two of them will topple on V. One is the voxel on the right that contains the value -3 (which, according to out convention, means it's going topple left), and the other is the voxel down that contains the value 0 (which , according to our convention, means it's going to topple up). So a total of 2 'grains of sand' will land on V, increasing it's Height.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<ul>
<li>if V itself is not stable (meaning that it will topple on one of its 4 adjacent vectors) decrease V's 'Height" accordingly. In other words, if the current voxel V itself is not stable (meaning <b>_toppledir </b>contains any value different than -1) it means that one 'grain of sand' is going to topple , decreasing V's Height.</li>
</ul>
<br />
Let's implement Step 2 in Houdini : drop a new volumewrangle node and get the data about the 4 adjacent voxels.<br />
<br />
<b></b>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-1cUlWken18g/XEa4RxNgnWI/AAAAAAABGJk/xqJii5MvxYUTnKVZAGlMg6pKF113TgaMACLcBGAs/s1600/Sketch.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="482" data-original-width="1514" height="203" src="https://4.bp.blogspot.com/-1cUlWken18g/XEa4RxNgnWI/AAAAAAABGJk/xqJii5MvxYUTnKVZAGlMg6pKF113TgaMACLcBGAs/s640/Sketch.png" width="640" /></a></div>
<br />
<b></b>
The first 4 lines should be familiar. We're fetching the current voxel's adjacent voxels content for the grid <b>_toppledir</b> that we populated in step 1.<br />
<br />
In line 5, I'm just fetching topple_threshold stored in step 1.<br />
Why am I dividing it by 4 ? I'll explain it in a moment.<br />
<br />
Cool, now we know in what direction the adjacent voxels will topple. Now we need to check if any of the adjacent stacks are going to topple on the current voxel. In that case we need to increase the current voxel's <b>Height </b>by the <b>topple_threshold</b>.<br />
Let's remember that <b>topple_threshold </b>is the threshold value that decides if a voxel is unstable. So , in step 2, if all 4 the adjacent voxels are toppling towards the current voxel, it means that the current voxel's <b>Height </b>will be increased by topple_threshold*4. <br />
Now, this wouldn't be 'fair' towards the adjacent voxels, because in Step 1, each one of them alone had a reason to topple on the current voxel. If when an adjacent stack topples on the current voxel I increase it by the *whole* topple_threshold amount, this alone would be enough to re-evaluate the reasons for the remaining voxels to topple on the current voxels. We need, instead, to preserve the consistency with the decisions made in Step 1. This is why I'm dividing the value by 4.0.<br />
This way, if all the adjacent stacks topple towards the current stack , Height will never get higher than any of those adjacent stacks.<br />
<br />
Cool, all we need to do, is to check if the adjacent stacks have any intention to topple on the current stack. In other words ... <br />
<ul>
<li>if the stack on the <b>right </b>wants to topple on the <b>left increase </b>the current stack by threshold/4</li>
<li>if the stack on the <b>left </b>wants to topple on the <b>right increase </b>the current stack by threshold/4</li>
<li>if the stack <b>up </b>wants to topple <b>down increase </b>the current stack by threshold/4</li>
<li>if the stack <b>down </b>wants to topple <b>up increase </b>the current stack by threshold/4</li>
</ul>
... that's not it yet. We need to check if the current stack is not stable .. <br />
<ul>
<li>if the <b>current stack</b> wants to topple (we don't care where) <b>decrease </b>the current stack by threshold/4 </li>
</ul>
Let's write all this in VEX :<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-ZSrW2y-psmE/XGkNxv7PxSI/AAAAAAABGpQ/HhSvTMLmNAMdjc41vkwzjqgwdpZh1IOdgCLcBGAs/s1600/Annotation%2B2019-02-16%2B233048.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="614" data-original-width="1081" height="361" src="https://3.bp.blogspot.com/-ZSrW2y-psmE/XGkNxv7PxSI/AAAAAAABGpQ/HhSvTMLmNAMdjc41vkwzjqgwdpZh1IOdgCLcBGAs/s640/Annotation%2B2019-02-16%2B233048.jpg" width="640" /></a></div>
<br />
<ul>
</ul>
<br />
<br />
<br />
<br />
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
</div>
<div class="separator" style="clear: both; text-align: justify;">
... first off I'm initializing to zero a new variable <b>height_incr</b> where I want to store the toppling contributions coming from all the directions.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
All the if... statements are doing exactly what I described earlier.</div>
<ul>
<li>if (dxp == 3) ... <br />means ... if the stack on the right (xp = x positive) wants to topple towards the left (remember that according to our convention in Fig.1 the number 3 means 'left').</li>
<li>if (dxn == 1) ...<br />means ... if the stack on the right (xn = x negative) wants to
topple towards the right (remember that according to our convention in
Fig.1 the number 1 means 'right').</li>
<li>...</li>
<li>if (f@_toppledir!=-1)<br />means ... if the current voxel is not stable (meaning <b>_toppledir</b> does not contain -1) then , regardless of what direction it'll topple, has to go down, decrease.</li>
</ul>
After accumulating all the sand contribution in <b>height_incr </b>, I'll add it to the current height (last line).<br />
<br />
Done. Finish. Caput. Finito.<br />
<br />
Almost.<br />
<br />
Ok to recap this is what your network should look like<br />
<span id="goog_846750035"></span><span id="goog_846750036"></span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-9oNNOORI04I/XGkTykS20PI/AAAAAAABGpc/xTAmsc_b8tci_zETWZe_t9P98i0xh-U1gCEwYBhgL/s1600/Annotation%2B2019-02-16%2B235440.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1064" data-original-width="663" height="400" src="https://2.bp.blogspot.com/-9oNNOORI04I/XGkTykS20PI/AAAAAAABGpc/xTAmsc_b8tci_zETWZe_t9P98i0xh-U1gCEwYBhgL/s400/Annotation%2B2019-02-16%2B235440.jpg" width="248" /> </a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
This is the vex code contained in 'step1' Point Wrangle node:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-OK7_XPAiaPs/XGkTzO23xmI/AAAAAAABGps/Dn7kcrjLntQZ0t5X0Y2kk802q4ggDjvbgCEwYBhgL/s1600/Annotation%2B2019-02-16%2B235544.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1555" data-original-width="1101" height="640" src="https://4.bp.blogspot.com/-OK7_XPAiaPs/XGkTzO23xmI/AAAAAAABGps/Dn7kcrjLntQZ0t5X0Y2kk802q4ggDjvbgCEwYBhgL/s640/Annotation%2B2019-02-16%2B235544.jpg" width="452" /> </a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
And this is the code contained in 'step2' Point Wrangle node:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-BFK9aD-YV3Q/XGkTzBxqM4I/AAAAAAABGps/_3njpHfxzEAHnVfKjsZJL1IMJgk5U4TUwCEwYBhgL/s1600/Annotation%2B2019-02-16%2B235621.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="625" data-original-width="987" height="252" src="https://1.bp.blogspot.com/-BFK9aD-YV3Q/XGkTzBxqM4I/AAAAAAABGps/_3njpHfxzEAHnVfKjsZJL1IMJgk5U4TUwCEwYBhgL/s400/Annotation%2B2019-02-16%2B235621.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Cool.</div>
<div class="separator" style="clear: both; text-align: justify;">
Now, if you check your bloody napk .. uhm your sand distribution in the viewport, not much is changed. Well, check better ... it is actually changed, but veeery very little. You need to really zoom in a detail, and if you enable and disable the node 'step2' you'll notice that something is actually changing.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-awuor-l8t3I/XHhpfoQGcqI/AAAAAAABHpg/lrkZLejGsUQmULrM2r_YXfdizZWRLN63ACLcBGAs/s1600/anim.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1024" data-original-width="1024" height="400" src="https://4.bp.blogspot.com/-awuor-l8t3I/XHhpfoQGcqI/AAAAAAABHpg/lrkZLejGsUQmULrM2r_YXfdizZWRLN63ACLcBGAs/s400/anim.gif" width="400" /></a> </div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
This happens of course because this is only one iteration. We need to repeat this process over and over in order to see the something interesting, and of course Solver SOP is once again our friend.</div>
<div class="separator" style="clear: both; text-align: justify;">
All we need to do is move the nodes <b>step1 </b>and <b>step2 </b>in a Solver SOP. The Solver SOP will fetch the geometry only at the frame specified in the parameter <i>Start Frame</i> (typically frame 1). For all the subsequent frames, the Solver will use the result of the previous iteration. In other words, will perform a 'Feedback Loop'.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Let's do that. </div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<ul>
<li>Create a Solver SOP node, and copy and paste the nodes <b>step1 </b>and <b>step2 </b>inside the Solver SOP network.</li>
<li>inside the Solver SOP network connect <b>Prev_Frame </b>input to the node <b>step1</b></li>
<li>inside the Solver SOP , change the expression on the node <b>step2 </b>to<br /><br /><i>ch("../../../../heightfield1/gridspacing")</i></li>
<li>inside the Solver SOP, on the node <b>step 1 </b>change the parameter Topple Threshold Mult to 2. This will make sure that only the very steep parts of the sand distribution will be modified, which is more realistic.</li>
</ul>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-qMNMoAXsqbk/XHhxuhyvmKI/AAAAAAABHpw/I44JgVYLlEg_OtQ0iqR5kFNMIC2fHxL_wCEwYBhgL/s1600/SharedScreenshot.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="977" data-original-width="1600" height="388" src="https://3.bp.blogspot.com/-qMNMoAXsqbk/XHhxuhyvmKI/AAAAAAABHpw/I44JgVYLlEg_OtQ0iqR5kFNMIC2fHxL_wCEwYBhgL/s640/SharedScreenshot.jpg" width="640" /></a></div>
<ul>
<li>outside of the Solver SOP connect the node <b>heightfield_noise1 </b>with input 1 of the Solver SOP.</li>
<li>outside of the Solver SOP connect the output of the Solver SOP to a delete node, and use it to delete the field '__toppledir'. <br />This field was only a temporary storage to store data between step1 and step2. Once we're out of the Solver SOP we no longer need it.</li>
<li>outside of the Solver SOP, Set the number of Sub Steps to 20.</li>
</ul>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-rCwTYcPOdKM/XHhy3EKrRjI/AAAAAAABHp8/ptAhNuL4PmA1tmhtgMmULD5dr9JuW2qgwCEwYBhgL/s1600/SharedScreenshot.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1076" data-original-width="1514" height="454" src="https://3.bp.blogspot.com/-rCwTYcPOdKM/XHhy3EKrRjI/AAAAAAABHp8/ptAhNuL4PmA1tmhtgMmULD5dr9JuW2qgwCEwYBhgL/s640/SharedScreenshot.jpg" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
Make sure you're on Frame 1 , and in your ViewPort you should see something like this :<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-t3XfyQ9qQcA/XHh0KFd5wVI/AAAAAAABHqI/3V4Vd3FPjJU5nIDQ-W2qFX73D9dD6H76ACLcBGAs/s1600/SharedScreenshot.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1478" data-original-width="1279" height="640" src="https://4.bp.blogspot.com/-t3XfyQ9qQcA/XHh0KFd5wVI/AAAAAAABHqI/3V4Vd3FPjJU5nIDQ-W2qFX73D9dD6H76ACLcBGAs/s640/SharedScreenshot.jpg" width="552" /></a></div>
<br />
Notice that the mask field is marking in red what elements of the sand distribution are not stable.<br />
Let's press play and see what happens.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://www.blogger.com/blogger.g?blogID=5626905874114803923" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=5626905874114803923" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://4.bp.blogspot.com/-IVJl3z1CFl4/XHjUZBppbaI/AAAAAAABHt0/VEsMvx6EX8AL91xpckZmIQXZUD1BXDwxgCLcBGAs/s1600/opt_anim.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="600" data-original-width="600" height="640" src="https://4.bp.blogspot.com/-IVJl3z1CFl4/XHjUZBppbaI/AAAAAAABHt0/VEsMvx6EX8AL91xpckZmIQXZUD1BXDwxgCLcBGAs/s640/opt_anim.gif" width="640" /></a></div>
<br />
It's cool to see how the unstable sand areas get smaller and smaller , that is comforting cause it means the sand distribution is adapting to find a stable state. Let's remove the red areas, so we can actually see the result of all our work.<br />
<br />
In the delete node, append <i>@name=mask</i> to the Group parameter to remove the mask feedback from the viewport.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-eb06HeBB-KA/XH2yGyNm0CI/AAAAAAABH0Y/KWhr4CkH4LE2EAemxcwjZTmWK2xLigGvwCLcBGAs/s1600/Annotation%2B2019-03-04%2B151655.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="133" data-original-width="1358" height="62" src="https://4.bp.blogspot.com/-eb06HeBB-KA/XH2yGyNm0CI/AAAAAAABH0Y/KWhr4CkH4LE2EAemxcwjZTmWK2xLigGvwCLcBGAs/s640/Annotation%2B2019-03-04%2B151655.jpg" width="640" /></a></div>
<br /></div>
<div style="text-align: justify;">
Now the red areas should be gone and we can run a new simulation...<br />
(for the sim below I increased Sub Steps to 100. It's a little slower per frame, but it takes less frames to see the progress.)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-SuK0DXDLq-A/XHjqls74lqI/AAAAAAABHuA/LGFAoOXLifo7B04px_ASl8JPHBzlKeXMQCLcBGAs/s1600/animated_optimized.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="600" data-original-width="600" height="640" src="https://2.bp.blogspot.com/-SuK0DXDLq-A/XHjqls74lqI/AAAAAAABHuA/LGFAoOXLifo7B04px_ASl8JPHBzlKeXMQCLcBGAs/s640/animated_optimized.gif" width="640" /></a></div>
<br />
Note how the steep area collapse ...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-YsbW1iD1q-0/XHjzaH6GkEI/AAAAAAABHuM/aYSofzXVAS4Sfyl7M3cbZDVLRGn8XP6sgCLcBGAs/s1600/animated_optimized.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="600" data-original-width="600" height="640" src="https://1.bp.blogspot.com/-YsbW1iD1q-0/XHjzaH6GkEI/AAAAAAABHuM/aYSofzXVAS4Sfyl7M3cbZDVLRGn8XP6sgCLcBGAs/s640/animated_optimized.gif" width="640" /></a></div>
<br />
.. and the sand accumulates at the base , creating an advancing leading edge ..<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-zkJamT7vAHY/XHj7M9gev2I/AAAAAAABHuY/t7MpnsGkFSMoBST1-56qWbD2kVKZiAbjgCLcBGAs/s1600/animated_optimized.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="600" data-original-width="600" height="640" src="https://4.bp.blogspot.com/-zkJamT7vAHY/XHj7M9gev2I/AAAAAAABHuY/t7MpnsGkFSMoBST1-56qWbD2kVKZiAbjgCLcBGAs/s640/animated_optimized.gif" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Furthermore, note how the process reaches an equilibrium , and little by little the sand distribution becomes stable and the result is an entirely new distribution.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Advanced ideas for a more production-ready tool :</div>
<ul>
<li>use f@mask to art direct the areas that should be affected by this process</li>
<li>use a noise function to create patterns / more interesting toppling effect</li>
<li>implement a wind direction </li>
<li>emit sand (this time particles) from the non-stable areas to simulate something like <a href="https://www.youtube.com/watch?v=dcooN4KWirs" target="_blank">this</a></li>
<li>use other objects as 'colliders'. For instance create a sand footstep fx where the sand gets pushed on the sides and accumulates in taller areas around the foot, when the foot sinks in the sand (I haven't implemented this one, but would be cool to see an example of this !) (Thank you for the idea Daniel !)</li>
</ul>
I hope you enjoyed this article and apologies for taking so long before releasing part-2.</div>
<div style="text-align: justify;">
</div>
<div style="text-align: justify;">
Next time we'll implement this same setup using OpenCL and compare the speed with the VEX implementation.<br />
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
You'll find this setup <a href="https://drive.google.com/file/d/1WfUWUZWGInM3SJ5tpvKxsFv3Hsw8PAgW/view?usp=sharing" target="_blank">HERE</a>.</div>
<div class="separator" style="clear: both; text-align: justify;">
Note : I've implemented this on H17.0.XXX., but it should work great even on 16.</div>
<br /></div>
Unknownnoreply@blogger.com5tag:blogger.com,1999:blog-5626905874114803923.post-71544646189195046042018-03-24T21:26:00.003-07:002021-06-05T08:14:31.506-07:00Sand without grains - PART 1 - Abelian Sandpile Model<div style="text-align: justify;">
About a month ago I stumbled upon a project with an interesting FX : Moving Sand Dunes.<br />
Yes ... like in the movie 'Dune' but without the worms.<br />
And with 3 weeks dev time.<br />
<br />
UPDATE:<br />
The commercial is finally online so I can show you where I did apply this technique !<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-EDvLGBru174/YLuUE63sBTI/AAAAAAABmio/gM-8mISrEz4GcWhA7URmbXdQFr4GltQ1wCLcBGAsYHQ/s1618/Screen%2BShot%2B2021-06-05%2Bat%2B5.09.47%2BPM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="790" data-original-width="1618" height="305" src="https://1.bp.blogspot.com/-EDvLGBru174/YLuUE63sBTI/AAAAAAABmio/gM-8mISrEz4GcWhA7URmbXdQFr4GltQ1wCLcBGAsYHQ/w627-h305/Screen%2BShot%2B2021-06-05%2Bat%2B5.09.47%2BPM.png" width="627" /></a></div><br /></div>
<div style="text-align: justify;">
<br />
For some reason this video keeps being moved from YouTube and eventually has been deleted. Don't ask me why. I'm kinda sick of re-linking it every month so here's a frame that hopefully should show has article picture in Blogger.</div><div style="text-align: justify;"><br /></div><div style="text-align: justify;"><a href="https://www.ispot.tv/ad/d1ZI/capital-one-quicksilver-desert-featuring-samuel-l-jackson" target="_blank"> This</a> is the only version I found on the Net.<br /><br /></div>
<div style="text-align: justify;">
During the first day I kept watching and re-watching this YouTube video.</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/a7bX7T8lltI/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/a7bX7T8lltI?feature=player_embedded" width="320"></iframe></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
The part I found really fascinating is around second 16, where you can see the leading edge of the falling sand moving up, and the pile of sand underneath accumulating trying to chase the leading edge. Strangely enough this results in something visually moving up , opposed to what we would expect from sand falling down. </div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
What causes this behavior in real life is the result of millions and millions of tiny sand rocks. Each little rock has no choice than falling when there's nothing else supporting it, roll down colliding with other rocks and eventually stop as soon as the inclination is not strong enough to win the friction with the other rocks. </div>
<div class="separator" style="clear: both; text-align: justify;">
All these little rocks will then eventually accumulate until the whole system becomes stable. </div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
If I had to recreate this in Houdini exactly as I just described, it would take a huge amount of CPU power, RAM and weeks of simulation. Not to mention disk space. In other words, a lot of resources. And the result would <strike>probably </strike>never be remotely close, because even with a terrific render farm, I could never have THAT many particles. </div>
<br />
Actually, how many particles are we talking about ?<br />
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Well, given that the average medium-grained sized sand is 0.375 mm in diameter, how many grains of sand can I fit ...</div>
<div class="separator" style="clear: both; text-align: justify;">
</div>
<ul>
<li style="text-align: justify;">in a handful ? <b>~400.000</b></li>
<li style="text-align: justify;">in a bath tub ? <b>~9.000.000.000</b></li>
<li style="text-align: justify;">one sand dune ? <a href="http://i.imgur.com/vj1IG.gif">probably this amount</a></li>
</ul>
<div style="text-align: justify;">
Houdini Sand Solver is NOT an option in this case.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Let's make a few considerations:</div>
<ul>
<li style="text-align: justify;">looking at the video, we cannot see the single sand rocks ! All we see is a large surface deforming. So maybe we can skip the particles approach entirely and focus on the behavior of this large surface.</li>
<li style="text-align: justify;">naively put, the behavior of the sand dune looks 'simple', hence there must be a 'simple' mechanism to reproduce it.</li>
<li style="text-align: justify;">we don't have to be physically correct ! (unless you're animating a cube : <a href="https://vimeo.com/221178360">proof</a>).</li>
</ul>
<div class="separator" style="clear: both; text-align: justify;">
After a few minutes on YouTube I found this video :</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
</div>
<div style="text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/xcPWH8ZklpA/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/xcPWH8ZklpA?feature=player_embedded" width="320"></iframe></div>
<br />
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
Is really that simple ? Well ... seems too good to be true.<br />
Good news is that since the algorithm is so simple, it will take no time to figure out if we're going in the right direction !<br />
<br />
Before we start, let me show you the kind of result we are aiming for.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-Vt9TrE1I5Gk/WrbWYl79POI/AAAAAAAA6VI/GSoS4RepFVUiJoRDRZ3vCvSbuXHg4kYNwCLcBGAs/s1600/output.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="600" data-original-width="710" height="640" src="https://3.bp.blogspot.com/-Vt9TrE1I5Gk/WrbWYl79POI/AAAAAAAA6VI/GSoS4RepFVUiJoRDRZ3vCvSbuXHg4kYNwCLcBGAs/s640/output.gif" width="640" /></a></div>
<br />
<div style="text-align: justify;">
What you're looking at is a flipbook generated in Houdini and the surface is a HeightField.</div>
<div style="text-align: justify;">
The sand starts from a veeery unstable configuration and slowly rearranges in a way that is stable. Note how the little mountain in the foreground becomes stable soon, cause its height is lower. Note how the sand accumulates at the base, and the walls slowly become flat while growing and covering underlying elements.</div>
<h2>
<span style="font-size: large;"><b><br /></b></span></h2>
<h2>
<span style="font-size: large;"><b>Sand Pile Algorithm in 2D</b></span></h2>
<div>
After watching that video, I Googled "sandpile algorithm" and found this : <b><a href="https://en.wikipedia.org/wiki/Abelian_sandpile_model">Abelian Sandpile Model</a></b></div>
<div>
<br /></div>
I started reading and after a few lines I got to this point :<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-PiKX4URcDcQ/Wn97mZ58fjI/AAAAAAAA3_4/8utXGL6v4ZwpJm_l4_0CNUaBQwX1kfK9wCLcBGAs/s1600/sketch.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="628" data-original-width="1600" height="249" src="https://2.bp.blogspot.com/-PiKX4URcDcQ/Wn97mZ58fjI/AAAAAAAA3_4/8utXGL6v4ZwpJm_l4_0CNUaBQwX1kfK9wCLcBGAs/s640/sketch.png" width="640" /></a></div>
<br />
I stopped reading here, and I was already on Houdini.<br />
<br />
<div>
NOTE:</div>
<div style="text-align: justify;">
Houdini has an HeightField Erode solver which is super mega cool, but in the long 5 minutes I tried I couldn't reproduce the sandpile behaviour described below. Regardless, I still wanted to experiment this idea, primarily because it's really quick to implement and once I do it myself I can control every single aspect of it.</div>
<br />
Cool, let's get started.<br />
<br />
As always, let's work in 2D first. It'll be easy to switch to 3D later.<br />
Let's pretend <b>this is our initial sand distribution</b>:<br />
<br />
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-eifca5g-YKo/WWsLZ2uHEMI/AAAAAAAAva4/cHk26PALqWkrBHYA3xBb8HjtLJ0Fjgv4gCLcBGAs/s1600/IMG_20170715_233024.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="896" data-original-width="1600" height="179" src="https://3.bp.blogspot.com/-eifca5g-YKo/WWsLZ2uHEMI/AAAAAAAAva4/cHk26PALqWkrBHYA3xBb8HjtLJ0Fjgv4gCLcBGAs/s320/IMG_20170715_233024.jpg" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div>
<br /></div>
<div>
Each column is a stack of sand grains.<br />
For instance the stack E has 2 grains piled , the stack G has 3, and so on.</div>
<div>
<br /></div>
<div>
The idea is that <b>if a stack of sand is too tall compared to the adjacent stacks (say by 2 grains), one grain of sand would fall down, towards one of the adjacent lower stacks.</b></div>
<div>
<b>When this happens, the taller stack becomes 'one grain' shorter , while the shorter stack will become 'one grain' taller ! Well, believe it or not, this is pretty much it.</b></div>
<div>
<b><br /></b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-kTXTZepGgIs/WWsM6Bm7ZOI/AAAAAAAAvbQ/8mAxeUVlF043YR8WcFCQg7na_Km6F5SvwCLcBGAs/s1600/IMG_20170715_233024.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="841" data-original-width="1600" height="168" src="https://4.bp.blogspot.com/-kTXTZepGgIs/WWsM6Bm7ZOI/AAAAAAAAvbQ/8mAxeUVlF043YR8WcFCQg7na_Km6F5SvwCLcBGAs/s320/IMG_20170715_233024.jpg" width="320" /></a></div>
<div>
<br /></div>
<div>
<u>Iteration - 1</u><br />
Let's analyze the stacks one by one to identify the 'unstable' stacks:</div>
<div>
stack C is 2 grains taller than B</div>
<div>
stack D is 2 grains taller than E</div>
<div>
stack G is 2 grains taller than F</div>
<div>
stack H is 3 grains taller than I</div>
<div>
<br /></div>
<div>
So ...</div>
<div>
<br /></div>
<div>
C = C - 1 and B = B +1 (one grain of sand moves from C to B)</div>
<div>
D = D - 1 and E = E +1 (one grain of sand moves from D to E)</div>
<div>
G = G - 1 and F = F +1 (one grain of sand moves from G to F)</div>
<div>
H = H - 1 and I = I +1 (one grain of sand moves from H to I)</div>
<div>
<br /></div>
<div>
Which brings us to the next state:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-Ihp2dtfusgg/WWsQALCXGpI/AAAAAAAAvbk/7WXGIG004q83kW1Gs7W8Ux0_UKn75BbigCLcBGAs/s1600/IMG_20170715_233024.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="922" data-original-width="1600" height="184" src="https://2.bp.blogspot.com/-Ihp2dtfusgg/WWsQALCXGpI/AAAAAAAAvbk/7WXGIG004q83kW1Gs7W8Ux0_UKn75BbigCLcBGAs/s320/IMG_20170715_233024.jpg" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div>
<u>Iteration - 2</u><br />
Let's analyze the stacks one by one again:</div>
<br />
<div>
stack B is now 2 grains taller than A</div>
<div>
<br /></div>
<div>
So ...</div>
<div>
<br /></div>
<div>
B = B - 1 and A = A +1 (one grain of sand moves from B to A)</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-cKhVZcHfRIg/WWsSPhckFEI/AAAAAAAAvb4/L8QPHsWSqi06sKhVAWuaUkd3ut4lbrtLACLcBGAs/s1600/IMG_20170715_233024.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="856" data-original-width="1600" height="171" src="https://2.bp.blogspot.com/-cKhVZcHfRIg/WWsSPhckFEI/AAAAAAAAvb4/L8QPHsWSqi06sKhVAWuaUkd3ut4lbrtLACLcBGAs/s320/IMG_20170715_233024.jpg" width="320" /></a></div>
<div>
<br /></div>
<div>
<u>Iteration - 3</u><br />
If we analyze the stacks again we notice there's nothing left to do. This means our sand distribution has reached a stable state and we're done !</div>
<div>
<br /></div>
<div>
What is interesting is that if you sum all the heights in each of the steps above , they'll return the same number.</div>
<div>
<br /></div>
<div>
0+1+3+4+2+1+3+3+0 = 17</div>
<div>
0+2+2+3+3+2+2+2+1 = 17</div>
<div>
1+1+2+3+3+2+2+2+1 = 17</div>
<div>
<br /></div>
<div>
This is good. It means that sand is not magically appearing or disappearing (the mass of the system is stable, like in reality). We just managed to re-arrange the stacks in a stable distribution using a simple iterative algorithm.</div>
<div>
<br /></div>
<div>
Assumptions about our initial sand distribution:<br />
<ul>
<li><b>if there is sand, there is sand underneath it.</b></li>
</ul>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://4.bp.blogspot.com/-42rmJ2DjIso/WWsESYC4X7I/AAAAAAAAvaE/-gLouBvZSMEamdQG7XDBSkj3NkR12kjAwCLcBGAs/s1600/Drawing.jpeg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="193" data-original-width="618" height="99" src="https://4.bp.blogspot.com/-42rmJ2DjIso/WWsESYC4X7I/AAAAAAAAvaE/-gLouBvZSMEamdQG7XDBSkj3NkR12kjAwCLcBGAs/s320/Drawing.jpeg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 16px;">This distribution would work with the algorithm ! Sand is supported by more sand underneath</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://3.bp.blogspot.com/-NZCpkU5CTTk/WWsFQ7y1LbI/AAAAAAAAvaM/FNPJBFbGZlokJJ_xT20OZT1JtMzO-OBKwCLcBGAs/s1600/Drawing.jpeg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="193" data-original-width="618" height="99" src="https://3.bp.blogspot.com/-NZCpkU5CTTk/WWsFQ7y1LbI/AAAAAAAAvaM/FNPJBFbGZlokJJ_xT20OZT1JtMzO-OBKwCLcBGAs/s320/Drawing.jpeg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 16px;">This distribution would NOT work with the algorithm ! Sand in the red area is not supported.</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div>
<ul>
<li><b>our sand grains are arranged into a grid.</b></li>
</ul>
<div style="text-align: justify;">
Given the two assumptions above, we can simplify the system <span style="text-align: start;">considering only the grains at the surface and ignore the sand below. In other words we'll only consider the visible surface and ignore whatever is happening in the depth.</span><br />
<br />
The ideal data structure for this approach turns out to be a simple grid. Each cell of the grid correspond to one virtual stack of sand rocks (seen from above) and the number in it corresponds to the height of each stack.<br />
<br />
<h2>
<span style="font-size: large;"><b>Sand Pile Algorithm in 3D</b></span></h2>
<br />
Let's set this initial 'sand' distribution.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-7i0TolwAdAg/WfmQPYizK6I/AAAAAAAAx9g/5E6RmvKYizoo9x3syHqge9if_p22UXISQCLcBGAs/s1600/Screenshot%2Bat%2B2017-11-01%2B02%253A12%253A55.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="511" data-original-width="511" height="320" src="https://4.bp.blogspot.com/-7i0TolwAdAg/WfmQPYizK6I/AAAAAAAAx9g/5E6RmvKYizoo9x3syHqge9if_p22UXISQCLcBGAs/s320/Screenshot%2Bat%2B2017-11-01%2B02%253A12%253A55.png" width="320" /></a></div>
<br />
Remember that each square is a stack of sand, and the number is the height of that stack.<br />
<br />
Before proceeding we need to set two 'global' variables:<br />
<ul>
<li><b>Mass transfer (M)</b> : how much sand will fall from an unstable stack to one of the adjacent stacks.</li>
<li><b>Threshold (T)</b> : the minimum height difference between the current stack and its adjacent stacks that will mark that stack as unstable (and cause an amount M of sand to be transferred to some adjacent lower stack).</li>
</ul>
<div>
For this example let's set <b>T = 2</b> and <b>M = 0.5</b></div>
<div>
Now, let's identify the stacks whose adjacent stacks are lower by T.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-w0qfBGE3ypE/WfmR5-5TV5I/AAAAAAAAx9s/VyTBaRsT-Ck5Y1jKStzMeqOiEK-74_5sgCLcBGAs/s1600/Screenshot%2Bat%2B2017-11-01%2B02%253A20%253A19.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="508" data-original-width="508" height="320" src="https://3.bp.blogspot.com/-w0qfBGE3ypE/WfmR5-5TV5I/AAAAAAAAx9s/VyTBaRsT-Ck5Y1jKStzMeqOiEK-74_5sgCLcBGAs/s320/Screenshot%2Bat%2B2017-11-01%2B02%253A20%253A19.png" width="320" /></a></div>
<br />
We can identify 3 ! For instance, both '2's are surrounded by many '0's (2-0 = T!), and '4' is obviously way taller than it's neighbors by a value bigger than T.<br />
'1' instead is not selected because the difference between it's height and the adjacent stack's height is smaller than T.<br />
<br />
The next step is to transfer some sand (M) from those 3 stacks to some adjacent lower stack.<br />
<br />
Let's check our options:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-MU7YsAG8zl0/WfmV5m1xFxI/AAAAAAAAx94/Zu2fJxCvi78zMixOC_Nhrkj_jpLTD5oTgCLcBGAs/s1600/Screenshot%2Bat%2B2017-11-01%2B02%253A37%253A20.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="511" data-original-width="509" height="320" src="https://1.bp.blogspot.com/-MU7YsAG8zl0/WfmV5m1xFxI/AAAAAAAAx94/Zu2fJxCvi78zMixOC_Nhrkj_jpLTD5oTgCLcBGAs/s320/Screenshot%2Bat%2B2017-11-01%2B02%253A37%253A20.png" width="318" /></a></div>
<br />
<br />
I marked the possible 'falling' directions with arrows. For instance '4' can fall in all the directions cause it's literally surrounded by lower stacks. So ... what direction shall we choose ? <b><u>Let's pick one randomly</u></b>.<br />
In the pic above I randomly choose one arrow (the red one), as the 'falling' direction.<br />
<br /></div>
<div>
<div style="text-align: justify;">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-J1mubBDFfms/WfmXa7sMR4I/AAAAAAAAx-E/bbJN6_ePbuYFn8GkiUKl3N2uAyJ8uNd3gCLcBGAs/s1600/Screenshot%2Bat%2B2017-11-01%2B02%253A43%253A58.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="511" data-original-width="508" height="320" src="https://3.bp.blogspot.com/-J1mubBDFfms/WfmXa7sMR4I/AAAAAAAAx-E/bbJN6_ePbuYFn8GkiUKl3N2uAyJ8uNd3gCLcBGAs/s320/Screenshot%2Bat%2B2017-11-01%2B02%253A43%253A58.png" width="318" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<b>Pic A</b></div>
<br /></div>
</div>
</div>
</div>
<div>
Cool. So the first '2' will fall left, '4' will fall down, and the last '2' will fall right.<br />
What does 'fall' mean ?<br />
It means that we'll have to remove M=0.5 from the tall stack (see pic below) ...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-I0UAgdhOARk/WfmX7-HbobI/AAAAAAAAx-M/TC2b0M6YPH0WIx_4dFclwO1E8SJ3VkLiQCLcBGAs/s1600/Screenshot%2Bat%2B2017-11-01%2B02%253A46%253A01.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="509" data-original-width="509" height="320" src="https://1.bp.blogspot.com/-I0UAgdhOARk/WfmX7-HbobI/AAAAAAAAx-M/TC2b0M6YPH0WIx_4dFclwO1E8SJ3VkLiQCLcBGAs/s320/Screenshot%2Bat%2B2017-11-01%2B02%253A46%253A01.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
... and transfer it to the selected adjacent lower stack (see pic below).</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-darZoOW6Hek/WfmYVjtljNI/AAAAAAAAx-Q/-9gz10If2y0Sz6fuJW_sMKClgLGsAROjwCLcBGAs/s1600/Screenshot_2017-11-01_02-47-30.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="509" data-original-width="509" height="320" src="https://3.bp.blogspot.com/-darZoOW6Hek/WfmYVjtljNI/AAAAAAAAx-Q/-9gz10If2y0Sz6fuJW_sMKClgLGsAROjwCLcBGAs/s320/Screenshot_2017-11-01_02-47-30.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
More specifically, in this last step <b>for each stack we need to add M as many times as incoming arrows.</b></div>
<div class="separator" style="clear: both; text-align: justify;">
<b><br /></b></div>
<div class="separator" style="clear: both; text-align: justify;">
At this point we have a new state, and we need to run the same steps again and again, until all the stacks are stable.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Remember that the orange stacks will become smaller (M will be subtracted) and the green stacks will become bigger (M will be added).</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Step 2 (unstable)</div>
<div class="separator" style="clear: both; text-align: left;">
<a href="http://4.bp.blogspot.com/-51ijMdhQn7E/WfoNwDgnCpI/AAAAAAAAx-o/9t4IzQbeWnEpbuiObc5xvPEI6KRwxMVFwCK4BGAYYCw/s1600/Screenshot.png"><img border="0" height="200" src="https://4.bp.blogspot.com/-51ijMdhQn7E/WfoNwDgnCpI/AAAAAAAAx-o/9t4IzQbeWnEpbuiObc5xvPEI6KRwxMVFwCK4BGAYYCw/s200/Screenshot.png" width="200" /></a> <a href="http://3.bp.blogspot.com/-sQmwUcf3tD8/WfoN0pnkl7I/AAAAAAAAx-w/4MElMEBP_SIIFjGtBWxNso6IPB5J9ekIQCK4BGAYYCw/s1600/Screenshot-1.png"><img border="0" height="200" src="https://3.bp.blogspot.com/-sQmwUcf3tD8/WfoN0pnkl7I/AAAAAAAAx-w/4MElMEBP_SIIFjGtBWxNso6IPB5J9ekIQCK4BGAYYCw/s200/Screenshot-1.png" width="200" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Step 3 (unstable)</div>
<div class="separator" style="clear: both; text-align: left;">
<a href="http://4.bp.blogspot.com/-VC2tBCeY3zY/WfoOdZiCdqI/AAAAAAAAx_c/eZvhihTjAxg0AuSS9kM1wjvKn8jOOdJ2wCK4BGAYYCw/s1600/Screenshot-2.png"><img border="0" height="200" src="https://4.bp.blogspot.com/-VC2tBCeY3zY/WfoOdZiCdqI/AAAAAAAAx_c/eZvhihTjAxg0AuSS9kM1wjvKn8jOOdJ2wCK4BGAYYCw/s200/Screenshot-2.png" width="199" /> </a><a href="http://2.bp.blogspot.com/-QhTVNJGpYEA/WfoOf_2e2NI/AAAAAAAAx_k/aRBFVzSsDrQQVGKq3bkReCeTTzMDfCfggCK4BGAYYCw/s1600/Screenshot-3.png"><img border="0" height="199" src="https://2.bp.blogspot.com/-QhTVNJGpYEA/WfoOf_2e2NI/AAAAAAAAx_k/aRBFVzSsDrQQVGKq3bkReCeTTzMDfCfggCK4BGAYYCw/s200/Screenshot-3.png" width="200" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Step 4 (unstable)</div>
<div class="separator" style="clear: both; text-align: left;">
<a href="http://2.bp.blogspot.com/-Sj1ecuo-QM8/WfoOpujr0cI/AAAAAAAAx_s/-2LgklhnzCkJwZmaMDd6ReulcG28or6oQCK4BGAYYCw/s1600/Screenshot-4.png"><img border="0" height="200" src="https://2.bp.blogspot.com/-Sj1ecuo-QM8/WfoOpujr0cI/AAAAAAAAx_s/-2LgklhnzCkJwZmaMDd6ReulcG28or6oQCK4BGAYYCw/s200/Screenshot-4.png" width="199" /> </a><a href="http://4.bp.blogspot.com/-wcxw2cQZu9M/WfoO6yDE8kI/AAAAAAAAyAI/Wm4TgYHTfCwfzH0RXGAqTq0so9P6nGhoACK4BGAYYCw/s1600/Screenshot-5.png"><img border="0" height="200" src="https://4.bp.blogspot.com/-wcxw2cQZu9M/WfoO6yDE8kI/AAAAAAAAyAI/Wm4TgYHTfCwfzH0RXGAqTq0so9P6nGhoACK4BGAYYCw/s200/Screenshot-5.png" width="200" /></a><a href="http://2.bp.blogspot.com/-aTNyYleUq6c/WfoOrgHP4jI/AAAAAAAAx_0/KOIukFZVoz41cVN0VVk1WFpotHA9mhZ8gCK4BGAYYCw/s1600/Screenshot-5.png"></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Step 5 (stable !!)</div>
<div class="separator" style="clear: both; text-align: left;">
<a href="http://1.bp.blogspot.com/-jZDUvAy8kBg/WfoO-Zi_VmI/AAAAAAAAyAQ/LdRJ32FUY8QAAtcL25bInS5XGkfQZkYBgCK4BGAYYCw/s1600/Screenshot-6.png"><img border="0" height="200" src="https://1.bp.blogspot.com/-jZDUvAy8kBg/WfoO-Zi_VmI/AAAAAAAAyAQ/LdRJ32FUY8QAAtcL25bInS5XGkfQZkYBgCK4BGAYYCw/s200/Screenshot-6.png" width="200" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Why is the last state stable ? Because no stack is surrounded by stacks lower than itself by T.</div>
<div class="separator" style="clear: both; text-align: justify;">
In other words, sand rocks have no reason to fall anywhere cause they're all supported by solid neighbors.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Cool.</div>
<div class="separator" style="clear: both; text-align: left;">
There's an <b><u>important</u></b> consideration to make.</div>
<div class="separator" style="clear: both; text-align: left;">
You might be tempted to do the following:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
For each stack A ...</div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<ul>
<li>get the 4 neighbors</li>
<li>if any neighbor is lower than A by (say) 2 then</li>
<ul>
<li>reduce A by (say) 0.5</li>
<li>increase one of those 'low' neighbors by 0.5</li>
</ul>
</ul>
<div>
<br /></div>
<div>
<div style="text-align: justify;">
Well this would NOT work because during one iteration you're modifying one neighbor. But in the next iteration that neighbor might become the current stack !</div>
</div>
<div>
<div style="text-align: justify;">
So .. we need to do this in 2 steps, as showed above in Sandpile Algorithm 3d.</div>
</div>
<div>
<br /></div>
<h3>
STEP 1 - identifying the unstable stacks only</h3>
<div>
For each stack <b>A</b> ...</div>
<div>
<ul>
<li>if it's surrounded by any stack lower than T, choose one randomly ... </li>
</ul>
</div>
<div>
<div style="text-align: justify;">
For instance if the stacks Up and Left of A are 'low', we can randomly choose one, say Up. So we'll mark A with 'U'. (so the choices are, U, D, L, R for an <u>unstable</u> A stack).</div>
</div>
<div>
<div style="text-align: justify;">
If A is NOT surrounded by 'low' stacks, then we mark A as 'S' (for <u>stable</u>).</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-uiKwV1_bRs0/WnVRt-nENiI/AAAAAAAA3ik/2ROdB2VR5RU3FXhvLrey4HgS3BVaJf71gCLcBGAs/s1600/sketch.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="707" data-original-width="1600" height="141" src="https://2.bp.blogspot.com/-uiKwV1_bRs0/WnVRt-nENiI/AAAAAAAA3ik/2ROdB2VR5RU3FXhvLrey4HgS3BVaJf71gCLcBGAs/s320/sketch.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<b>Pic B</b>: This corresponds to <b>Pic A</b> (scroll up a few pics).</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div>
<br /></div>
<h3>
STEP 2 - sand transfer</h3>
<div>
Now we can loop over each stack again and actually perform the transfer.</div>
<div>
More specifically.</div>
<div>
For every stack A...</div>
<div>
<ul>
<li>if A is NOT marked with S, reduce A by M (remember we set M = 0.5).<br />Or, simply put, if A is unstable has to drop some sand.</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-PN9vMe2jBJk/WnVV_fXMLlI/AAAAAAAA3iw/NLReNctYlJckWfnmdLwMNwCgSEK-jbuGgCLcBGAs/s1600/Sketch%2B%25281%2529.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1006" data-original-width="1380" height="233" src="https://1.bp.blogspot.com/-PN9vMe2jBJk/WnVV_fXMLlI/AAAAAAAA3iw/NLReNctYlJckWfnmdLwMNwCgSEK-jbuGgCLcBGAs/s320/Sketch%2B%25281%2529.png" width="320" /></a></div>
<div>
<br /></div>
<ul>
<li style="text-align: justify;">if the up stack is marked D (down) it means that the north stack wants to transfer some sand to the stack to his south, which is A. So increase A by M.</li>
<li style="text-align: justify;">if the down stack is marked U (up) it means that the south stack wants to transfer some sand to the stack to his north, which is A. So increase A by M.</li>
<li style="text-align: justify;">if the left stack is marked R (right) it means that the left stack wants to transfer some sand to the stack to his right, which is A. So increase A by M.</li>
<li style="text-align: justify;">if the right stack is marked L (left) it means that the right stack wants to transfer some sand to the stack to his left, which is A. So increase A by M.</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-gAHSWzYB8e0/WnVWM-6EDYI/AAAAAAAA3i0/ID54afgrs4QvfsgWIm_T7pS4mtrzrL4_gCLcBGAs/s1600/sketch.png"><img border="0" data-original-height="754" data-original-width="944" height="255" src="https://4.bp.blogspot.com/-gAHSWzYB8e0/WnVWM-6EDYI/AAAAAAAA3i0/ID54afgrs4QvfsgWIm_T7pS4mtrzrL4_gCLcBGAs/s320/sketch.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<b>Pic C</b></div>
<div>
<br /></div>
<div>
<div style="text-align: justify;">
Note that in both Step 1 and 2 we're modifying only the stack currently looped on (A). The neighbors are inspected, never modified. Which happens to be exactly how Houdini VEX works ! What an incredible coincidence (it's not, I did it on purpose since the beginning ha!).</div>
<br />
Cool.<br />
Let's summarize the whole thing in pseudo-code.<br />
<br />
<b>STEP 1 (pseudo code)</b><br />
<br />
<ol>
<li>set T = 2 (T is the height threshold)</li>
<li>for each stack A</li>
<ol>
<li>h = height of A</li>
<li>hU = A's UP neighbor height</li>
<li>hD = A's DOWN neighbor height</li>
<li>hL = A's LEFT neighbor height</li>
<li>hR = A's RIGHT neighbor height</li>
<li>fU = 1 if hU is smaller than h by at least T, 0 otherwise. (or ... h-hU>=T) </li>
<li>fD = 1 if hD is smaller than h by at least T, 0 otherwise. (or ... h-hD>=T)</li>
<li>fL = 1 if hL is smaller than h by at least T, 0 otherwise. (or ... h-hL>=T)</li>
<li>fR = 1 if hR is smaller than h by at least T, 0 otherwise. (or ... h-hR>=T)</li>
<li>if (any of fU or fD or fL or fR is 1) </li>
<ol>
<li>then pick one randomly and tag A with that label (example if we choose fU, A is marked with 'U').</li>
<li>else, mark A with 'S' (as stable)</li>
</ol>
</ol>
</ol>
</div>
</div>
<div>
At this stage all the stacks should have a tag that can be 'U', 'D', 'L', 'R', 'S' (like in Pic B).<br />
<br />
<b>STEP 2 (pseudo code)</b><br />
<br />
<ol>
<li>set M = 0.5 (M is the mass transfer between sand stacks)</li>
<li>for each stack A</li>
<ol>
<li>h = A's height</li>
<li>u = label of A's UP neighbor (could be 'U', 'D', 'L', 'R', 'S')</li>
<li>d = label of A's DOWN neighbor (could be 'U', 'D', 'L', 'R', 'S')</li>
<li>l = label of A's LEFT neighbor (could be 'U', 'D', 'L', 'R', 'S')</li>
<li>r = label of A's RIGHT neighbor (could be 'U', 'D', 'L', 'R', 'S')</li>
<li>if (u is 'D') then h = h + M</li>
<li>if (d is 'U') then h = h + M</li>
<li>if (l is 'R') then h = h + M</li>
<li>if (r is 'L') then h = h + M</li>
<li>if (A's label is different than 'S') then h = h - M</li>
</ol>
</ol>
<div>
Cool ! This is pretty much the whole idea.<br />
<br /></div>
<div>
<div style="text-align: justify;">
<b>PART 2</b> - <a href="https://pepefx.blogspot.com/2019/03/sand-without-grains-part-2-vex.html" target="_blank"><b>Implementing a HeightField Pandpile Solver in Houdini with VEX</b></a></div>
</div>
<div>
<div>
<br />
<div style="text-align: justify;">
<b>PART 3 -</b> <b>Implementing a HeightField Sandpile Solver in Houdini with <u>OpenCL</u> </b>(coming a little later than soon)</div>
<div style="text-align: justify;">
This algorithm is already fast cause it involves very simple math, nevertheless if you want to have production quality (<a href="https://media.giphy.com/media/saD01LsKDzxL2/giphy.gif">don't we all ?</a> ) you might want to use a very large grid with a ton of detail. In that case, why not speed up the calculation by say ... 10 or 20 times converting the code into OpenCL ?</div>
</div>
</div>
</div>
<br />
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
</div>
<br />Unknownnoreply@blogger.com3Wellington, New Zealand-41.2864603 174.77623600000004-41.6685158 174.13078900000005 -40.9044048 175.42168300000003tag:blogger.com,1999:blog-5626905874114803923.post-6276008064483395842017-02-12T22:50:00.000-08:002019-06-27T12:06:38.438-07:00HUG L.A. Presentation - Nov 2016<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-rGjaAcZZTSU/WKFVMNAXVNI/AAAAAAAAoYk/brlBIcpaxuoYFR0IGgN1FidbIpQKOHsegCLcB/s1600/ink_pres_pic.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="218" src="https://3.bp.blogspot.com/-rGjaAcZZTSU/WKFVMNAXVNI/AAAAAAAAoYk/brlBIcpaxuoYFR0IGgN1FidbIpQKOHsegCLcB/s400/ink_pres_pic.png" width="400" /></a></div>
A few months ago I had the honor to present L.A. Hug Nov 2016, where I explained in detail the workflow I used to produce this tornado fx.<br />
<div>
<br /></div>
<div style="text-align: justify;">
The event was hosted in Framestore L.A.</div>
<div style="text-align: justify;">
You'll find the details <a href="https://www.meetup.com/LA-Houdini-User-Group/events/235146114/">Here</a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
And below you'll find the presentation PDF and Hip files (H15.5) for both the Tornado and the Ink effect.</div>
<br />
<a href="https://2.bp.blogspot.com/-3mH3SzQmETQ/WKFVNhci6LI/AAAAAAAAoYo/ecUUuTUHskcLxBitWhlXkrWgzsJkrUxEQCLcB/s1600/volumeDispl_pres_pic.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="213" src="https://2.bp.blogspot.com/-3mH3SzQmETQ/WKFVNhci6LI/AAAAAAAAoYo/ecUUuTUHskcLxBitWhlXkrWgzsJkrUxEQCLcB/s400/volumeDispl_pres_pic.png" width="400" /></a><a href="https://drive.google.com/file/d/0B_GDFhnt3Ph_QmFRcTU5Tm5rbkk/view?usp=sharing">HUG-Nov2016 Presentation and Files</a><br />
<br />
Enjoy and if you've question please don't hesitate to contact me.<br />
<br />
Video Breakdown:<br />
<a href="https://vimeo.com/153202998" target="_blank">https://vimeo.com/153202998</a>Unknownnoreply@blogger.com8tag:blogger.com,1999:blog-5626905874114803923.post-86999193998697669692016-04-22T01:28:00.000-07:002017-07-22T13:02:47.869-07:00Cigarette Smoke - Part 1<div style="text-align: justify;">
Before you start reading, be aware there is a Part 2, and you'll find it here :<br />
<a href="http://www.gridmarkets.com/alessandro-pepe">LINK</a><br />
<br />
But don't read it before reading Part 1. So pretend I didn't say anything.<br />
<span style="font-size: x-small;"><br /></span>
Now for real.<br />
<br />
I don't think there is a single person on earth who didn't spent at least 10 minutes of their life contemplating in awe the beautiful shapes drawn in mid air by an incense stick or cigarette smoke. If I had to put together the time I spent observing smoke till this day I'm sure it wouldn't be less than a week.<br />
<br />
I found this beautiful reference video on Vimeo.<br />
Let's watch it together for a little while.</div>
<div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><div style="text-align: left;">
<br /></div>
<a href="https://2.bp.blogspot.com/-HFdQT8cKcMA/VxRDXf5l3sI/AAAAAAAAaBE/EDRmcrtV9fIgAdOm1xykslEnLb8lXHWPACLcB/s1600/Screenshot%2Bfrom%2B2016-04-17%2B19%253A15%253A41.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="142" src="https://2.bp.blogspot.com/-HFdQT8cKcMA/VxRDXf5l3sI/AAAAAAAAaBE/EDRmcrtV9fIgAdOm1xykslEnLb8lXHWPACLcB/s400/Screenshot%2Bfrom%2B2016-04-17%2B19%253A15%253A41.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><a href="https://vimeo.com/32196544">Cigarette Smoke Reference</a></td></tr>
</tbody></table>
<br />
<div>
<div style="text-align: justify;">
Many years ago I tried to create this fx in Maya. I recall the attempt, but I can't remember the outcome. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Let's try with Houdini. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The first solution that crossed my mind was the same I would apply to generate ink in water: lots and lots of points advected by the velocity field generated by a Pyro/Smoke sim.<br />
The only difference would simply be the distribution of the points source.</div>
<div style="text-align: justify;">
Let's try this approach first.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Here I created an low-res smoke sim with a little bit of turbulence.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-MJnxKQwA3u0/Vxc8FBSqUHI/AAAAAAAAaFY/b6VHK8dQh20sdeU1VZLFrPCbG9C9uvjlgCKgB/s1600/animaion.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="360" src="https://4.bp.blogspot.com/-MJnxKQwA3u0/Vxc8FBSqUHI/AAAAAAAAaFY/b6VHK8dQh20sdeU1VZLFrPCbG9C9uvjlgCKgB/s640/animaion.gif" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br /></div>
<div style="text-align: justify;">
Let's cache this sim (only "vel" and "density" volume fields are really required for this tutorial).<br />
<br />
<span style="font-size: large;"><u>OPTION 1 : Advect (a lot of) Points</u></span><br />
<br />
Now let's advect a points along the velocity field generated by this sim.<br />
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
In the pic below, in the right branch, import your smoke sim, convert it to VDB and make sure to use a VDB Vector Merge to merge vel.x, vel.y, vel.z in a single VDB vector field "vel".</div>
<div style="text-align: justify;">
Connect this branch to the right input of the Solver SOP.</div>
<br />
<div style="text-align: justify;">
In the left branch I just imported the same geometry I used to emit the smoke, made it really small and scattered points (make sure to randomize the Scatter seed per frame). This will be the points emitter.</div>
<div style="text-align: justify;">
Connect this branch to the left input of the Solver SOP.</div>
</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-sySLKF9-aQ0/VxXYgszBWBI/AAAAAAAAaCs/-dceHkXMmSECJmoVIXPDAKren-10GzyWwCLcB/s1600/01_capture.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="305" src="https://4.bp.blogspot.com/-sySLKF9-aQ0/VxXYgszBWBI/AAAAAAAAaCs/-dceHkXMmSECJmoVIXPDAKren-10GzyWwCLcB/s400/01_capture.png" width="400" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<b>Note 1 :</b></div>
<div style="text-align: justify;">
<b>The reason cause I made the points emitter very small is because I want to keep the advected points as close as possible for as long as possible during the simulation, to mimic the behavior of this kind of smoke. The velocity field will be in charge of moving those points, so the closer the points, the higher the chances that they will live in contiguous velocity voxels, or maybe even in the same voxel, and consequently move together, at least for that specific time step !</b><br />
<br />
<b>Note 2:</b><br />
<b>After the scatter node I added a Transform node , and added an expression to animate the scale using trigonometric functions (see pic below for details, but feel free to experiment with different values of course). Why ? Well, if you notice in the reference video at the beginning of this article, the width of the stream of smoke taking off from the source changes a lot. This is important to add variety to the evolution of the smoke, and to mimic nature as best as we can of course !</b><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-RnAPAucRlYk/VxdF0DOO2YI/AAAAAAAAaGc/X1H6oLOTs2EokQc1d11Gy09B0aThRR4JwCLcB/s1600/Screenshot%2B-%2B04202016%2B-%2B02_01_01%2BAM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="192" src="https://1.bp.blogspot.com/-RnAPAucRlYk/VxdF0DOO2YI/AAAAAAAAaGc/X1H6oLOTs2EokQc1d11Gy09B0aThRR4JwCLcB/s400/Screenshot%2B-%2B04202016%2B-%2B02_01_01%2BAM.png" width="400" /></a></div>
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The content of the Solver is the usual feedback loop with an injection of new points every frame.</div>
<div style="text-align: justify;">
If we assume that the first frame of the range is 1, at frame 1 the switch node will let pass the first set of scattered points.</div>
<div style="text-align: justify;">
At frame 2 the switch node will let pass the previous state, merged with the second set of scattered points. And so on for all the frames after 2.</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-8cfDMTbVXoE/VxXWsokKcGI/AAAAAAAAaCg/mW9jNbnJS8AmIBO3KAYdTwYz8PhlhFZWACLcB/s1600/02_capture.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="401" src="https://1.bp.blogspot.com/-8cfDMTbVXoE/VxXWsokKcGI/AAAAAAAAaCg/mW9jNbnJS8AmIBO3KAYdTwYz8PhlhFZWACLcB/s640/02_capture.png" style="cursor: move;" width="640" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Note :</div>
<div style="text-align: justify;">
You don't have to use a Solver for this setup. You can achieve the same result using a POP Network and a POP Points Advect. I prefer to use a Solver cause of my masochistic tendency of creating everything from scratch as an exercise. </div>
<div style="text-align: justify;">
And because VDB Points Advect is much faster than POP Points Advect.</div>
<div style="text-align: justify;">
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-Ag8_QV7BfyM/Vxc9N4KzqXI/AAAAAAAAaFk/XuPNh6yNvcAUHYsigS-R62dEYy00736SACLcB/s1600/animated.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="360" src="https://1.bp.blogspot.com/-Ag8_QV7BfyM/Vxc9N4KzqXI/AAAAAAAAaFk/XuPNh6yNvcAUHYsigS-R62dEYy00736SACLcB/s640/animated.gif" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
</div>
<div style="text-align: justify;">
This is the result scattering 100 points per frame.</div>
<div style="text-align: justify;">
Let's try with 1000 points per frame.<br />
<br /></div>
<div style="text-align: justify;">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-uQC2_Ygf5W0/VxiC3M2Z7WI/AAAAAAAAaHk/p4XKfVq495cfj2rwYIYcpCZdV_gmtEkBgCLcB/s1600/animated.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="360" src="https://1.bp.blogspot.com/-uQC2_Ygf5W0/VxiC3M2Z7WI/AAAAAAAAaHk/p4XKfVq495cfj2rwYIYcpCZdV_gmtEkBgCLcB/s640/animated.gif" width="640" /></a></div>
<br /></div>
<div style="text-align: justify;">
Better , but still there are sparse, disconnected points.</div>
<div style="text-align: justify;">
Furthermore, I don't like the stepped pattern in the lower part.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-3lZkYQxXxkc/Vxc-qdskVcI/AAAAAAAAaFw/kFkckOLUWKE7RwrEGlHRURoLJ0TiLPP4ACLcB/s1600/Screenshot%2Bfrom%2B2016-04-20%2B01_31_05.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://1.bp.blogspot.com/-3lZkYQxXxkc/Vxc-qdskVcI/AAAAAAAAaFw/kFkckOLUWKE7RwrEGlHRURoLJ0TiLPP4ACLcB/s200/Screenshot%2Bfrom%2B2016-04-20%2B01_31_05.png" width="199" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Let's "smear" the initial position of the source points along "vel":<br />
<br />
Add a Point Wrangle SOP (red node below) and connect the input points to the input 1, and the volumes to the input 2.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-3eXV5WvLBLA/VxdB7341LMI/AAAAAAAAaF8/z1VQQIGCm5UGErP_Yg4cSH2NV9geG1IQACLcB/s1600/Screenshot%2Bfrom%2B2016-04-20%2B01_41_11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="393" src="https://2.bp.blogspot.com/-3eXV5WvLBLA/VxdB7341LMI/AAAAAAAAaF8/z1VQQIGCm5UGErP_Yg4cSH2NV9geG1IQACLcB/s400/Screenshot%2Bfrom%2B2016-04-20%2B01_41_11.png" width="400" /></a></div>
</div>
<div style="text-align: justify;">
<br />
In the Point Wrangle node enter this vex code:<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">vector vel=volumesamplev(@OpInput2,"vel",v@P);<br />v@P+=float(random(i@ptnum*3.432))*normalize(vel)*0.025;</span></blockquote>
I used 0.025 in my sim cause it was working for me. If you still see 'steps' in the lower part of the sim, feel free to change this value to whatever fits your sim.<br />
<br />
If you play the sim now the lower part should look roughly like this.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-cg93xULB-aY/VxdCa7oAJdI/AAAAAAAAaGA/VNIHHPig6OkYcrnDW_fzztCt6o3bxISigCLcB/s1600/Screenshot%2Bfrom%2B2016-04-20%2B01_45_25.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://1.bp.blogspot.com/-cg93xULB-aY/VxdCa7oAJdI/AAAAAAAAaGA/VNIHHPig6OkYcrnDW_fzztCt6o3bxISigCLcB/s320/Screenshot%2Bfrom%2B2016-04-20%2B01_45_25.png" width="150" /></a></div>
<br /></div>
<div style="text-align: justify;">
I guess this solves the stepping issue...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-0PJc5Jz-54I/VxdEgfGQlrI/AAAAAAAAaGQ/QQaJ_HBCmvkQUeB8F62pJqYAuUOo9885ACLcB/s1600/animated.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://2.bp.blogspot.com/-0PJc5Jz-54I/VxdEgfGQlrI/AAAAAAAAaGQ/QQaJ_HBCmvkQUeB8F62pJqYAuUOo9885ACLcB/s400/animated.gif" width="361" /></a></div>
<br />
...but we still got the sparse / disconnected points in the upper part of the sim.<br />
<br /></div>
<div style="text-align: justify;">
Let's try to reduce the points sparse-ness using the Gradient Vector Field generated by the density field . <i>A quick way that helped me to understand the Gradient vector is the following : the Gradient vector field is for a density scalar field what normals are for a surface. In other words think of the Gradient as the normal vector for the density field. What we want to do is a sort of 'peak' SOP in Volume-land. We want to "shrink" the points along a normal vector (which for volumes is called Gradient), just a little bit, every time step.</i></div>
<div style="text-align: justify;">
<br />
What we want to do is advect the points along the Gradient vector field, on top of advecting along "vel". Double advection !! (it'll be slightly slower, but it's worth).<br />
<br /></div>
<div style="text-align: justify;">
Separate "vel" from the "density", apply a VDB Analysis set to "gradient", and make sure to explicitly set the name for the resulting volume to "gradient", or "grad". Then merge it back to the original branch with "vel" and "density".<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-LgwDqeqzqwk/Vxm0O-I90JI/AAAAAAAAaIA/eqE4nadieO0NF4MSgBLuS668bVwkY0tUQCLcB/s1600/Screenshot%2Bfrom%2B2016-04-21%2B22_17_39.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="392" src="https://2.bp.blogspot.com/-LgwDqeqzqwk/Vxm0O-I90JI/AAAAAAAAaIA/eqE4nadieO0NF4MSgBLuS668bVwkY0tUQCLcB/s640/Screenshot%2Bfrom%2B2016-04-21%2B22_17_39.png" width="640" /></a></div>
<br />
Now, dive in Solver1 and add an extra VDB Advect Points , and make sure to shorten the timestep advection by at least 0.01 (feel free to play with this number).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-hD39XbkZSPo/Vxm4vMjNvAI/AAAAAAAAaIM/uAOfzKUKTi4IBhoJzfGXnd7a1ZKo8hniwCLcB/s1600/Screenshot%2B-%2B04212016%2B-%2B10_35_40%2BPM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="388" src="https://1.bp.blogspot.com/-hD39XbkZSPo/Vxm4vMjNvAI/AAAAAAAAaIM/uAOfzKUKTi4IBhoJzfGXnd7a1ZKo8hniwCLcB/s640/Screenshot%2B-%2B04212016%2B-%2B10_35_40%2BPM.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
The reason cause we need to resize the timestep for the Gradient advection is that we want to push the points towards the zone of higher density only a tiny bit for each point, and every frame. If we keep this number to 1, the points will overshoot.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-t-4zm1BoVLg/Vxm5m0bfAoI/AAAAAAAAaIU/KSDTORi5Eh0FPai1I5_OnuG46b9zAR7UACLcB/s1600/animated.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://1.bp.blogspot.com/-t-4zm1BoVLg/Vxm5m0bfAoI/AAAAAAAAaIU/KSDTORi5Eh0FPai1I5_OnuG46b9zAR7UACLcB/s1600/animated.gif" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
This is working much better. Notice how the points tend to converge to curves and we have much less sparse points now.</div>
<div class="separator" style="clear: both; text-align: justify;">
This is a possible solution and if you emit enough points you can get decent results.</div>
<div class="separator" style="clear: both; text-align: justify;">
Personally I prefer the next solution.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<u style="font-size: x-large;">OPTION 2 : Advect Lines</u></div>
<div class="separator" style="clear: both; text-align: justify;">
<u style="font-size: x-large;"><br /></u></div>
<div class="separator" style="clear: both; text-align: justify;">
Apparently the problem that we have with Option 1, is the sparse points. We are trying every possible workaround to keep those points in lines. </div>
<div class="separator" style="clear: both; text-align: justify;">
So .... why don't we advect lines instead of points ?</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Let's try.</div>
<div class="separator" style="clear: both; text-align: justify;">
First off , we need to remove the expression from Scatter SOP seed. This way, we'll make sure to have a consistent point source where each point will maintain it's own id (@ptnum). </div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-3cjTF2MKP2s/Vxm_x7Yw9XI/AAAAAAAAaIs/QY6DIyI5w3gN6btqOmuCTRg74l1p_PLDQCLcB/s1600/Screenshot%2Bfrom%2B2016-04-21%2B23_07_32.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="338" src="https://3.bp.blogspot.com/-3cjTF2MKP2s/Vxm_x7Yw9XI/AAAAAAAAaIs/QY6DIyI5w3gN6btqOmuCTRg74l1p_PLDQCLcB/s400/Screenshot%2Bfrom%2B2016-04-21%2B23_07_32.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Next, we need to assign some kind of id to the source points. This way later we can use that id to create lines using Add SOP. Since we are no longer randomizing the point scattering per frame, @ptnum will work perfectly as id.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Add a Point Wrangle to the source branch and enter this VEX code:</div>
<blockquote class="tr_bq" style="clear: both; text-align: justify;">
<span style="font-family: "courier new" , "courier" , monospace;"><br />i@id=@ptnum;</span></blockquote>
Your network should look more or less like this ...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-kqSe2QoeK94/Vxm9QABag3I/AAAAAAAAaIg/FxAt3tcjpBolbT3PTX0eRpAM-15OdGcEgCLcB/s1600/Screenshot%2Bfrom%2B2016-04-21%2B22_56_27.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="448" src="https://2.bp.blogspot.com/-kqSe2QoeK94/Vxm9QABag3I/AAAAAAAAaIg/FxAt3tcjpBolbT3PTX0eRpAM-15OdGcEgCLcB/s640/Screenshot%2Bfrom%2B2016-04-21%2B22_56_27.png" width="640" /></a></div>
<br />
<br />
<div class="separator" style="clear: both; text-align: justify;">
Good. Now the plan is to do the following every time step (in the Solver):</div>
<div class="separator" style="clear: both; text-align: justify;">
</div>
<ul>
<li>advect the points as usual (we already have this part)</li>
<li>create lines connecting all the points with the same i@id</li>
<li>resample the lines</li>
<li>smooth the lines (this is optional but highly reccomended)</li>
<li>delete the lines and keep only the points</li>
</ul>
<div>
Why do we create lines ? </div>
<div>
So we can resample them.</div>
<div>
<br /></div>
<div>
Why do we need to resample them ? </div>
<div>
Cause after the advection step we don't want to loose detail. So we resample the lines at fixed sized segments, and this way we are sure that every line will always be nice and smooth.</div>
<div>
<br /></div>
<div>
Why do we smooth ? </div>
<div>
To add extra niceness and smoothness (as per beautiful video reference).</div>
<div>
<br /></div>
<div>
Why (on earth) do we delete the lines that we just created ?</div>
<div>
Because new points are introduced at every time step. We could extend the lines with the new points but it's easier to just delete the lines at the end of the time-step, and re-create them at the beginning of the next time step after injecting the new source points.</div>
<br />
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Let's to that.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
In the Solver append the following nodes and parameters.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-5Nn7lQjtBuE/VxnEXOu4DEI/AAAAAAAAaI4/Vb-YPDTRnoMOC1YHonEZW-qb2axifYz7gCLcB/s1600/Screenshot%2Bfrom%2B2016-04-21%2B23_27_09.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://1.bp.blogspot.com/-5Nn7lQjtBuE/VxnEXOu4DEI/AAAAAAAAaI4/Vb-YPDTRnoMOC1YHonEZW-qb2axifYz7gCLcB/s640/Screenshot%2Bfrom%2B2016-04-21%2B23_27_09.png" width="592" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
And immediately after the Solver plug an extra Add SOP with the same settings as he first Add SOP in the image above.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-kqxMOySieAE/VxnHYNaa60I/AAAAAAAAaJE/L5p6m6POk8kBBkatXR9dER-8MgNDL4xuACLcB/s1600/Screenshot%2Bfrom%2B2016-04-21%2B23_39_57.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="340" src="https://1.bp.blogspot.com/-kqxMOySieAE/VxnHYNaa60I/AAAAAAAAaJE/L5p6m6POk8kBBkatXR9dER-8MgNDL4xuACLcB/s640/Screenshot%2Bfrom%2B2016-04-21%2B23_39_57.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Set the number of Scattered points to 10 and run the Sim.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-iklc9SGWGqI/VxnHv3t3YgI/AAAAAAAAaJI/YG8d7NivwjMGU6EuiOA5mqYlj411w0gXQCLcB/s1600/animated.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://2.bp.blogspot.com/-iklc9SGWGqI/VxnHv3t3YgI/AAAAAAAAaJI/YG8d7NivwjMGU6EuiOA5mqYlj411w0gXQCLcB/s1600/animated.gif" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Finally we got rid of the sparse points and honestly this version, with only 10 lines, looks much much better than the Option 1.</div>
<div class="separator" style="clear: both; text-align: justify;">
Of course now, instead of having sparse points, we have sparse lines ! But somehow this works much better because the characteristic feature of this kind of smoke is curved lines in space. Which is exactly what we got.</div>
<div class="separator" style="clear: both; text-align: justify;">
Furthermore, sparse points are sparse in every dimension. Sparse lines at least are continuous in one dimension, the one that counts. Plus, remember that we scattered only 10 points so far.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
This is the main gist of this technique and if you're interested <a href="https://drive.google.com/file/d/0B_GDFhnt3Ph_YnBuZzkyNDM0RjQ/view?usp=sharing" target="_blank">HERE</a> you will find the Project File.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Now, let's render this thing.</div>
<div class="separator" style="clear: both; text-align: justify;">
In the following iteration I worked a bit on the Render side.</div>
<div class="separator" style="clear: both; text-align: justify;">
</div>
<ul>
<li>added age and life to the points (if you use the POP Network approach age and life are created automatically)</li>
<li>scattered 1000 points (= 1000 lines)</li>
<li>created a "density" attribute mapped to the normalized age</li>
<li>Rasterized a VDB from the curves using Volume Rasterize and sampled the above mentioned density attribute into a Volume Density Attribute</li>
<li>assigned a simple Pyro shader</li>
<li>rendered in Mantra</li>
<li>added a bit of glow and bluish tint in Nuke</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-JTrDvJpqp2c/VxnOMJJ2ylI/AAAAAAAAaJc/8Q3ETGppBlcaBrpbwXdd2TBDjXKjkEh5ACLcB/s1600/animated.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://1.bp.blogspot.com/-JTrDvJpqp2c/VxnOMJJ2ylI/AAAAAAAAaJc/8Q3ETGppBlcaBrpbwXdd2TBDjXKjkEh5ACLcB/s640/animated.gif" width="438" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
</div>
<div class="separator" style="clear: both;">
</div>
If you're interested in Part 2, you'll find it <a href="http://www.gridmarkets.com/alessandro-pepe">HERE</a><br />
<br />
<div>
</div>
<br />
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Thank you for reading and let me know if you come up with some idea to improve this technique.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div>
<br /></div>
<br />
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
</div>
</div>
</div>
Unknownnoreply@blogger.com61Los Angeles, CA, USA34.0522342 -118.243684933.2099567 -119.5345784 34.8945117 -116.95279140000001tag:blogger.com,1999:blog-5626905874114803923.post-71164131260267787212015-08-15T18:12:00.001-07:002019-03-06T11:35:08.131-08:00How to create a Magnetic Vector Field in Houdini - Tutorial<div style="text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;">A few days ago I was chatting with a fellow colleague (Jon Parker, great Houdini Artist) about magnetic fields. So I decided to create a little OTL to generate a vector field starting from a pole cloud distribution and share the workflow, since it's really simple to implement and the results are great. I am not sure (actually I highly doubt) that the following implementation is physically correct in any way, I just wanted to have a way to quickly create a magnetic-looking vector field.</span></div>
<div style="text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;"><br /></span></div>
<div style="text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;">I generally use Wrangle nodes pretty much for everything since it's very quick to create, initialize attributes and modify their value with just a few lines of code. Feel free to use VOP nodes if you prefer a node-based approach.</span></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;">For what concerns Volumes, I generally prefer to use VDB (when I am not forced to use Houdini Volumes). VDB have an optimized memory structure that allow a very fast access to voxels data, plus they are sparse so they have a lower memory foot print compared to Houdini Volumes. They are slightly more tricky to use, but the reward is huge in terms of speed and memory.</span></div>
<div style="text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;"><br /></span></div>
<div style="text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;">Let's start simple. Two points with a "f@pole" attribute that can be either +1 or -1.<span style="text-align: center;"> </span></span></div>
<div style="text-align: justify;">
<span style="text-align: center;"><span style="font-family: "georgia" , "times new roman" , serif;"><br /></span></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-2MDUOoppa3w/Vc-y4qQ6iII/AAAAAAAAUDo/xNj8kK0Bycc/s1600/Screenshot%2Bfrom%2B2015-08-15%2B14%253A41%253A21.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "georgia" , "times new roman" , serif;"><img border="0" height="178" src="https://3.bp.blogspot.com/-2MDUOoppa3w/Vc-y4qQ6iII/AAAAAAAAUDo/xNj8kK0Bycc/s400/Screenshot%2Bfrom%2B2015-08-15%2B14%253A41%253A21.png" width="400" /></span></a></div>
<div style="text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;"><br /></span></div>
<div style="text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;">For mere visualization purpose I created a little setup where a sphere is copied (copy SOP) to each point ...</span></div>
<div style="text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-DGKa5VjXbpk/Vc-y1qXQY3I/AAAAAAAAUDY/LMpEMOb2fB0/s1600/Screenshot%2Bfrom%2B2015-08-15%2B14%253A39%253A53.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "georgia" , "times new roman" , serif;"><img border="0" height="179" src="https://3.bp.blogspot.com/-DGKa5VjXbpk/Vc-y1qXQY3I/AAAAAAAAUDY/LMpEMOb2fB0/s320/Screenshot%2Bfrom%2B2015-08-15%2B14%253A39%253A53.png" width="320" /></span></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div style="text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;">.. the sphere is RED if the pole is negative, and GREEN if the pole is positive (using a copy SOP and the stamp expression function).</span></div>
<div style="text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: "georgia" , "times new roman" , serif; margin-left: 1em; margin-right: 1em;"><a href="http://1.bp.blogspot.com/-LIdnsl0XYNg/Vc-y5Gty6uI/AAAAAAAAUDw/GoWE9aIFsZk/s1600/Screenshot%2Bfrom%2B2015-08-15%2B14%253A44%253A09.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="189" src="https://1.bp.blogspot.com/-LIdnsl0XYNg/Vc-y5Gty6uI/AAAAAAAAUDw/GoWE9aIFsZk/s320/Screenshot%2Bfrom%2B2015-08-15%2B14%253A44%253A09.png" width="320" /></a></span></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;">Using a wrangle node and the stamp function in one of the parameters I can select the color of each sphere based on <i>f@pole</i> attribute.</span></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: "georgia" , "times new roman" , serif; margin-left: 1em; margin-right: 1em;"><a href="http://4.bp.blogspot.com/-4w9TRQCZeXA/Vc-y4nXSz2I/AAAAAAAAUD4/kgqUbuW_nbk/s1600/Screenshot%2Bfrom%2B2015-08-15%2B14%253A43%253A24.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="156" src="https://4.bp.blogspot.com/-4w9TRQCZeXA/Vc-y4nXSz2I/AAAAAAAAUD4/kgqUbuW_nbk/s400/Screenshot%2Bfrom%2B2015-08-15%2B14%253A43%253A24.png" width="400" /></a></span></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
EDIT : I was completely unaware of <b>chv ( ... )</b> vex command, which allows to read directly a vector parameter from the UI (thank you anonymous poster !!). Which means that the lines above can be written, much more elegantly, this way :</div>
<div class="separator" style="clear: both; text-align: left;">
<span style="color: #741b47;">vector</span> negcol = <span style="color: #0b5394;">chv</span> (<span style="color: #38761d;">"negativecol"</span>);</div>
<div class="separator" style="clear: both; text-align: left;">
<span style="color: #741b47;">vector</span> poscol = <span style="color: #0b5394;">chv</span> (<span style="color: #38761d;">"positivecol"</span>);</div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: "georgia" , "times new roman" , serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;">... your setup should now look more or less like this.</span></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-vGFybDEDVws/Vc-y4iaFy8I/AAAAAAAAUD0/NCmtDd7ZqnE/s1600/Screenshot%2Bfrom%2B2015-08-15%2B14%253A36%253A09.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "georgia" , "times new roman" , serif;"><img border="0" height="183" src="https://3.bp.blogspot.com/-vGFybDEDVws/Vc-y4iaFy8I/AAAAAAAAUD0/NCmtDd7ZqnE/s320/Screenshot%2Bfrom%2B2015-08-15%2B14%253A36%253A09.png" width="320" /></span></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: "georgia" , "times new roman" , serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;">It's now time to create the Volume that will contain our vector field.</span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;">Let's create a VDB node, make sure to give the field a name (I used "magneticfield"), and set it to be a vector field.</span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="http://1.bp.blogspot.com/-Ay-DIcM0icY/Vc-6kv8B6BI/AAAAAAAAUEE/6w-4RMVbBLA/s1600/Screenshot%2Bfrom%2B2015-08-15%2B15%253A14%253A20.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;"><img border="0" height="295" src="https://1.bp.blogspot.com/-Ay-DIcM0icY/Vc-6kv8B6BI/AAAAAAAAUEE/6w-4RMVbBLA/s400/Screenshot%2Bfrom%2B2015-08-15%2B15%253A14%253A20.png" width="400" /></a>T<span style="font-family: "georgia" , "times new roman" , serif;">his will create an empty VDB volume. By default a VDB volume is dimensionless. Why ? Cause VDB is a sparse volume, meaning that it exists only where we want to. Consequently we need to "activate" the VDB in the regions of space where we need it, before actually 'filling' it. </span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="http://2.bp.blogspot.com/-SgSsWF67kHU/Vc-6ksmOkKI/AAAAAAAAUEo/32FWfxevlXM/s1600/Screenshot%2Bfrom%2B2015-08-15%2B15%253A15%253A04.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;"><img border="0" height="152" src="https://2.bp.blogspot.com/-SgSsWF67kHU/Vc-6ksmOkKI/AAAAAAAAUEo/32FWfxevlXM/s320/Screenshot%2Bfrom%2B2015-08-15%2B15%253A15%253A04.png" width="320" /></a><span style="font-family: "georgia" , "times new roman" , serif;">In order to do that, we use "VDB_activate" SOP which allows to use geometry to activate the region. </span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="http://3.bp.blogspot.com/-HkywRLfGAf0/Vc-6kty7AXI/AAAAAAAAUEc/QR4kIwxU4Uo/s1600/Screenshot%2Bfrom%2B2015-08-15%2B15%253A14%253A37.png" imageanchor="1" style="clear: right; float: right; font-family: Georgia, 'Times New Roman', serif; margin-bottom: 1em; margin-left: 1em; text-align: center;"><img border="0" height="253" src="https://3.bp.blogspot.com/-HkywRLfGAf0/Vc-6kty7AXI/AAAAAAAAUEc/QR4kIwxU4Uo/s400/Screenshot%2Bfrom%2B2015-08-15%2B15%253A14%253A37.png" width="400" /></a><span style="font-family: "georgia" , "times new roman" , serif;">In this case I am using a bounding box, with some padding, generated directly by my point cloud.</span><span style="font-family: "georgia" , "times new roman" , serif;"></span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;">This is what the node graph should look like roughly.</span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;">NOTE: don't forget to enable "Pack Geometry before Copying" in the Copy SOP, under Stamp section (later, when we'll use thousands of points, this option will make a huge difference).</span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: "georgia" , "times new roman" , serif;"><a href="http://2.bp.blogspot.com/-mEBNMAUv9oo/Vc-6lL_GhHI/AAAAAAAAUEY/U8kzFicDRy0/s1600/Screenshot%2Bfrom%2B2015-08-15%2B15%253A16%253A15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="277" src="https://2.bp.blogspot.com/-mEBNMAUv9oo/Vc-6lL_GhHI/AAAAAAAAUEY/U8kzFicDRy0/s320/Screenshot%2Bfrom%2B2015-08-15%2B15%253A16%253A15.png" width="320" /></a></span></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: "georgia" , "times new roman" , serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;">And the viewport (note the empty VDB defined by the bounding box surrounding the poles):</span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: "georgia" , "times new roman" , serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: "georgia" , "times new roman" , serif;"><a href="http://4.bp.blogspot.com/-NIdJi_OxDzM/Vc-6lgdYbDI/AAAAAAAAUEk/-UMCg_xWxOo/s1600/Screenshot%2Bfrom%2B2015-08-15%2B15%253A16%253A29.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="181" src="https://4.bp.blogspot.com/-NIdJi_OxDzM/Vc-6lgdYbDI/AAAAAAAAUEk/-UMCg_xWxOo/s320/Screenshot%2Bfrom%2B2015-08-15%2B15%253A16%253A29.png" width="320" /></a></span></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
There's one more step we should follow before starting to write our Volume Wrangle node. At the moment, we wouldn't be able to visualize the content of the Vector VDB cause of the nature of a vector field. For this purpose we can use the Volume Trail node. This node will sample the vector field in the points specified by the First Input , and draw curves following the vector volume connected in the Second Input.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="http://1.bp.blogspot.com/-yJtIWu4VI4M/Vc_AFaWksAI/AAAAAAAAUE0/35PDIAu7eic/s1600/Screenshot%2Bfrom%2B2015-08-15%2B15%253A38%253A57.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;"><img border="0" height="217" src="https://1.bp.blogspot.com/-yJtIWu4VI4M/Vc_AFaWksAI/AAAAAAAAUE0/35PDIAu7eic/s400/Screenshot%2Bfrom%2B2015-08-15%2B15%253A38%253A57.png" width="400" /></a>I used the previously created bounding box to generate a Fog Volume, and scatter points within it. These will be the sample points used by the Trail SOP.</div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="http://2.bp.blogspot.com/-aCKZ4em3C78/Vc_AhxjeXhI/AAAAAAAAUE8/4TdjQDI7gSI/s1600/Screenshot%2Bfrom%2B2015-08-15%2B15%253A39%253A21.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em; text-align: center;"><img border="0" height="281" src="https://2.bp.blogspot.com/-aCKZ4em3C78/Vc_AhxjeXhI/AAAAAAAAUE8/4TdjQDI7gSI/s400/Screenshot%2Bfrom%2B2015-08-15%2B15%253A39%253A21.png" width="400" /></a>And this is what the graph should look like. Note that the sample points go in the input 1 of the Volume Trail SOP.</div>
<div class="separator" style="clear: both; text-align: justify;">
Now, we can't see any trail cause the vector field is empty. Let's test if it works filling the volume with a constant vector.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-i7-96tQh66w/Vc_Bg59-uoI/AAAAAAAAUFI/2zZUWn7ekUk/s1600/Screenshot%2Bfrom%2B2015-08-15%2B15%253A46%253A56.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="161" src="https://1.bp.blogspot.com/-i7-96tQh66w/Vc_Bg59-uoI/AAAAAAAAUFI/2zZUWn7ekUk/s320/Screenshot%2Bfrom%2B2015-08-15%2B15%253A46%253A56.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Your viewport should now look like this. </div>
<div class="separator" style="clear: both; text-align: justify;">
Horizontal lines, matching the horizontal vector {1.0 , 0.0 , 0.0 }.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-QGecDbXyHRw/Vc_CmveNATI/AAAAAAAAUFU/FoN3N868wcU/s1600/Screenshot%2Bfrom%2B2015-08-15%2B15%253A47%253A08.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="157" src="https://1.bp.blogspot.com/-QGecDbXyHRw/Vc_CmveNATI/AAAAAAAAUFU/FoN3N868wcU/s320/Screenshot%2Bfrom%2B2015-08-15%2B15%253A47%253A08.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Now we have a good environment that will allow us to visualize and debug our code.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Let's now discuss how to calculate the magnetic field resulting by the 2 poles , for each voxel in the VDB grid.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-BM2hoD__DCQ/Vc_Ocm9qe9I/AAAAAAAAUFg/0uQ_lLvrRek/s1600/Screenshot%2Bfrom%2B2015-08-15%2B16%253A42%253A16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="462" src="https://1.bp.blogspot.com/-BM2hoD__DCQ/Vc_Ocm9qe9I/AAAAAAAAUFg/0uQ_lLvrRek/s640/Screenshot%2Bfrom%2B2015-08-15%2B16%253A42%253A16.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Let's calculate the magnetic field generated by the poles p1 and p2 at the position v@P (the dark grey voxel visible in the picture above).</div>
<div class="separator" style="clear: both; text-align: justify;">
That Voxel will feel the negative influence of p1 (meaning , will be repelled from p1) and the positive influence of p2 (meaning, will be attracted towards p2). If we calculate the vector distance between the voxel and each pole position, of course we obtain a vector that is very small when the voxel is close to the point, and very big when it's far. We want the opposite. That's why, in our calculation we use the inverse of the distance from each pole, as a multiplier of the (normalized) distance vector. This way, the closer we are to the pole, the stronger is the attraction or repulsion as you can see from the function plot below.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-_QKMkduio98/Vc_YSHF9RII/AAAAAAAAUFw/ppzfvqthJzU/s1600/Screenshot%2Bfrom%2B2015-08-15%2B17%253A23%253A30.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="243" src="https://2.bp.blogspot.com/-_QKMkduio98/Vc_YSHF9RII/AAAAAAAAUFw/ppzfvqthJzU/s400/Screenshot%2Bfrom%2B2015-08-15%2B17%253A23%253A30.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
d is red (no good, cause it gets bigger and bigger with the distance)</div>
<div class="separator" style="clear: both; text-align: justify;">
1/d is green (good cause it big close to the pole, and smaller and smaller far from the pole).</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
This was the trickiest part. The rest is pretty simple.</div>
<div class="separator" style="clear: both; text-align: justify;">
All we need to do is to store in each voxel the sum of all the normalized distance from each pole position, multiplied by the pole attribute ( to make sure the contributing vector is repelling or attracting depending on the pole ) and multiplied again by the inverse of the distance, as explained before. </div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
The pseudo code is the following:</div>
<div class="separator" style="clear: both; text-align: justify;">
</div>
<ul>
<li>For each Voxel position v@P</li>
<ul>
<li>initialize a vector called <b>VectorField</b> = { 0 , 0 , 0 }</li>
<li>iterate through all the points (pi) in a certain radius from v@P</li>
<ul>
<li>import the pole attribute of the point, <b>pole</b></li>
<li>find the vector <b>d </b>between <b>pi</b> and<b> </b><b>v@P</b></li>
<li>find the normalized<b> (nd) </b>and magnitude<b> (md) </b>of vector<b> d</b></li>
<li>add <b>nd</b> , multiplied by <b>pole</b>, and by the inverse of the distance <b>md </b>to<b> VectorField</b></li>
</ul>
<li>the magnetic field <b>v@magneticfield</b> in the voxel <b>v@P</b> is set to <b>VectorField</b></li>
</ul>
</ul>
<div>
Let's create a Volume Wrangle, and connect the Input 1 to the VDB volume , and the input 2 to the merge node containing the 2 points with the "pole" attribute.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-qeB0ubL4nLA/Vc_dGaRmFQI/AAAAAAAAUGI/N0TA7eIsxbM/s1600/Screenshot%2Bfrom%2B2015-08-15%2B17%253A45%253A01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://4.bp.blogspot.com/-qeB0ubL4nLA/Vc_dGaRmFQI/AAAAAAAAUGI/N0TA7eIsxbM/s320/Screenshot%2Bfrom%2B2015-08-15%2B17%253A45%253A01.png" width="306" /></a></div>
<div>
<br /></div>
<div>
<b><br /></b></div>
<div>
Converting the pseudo code in VEX is probably easier than writing the pseudo code itself.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-vJBzmfNXcEw/Vc_dm9P2XcI/AAAAAAAAUGQ/9vbcOIv7tWU/s1600/Screenshot%2Bfrom%2B2015-08-15%2B17%253A47%253A08.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="316" src="https://3.bp.blogspot.com/-vJBzmfNXcEw/Vc_dm9P2XcI/AAAAAAAAUGQ/9vbcOIv7tWU/s640/Screenshot%2Bfrom%2B2015-08-15%2B17%253A47%253A08.png" width="640" /></a></div>
<div>
<br /></div>
<div>
Now this is what the view port should look like:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-Y258WUqcrrk/Vc_dy99wHxI/AAAAAAAAUGY/Nrr1sAscCA8/s1600/Screenshot%2Bfrom%2B2015-08-15%2B17%253A46%253A55.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="332" src="https://4.bp.blogspot.com/-Y258WUqcrrk/Vc_dy99wHxI/AAAAAAAAUGY/Nrr1sAscCA8/s640/Screenshot%2Bfrom%2B2015-08-15%2B17%253A46%253A55.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Now we can replace the boring 2 poles setup with something more attractive.</div>
<div class="separator" style="clear: both; text-align: justify;">
How about ... simulating the magnetic field on the surface of the sun ? </div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-7v-khzuQxmM/Vc_e0i9Bm-I/AAAAAAAAUGk/lJpetuxPBTk/s1600/sunmagneticfield300.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://2.bp.blogspot.com/-7v-khzuQxmM/Vc_e0i9Bm-I/AAAAAAAAUGk/lJpetuxPBTk/s1600/sunmagneticfield300.jpeg" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
This picture I downloaded from Google Image is a good reference.</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
In order to recreate that look, all we need to do is scatter points on a sphere (about 2k), randomly assign f@pole to -1 or 1 and feed it into the simple setup we just created.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-Rw_qYZaeZEE/Vc_gx7CyoLI/AAAAAAAAUGw/hw4VOJ9UxoE/s1600/Screenshot%2Bfrom%2B2015-08-15%2B17%253A59%253A42.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="428" src="https://3.bp.blogspot.com/-Rw_qYZaeZEE/Vc_gx7CyoLI/AAAAAAAAUGw/hw4VOJ9UxoE/s640/Screenshot%2Bfrom%2B2015-08-15%2B17%253A59%253A42.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
I find this pretty cool ! :)</div>
Ok, I guess that's it.<br />
<div class="separator" style="clear: both; text-align: justify;">
If you like this tutorial, or found a better way to achieve the same result, please don't hesitate to comment.</div>
<div class="separator" style="clear: both; text-align: justify;">
Thank you for reading !</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Since you read the whole article you definitely deserve the hip file.</div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://drive.google.com/file/d/0B_GDFhnt3Ph_SFdxTjdaS25rNlU/view?usp=sharing" target="_blank">HIP FILE (H15)</a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<h4 style="clear: both; text-align: justify;">
<div class="separator" style="clear: both; font-weight: 400;">
It's better to use the inverse of the square of the distance, instead of the inverse of the distance. This will definitely give better results and less interference in the voxels far from poles.</div>
<div class="separator" style="clear: both; font-weight: 400;">
Thanks to Jon Parker for this suggestion.<br /><b>[ EDITED in 2015 ]</b></div>
</h4>
<h4 style="clear: both; text-align: justify;">
<br /></h4>
<h4 style="clear: both; text-align: justify;">
ADVECT PARTICLES ALONG THE CURVES</h4>
<div class="separator" style="clear: both; text-align: justify;">
Many people asked me this question:</div>
<div class="separator" style="clear: both; text-align: justify;">
Great job with the lines, but ... what now ?? For instance, how do I advect particles along those lines ?</div>
<div class="separator" style="clear: both; text-align: justify;">
Ok this is what I would do.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
First off I assume you are creating some kind of Sun Flare FX, so I assume you've got your sun surface from which you're generating your magnetic arcs.</div>
<div class="separator" style="clear: both; text-align: justify;">
I'll write here broad strokes, without really going into details for now and see if that works:</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
</div>
<ol>
<li>find a way to create a direction vector on the lines (one way could be using a PolyFrame SOP). Let's call this v@linedir , and find a way to orient this path so that it goes in the dir you want (like from the positive to the negative or the other way around).</li>
<li>emit particles from the surface of the sun in some POP network</li>
<li>using a POP Wrangle open a point cloud (pcopen / pcfind) on the curves, sample v@linedir attribute (using pcfilter or pcimport in a for loop ...) to return an average direction (lets call it <b>magv</b>) and add it to the particles velocity (<b>v@v += magv</b>).</li>
<li>optionally use the distance from the sun to modulate <b>magv</b> length using a ramp.</li>
</ol>
<div>
</div>
<br />
<h4 style="clear: both; text-align: justify;">
[ EDITED in 2019 ]</h4>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div>
<br /></div>
Unknownnoreply@blogger.com27Los Angeles, CA, USA34.0522342 -118.243684933.2099567 -119.5345784 34.8945117 -116.95279140000001tag:blogger.com,1999:blog-5626905874114803923.post-48123364978617342162015-06-15T22:19:00.001-07:002015-06-15T22:19:50.551-07:00H14 - Point Wrangle vs Creep SOPThe following Vex code used in a Point Wrangle reproduces the behavior of the <a href="http://www.sidefx.com/docs/houdini14.0/nodes/sop/creep" target="_blank">Creep SOP</a>, and it's about 5 times faster.<br />
<br />
I've used it to stick and move points on a NURBs surface.<br />
<br />
Points To Stick --> Point Wrangle Input 1<br />
Nurbs Surface --> Point Wrangle Input 2<br />
<br />
<blockquote class="tr_bq">
<i>vector newP=primuv(@OpInput2, "P", 0, set(v@P.x,v@P.y,0));<br />vector newN=primuv(@OpInput2, "N", 0, set(v@P.x,v@P.y,0));<br />v@P=newP+normalize(newN)*v@P.z; </i></blockquote>
The VEX command <a href="http://www.sidefx.com/docs/houdini14.0/vex/functions/primuv" target="_blank">primuv</a> is super fast.<br />
<br />
This system works of course even with a polygonal object. Just make sure to create UV coords and N (using Facet Sop).<br />
<br />
Wrangle nodes win again !<br />
<br />
<br />Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5626905874114803923.post-78195934923111536812014-07-17T00:36:00.000-07:002014-07-22T10:33:56.473-07:00Ink in Water<div style="text-align: justify;">
One way to achieve this effect is using a looot of particles advected by a velocity field generated by some gas simulation. Why not use the gas sim itself ? Cause in order to achieve the same detail offered by particles, the resolution of the gas grid should be so high that even Hal 9000 wouldn't be able to handle it or Sky-net would suddenly become self-unaware.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
So I generated a simple smoke sim using H13 Smoke Solver. I didn't use Pyro cause it was probably overkill for such a simple sim. Then I wedged and cached to disk 10 versions of the smoke sim varying parameters like the turbulence amplitude and twirl radius (finding out what parameters to wedge is an art on its own).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Once I had 10 different smoke sims, I had 10 different velocity field sequences that I can use to advect my points.</div>
<div style="text-align: justify;">
Now, two ways to advect points in Houdini are:</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<ul>
<li>create a POP system in a DOP Network and use the POP Advect by Volumes DOP node making sure to point it to the velocity fields cached on disk and re-imported somewhere else at the SOP level.<br />PROs: POP land provides a large range of nodes to control particle motion.<br />CONs: Slow</li>
<li>convert the velocity to VDB, merge the fields into one single VDB vector field (with VDB Vector Merge - don't worry if Houdini complains cause the grid components are different), then use VDB Advect Points in a SOP Solver to advect your points.<br />PROs: Very fast.<br />CONs: you've to do everything yourself.</li>
<ul>
<li>PRO of this CONS: you CAN do whatever you want :) !</li>
</ul>
</ul>
<div>
EXAMPLE HIP FILE:<br />
<a href="https://drive.google.com/file/d/0B_GDFhnt3Ph_UzhScVBLQk1NWDg/edit?usp=sharing">VDB_advect_points_example.hipnc</a></div>
<div>
<br /></div>
<div>
When I mentioned thet the POP is slow, I still mean that it can process several hundreds of thousands of particles per second. The VDB solution can handle millions of points per second. Because of this, you can avoid rendering different versions of the particle sim and compositing them in order to smooth out the "particular" look (which is the main challenge of ink in the water effect).</div>
<div>
<br /></div>
<div>
So I decided to adopt the VDB solution.</div>
<div>
<br /></div>
<div>
In order to add extra points in the areas where the particles were more sparse, I added an Attribute Wrangle node in the end of the chain and I called it Fill Gaps. Since Attribute Wrangle works in the CVEX context, I am given the opportunity to add (or destroy) geometry.</div>
<div>
The purpose of the script is basically adding points where there aren't.</div>
<div>
<br /></div>
<div>
This is the content of the script (make sure to create the proper UI parameters before sourcing the script) and feed the same point geometry both in input 1 and 2 of the Attrib Wrangle node.</div>
<div>
<br /></div>
<div>
Note: this fill gap algorithm is FAR from being perfect. Plus it just really fills the spaces between points larger than "mindist" within "seachrad". So the results might not be as expected. The good thing is that the added points remain surprisingly consistent during the animation, so , at least, you don't see crazy artifacts or points popping in suddenly, in the render.</div>
<div>
<br /></div>
<div>
<div>
<i>float searchrad=ch("searchrad");</i></div>
<div>
<i>float mindist=ch("mindist");</i></div>
<div>
<i>int maxpoints=ch("maxpoints");</i></div>
<div>
<i>int fillpoints=ch("fillpts");</i></div>
<div>
<i><br /></i></div>
<div>
<i>vector clpos;</i></div>
<div>
<i>int handle=pcopen(@OpInput2,"P",@P,searchrad,maxpoints+1);</i></div>
<div>
<i>int i=0;</i></div>
<div>
<i>while(pciterate(handle))</i></div>
<div>
<i>{</i></div>
<div>
<i> if (i==0) // the first point found should be the closest, in this case, itself. We want to skip it.</i></div>
<div>
<i> {</i></div>
<div>
<i> i++;</i></div>
<div>
<i> continue;</i></div>
<div>
<i> }</i></div>
<div>
<i> pcimport(handle,"P",clpos);</i></div>
<div>
<i> if (length(@P-clpos)>mindist)</i></div>
<div>
<i> {</i></div>
<div>
<i> vector pointstep=(clpos-@P)/(fillpoints*2+1); // this ensures there are no duplicate point</i></div>
<div>
<i> // at the cost of doubling the fill points number</i></div>
<div>
<i> for (int t=0;t<fillpoints;t++)</i></div>
<div>
<i> addpoint(geoself(),@P+(pointstep*float(t+1)));</i></div>
<div>
<i> }</i></div>
<div>
<i>}</i></div>
</div>
<div>
<i><br /></i></div>
<div>
After rendering 10 exr sequences, I imported them in Nuke and used them as sprites on a particle distribution scattered over a large plane, and rendered with DOF. Kinda slow actually, and the DOF in comp is always a PITA (has this improved in the last 15 years ? nope!).</div>
<div>
<br /></div>
<div>
This is the result.</div>
<div>
Oh, there is a card disappearing in the last 10 frames, probably one of the Nuke particles ran out of fuel.</div>
<div>
<br /></div>
<div>
<br /></div>
</div>
<iframe allowfullscreen="" frameborder="0" height="281" mozallowfullscreen="" src="//player.vimeo.com/video/100877206" webkitallowfullscreen="" width="500"></iframe> <br />
<a href="http://vimeo.com/100877206">Ink in Water - test</a> from <a href="http://vimeo.com/user2891147">Alessandro Pepe</a> on <a href="https://vimeo.com/">Vimeo</a>.<br />
<br />
Thank you for reading !Unknownnoreply@blogger.com5tag:blogger.com,1999:blog-5626905874114803923.post-13250437639010205392014-05-29T14:52:00.001-07:002014-05-29T15:04:01.396-07:00Houdini - Explicit CacheClassical Scenario:<br />
<div>
You deadline is today at 7pm. Which means your deadline really is tomorrow around 9pm ! You've time to recalculate your huge flip sim that takes about 13 hours to complete (note, not 12 hours, not 14...13 the evil number). Each bgeo file is about 666mb. You hope you'll have enough HD space, but hey ... HD space is never enough ! Even Confucius knew this.</div>
<div>
So you cross your fingers, and hit "Render" on your rop_geometry network to start your 999 frames FLIP sim.</div>
<div>
<br /></div>
<div>
Now usually, when you've only one chance to do it right and if you miss it you'll be fired, usually this is what happens in order:</div>
<div>
1 - you run out of HD space on the frame 998.</div>
<div>
2 - Linux crashes for the first time in 12 years</div>
<div>
3 - Houdini crashes for the 13th time in 2 hours</div>
<div>
4 - power outage in the whole <city where you are in that moment></div>
<div>
<br /></div>
<div>
You better remember that one of these 4 things (if not all of them) will happen. It's important to be positive.</div>
<div>
<br /></div>
<div>
How will our fellow FX Artist save his job, and manage to pay the rent of his mansion with pool in the Hollywood Hills ?</div>
<div>
<br /></div>
<div>
The answer is this gorgeous little gem of pure love called "explicit cache" and his little friend "explicit frames to cache" on the DOP Network node.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-3j5t2All1so/U4eni6dT85I/AAAAAAAAJUs/j25nhtHZmZU/s1600/dopnetwork_explicit_cache.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-3j5t2All1so/U4eni6dT85I/AAAAAAAAJUs/j25nhtHZmZU/s1600/dopnetwork_explicit_cache.png" height="367" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
This option is off by default, cause the files generated can fill your 12Tb HD very quickly.</div>
<div class="separator" style="clear: both; text-align: left;">
But this is where the second option comes really in handy !</div>
<div class="separator" style="clear: both; text-align: left;">
You don't have to save ALL the .sim files for each one of your 999 frames of simulation.</div>
<div class="separator" style="clear: both; text-align: left;">
You can save the last, say, 5 (or less maybe) and restart the sim from the smallest frame number in your cache ! This way you'll not clog your HD with unnecessary GIGANTIC .sim files, and you'll still be able to resume your sim.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Example:</div>
<div class="separator" style="clear: both; text-align: left;">
n.4 happened. And I managed to simulate only 362 frames of my 999 frames sim. But I was wise enough to enable the explicit cache option and specify a frame history of 5.</div>
<div class="separator" style="clear: both; text-align: left;">
So, if I go in my ...../simcache directory I'll see these 5 files:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-JkyhWNCWU54/U4eo5CSrH6I/AAAAAAAAJU4/LoI7bOqB8jo/s1600/dopnetwork_sim_after_crash.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-JkyhWNCWU54/U4eo5CSrH6I/AAAAAAAAJU4/LoI7bOqB8jo/s1600/dopnetwork_sim_after_crash.png" height="98" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div>
What I usually do is delete the last one. Why ? Well ... I love Houdini, but I will never trust that he managed to write out the last sim file while the power of my computer was off. And you've a pretty good clue of this checking the file size of the files. They are all ~153 Mb, apart from the last one ! MM ... suspicious. Delete !</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-2LBnrzrxPxE/U4epWRZ_H5I/AAAAAAAAJVA/_NTXL2MKH7I/s1600/dopnetwork_sim_after_crash2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-2LBnrzrxPxE/U4epWRZ_H5I/AAAAAAAAJVA/_NTXL2MKH7I/s1600/dopnetwork_sim_after_crash2.png" height="93" width="320" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Perfect. Now you have 4 cache files and you're sure they are fully functional.</div>
<div>
<br /></div>
<div>
Now you can restart your sim starting from the frame 358 and Houdini will seamlessly continue simming like if you started from frame 1.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-eriJf4adhd0/U4ep1sUlGxI/AAAAAAAAJVI/VIQlMSgQsSo/s1600/dopnetwork_rop_network.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-eriJf4adhd0/U4ep1sUlGxI/AAAAAAAAJVI/VIQlMSgQsSo/s1600/dopnetwork_rop_network.png" height="174" width="400" /></a></div>
<div>
<br /></div>
<div>
This saved my <censored> several times already.</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
Unknownnoreply@blogger.com2Los Angeles, CA, USA34.0522342 -118.243684933.2099567 -119.5345784 34.8945117 -116.95279140000001tag:blogger.com,1999:blog-5626905874114803923.post-26824578520091889112013-07-19T00:58:00.000-07:002013-07-19T01:12:24.108-07:00(just another) Houdini FLIP water sim<br />
<br />
<br />
<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="281" mozallowfullscreen="" src="http://player.vimeo.com/video/70611624" webkitallowfullscreen="" width="500"></iframe> <br />
<a href="http://vimeo.com/70611624">Houdini fountain water sim - FLIP solver test</a> from <a href="http://vimeo.com/user2891147">Alessandro Pepe</a> on <a href="https://vimeo.com/">Vimeo</a>.<br />
<br />
<div style="text-align: justify;">
A few days ago I realized the world couldn't survive without my contribution to the countless water simulation tests out there. Honestly this is not better or worse than many others but I had so much fun working on it.<br />
I spent about 5 days overall between setup and render.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The workflow is pretty much the same explained in the SideFX Waterfall tutorials, with some minor changes in the water shader, and the creation of a wet map. Furthermore I paid extra attention to the bubbles underwater.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
For the wet map I used Attribute Transfer SOP to transfer the "wetness" attribute from the particles generated by the Flip Simulation (after caching only the ones close to the fountain walls) to a dense particle object scattered on the fountain. The attribute transferring was performed in a DOPs Network (via SOP Solver), in order to preserve the previous state.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Initially I created 2 different wet maps:</div>
<ul>
<li>WETNESS - This is a wet map that dries very quickly and reveals only the water spec on the fountain shader.</li>
<li>DARKNESS - This wet map is identical to the previous one for what concerns the shape, but dries way way slower and is in charge of keeping the fountain shader just darker. </li>
</ul>
<div style="text-align: justify;">
Eventually I ended up using only the "DARKNESS" wetmap cause the light position didn't show any wet part of the fountain revealed by the water, (unfortunately) so ... useless ! But I thought it was a good idea to illustrate both wetness (in cyan) and darkness (in blue), in the pass below. </div>
<br />
<iframe allowfullscreen="" frameborder="0" height="281" mozallowfullscreen="" src="http://player.vimeo.com/video/70612912" webkitallowfullscreen="" width="500"></iframe> <br />
Houdini Wet map from <a href="http://vimeo.com/user2891147">Alessandro Pepe</a> on <a href="https://vimeo.com/">Vimeo</a>.Unknownnoreply@blogger.com1Los Angeles, CA, USA34.0522342 -118.243684933.2099567 -119.5345784 34.8945117 -116.95279140000001tag:blogger.com,1999:blog-5626905874114803923.post-42660505064684796362013-06-24T01:40:00.000-07:002013-06-24T01:40:08.185-07:00stuff growing and crawling - Venations - HDK version<div class="first">
</div>
<div style="text-align: justify;">
I know, it's time to move on, enough with this growing and crawling stuff, but ... I just can't ! It's too much fun to see those tentacles growing and reaching out drawing crazy shapes in the virtual space. The problem with the previous version was that ... the algorithm is quite heavy because it relies upon two nested loops (for each seed, search for the closest root) : it was too slow.<br />
So I decided it was about time to learn a bit of HDK and implement a prototype in C++.<br />
This version is about 30 times faster and allows a real time feedback for way more complex structures than the OTL version.<br />
In this video I am illustrating how to use this SOP node to create a simple tree and in the second part of the video , how to paint venations on a polygonal head.</div>
<br />
<iframe allowfullscreen="" frameborder="0" height="281" mozallowfullscreen="" src="http://player.vimeo.com/video/68987323" webkitallowfullscreen="" width="500"></iframe> <br />
<a href="http://vimeo.com/68987323">Venation System - HDK version - demo</a> from <a href="http://vimeo.com/user2891147">Alessandro Pepe</a> on <a href="http://vimeo.com/">Vimeo</a>.<br />
<div style="text-align: justify;">
<br />
<span style="font-size: x-small;"><i>Reference:</i></span><br />
<span style="font-size: x-small;"><i>The venation algorithm is based on <a href="http://algorithmicbotany.org/papers/venation.sig2005.pdf" target="_blank">this</a> paper by Adam Runions.</i></span></div>
<span style="font-size: x-small;"><b><br /></b></span>
<br />Unknownnoreply@blogger.com4Los Angeles, CA, USA34.0522342 -118.243684933.2099567 -119.5345784 34.8945117 -116.95279140000001tag:blogger.com,1999:blog-5626905874114803923.post-6155532950151067632013-06-12T03:37:00.000-07:002013-07-03T19:16:52.485-07:00HDK Learning Notes - quick start on Linux(tested on Linux - CentOS 6.4 and Ubuntu 13.04)<br />
<br />
<span style="font-size: large;">Install compiler and libraries</span> (as mentioned <a href="http://www.sidefx.com/docs/hdk12.5/hdk_intro_gettingstarted.html" target="_blank">here</a>)<br />
<br />
<span style="color: lime;"><span style="background-color: black;"><span style="font-size: x-small;"><i><span style="background-color: white;"><span style="color: black;">(<span style="font-size: x-small;">CENTOS)</span></span></span> </i></span></span></span><br />
<span style="color: lime;"><span style="background-color: black;"><span style="font-size: x-small;"><i>$ sudo yum install tcsh gcc-c++ mesa-libGL-devel mesa-libGLU-devel Xi-devel</i></span></span></span><br />
<span style="background-color: white;"><span style="color: black;"><span style="font-size: x-small;"><i><span style="font-size: x-small;">(if yum cannot find Xi-devel, try <i>libXi-devel)</i></span></i></span></span></span><br />
<span style="color: lime;"><span style="background-color: black;"><span style="font-size: x-small;"><i><span style="color: black;"><span style="background-color: white;"><span style="font-size: x-small;"><i> </i></span></span></span> </i></span></span></span><br />
<span style="background-color: white;"><span style="color: black;"><span style="font-size: x-small;"><i>(UBUNTU)</i></span></span></span><br />
<span style="color: lime;"><span style="background-color: black;"><span style="font-size: x-small;"><i>$<span style="font-size: x-small;">sud<span style="font-size: x-small;">o a<span style="font-size: x-small;">pt-g<span style="font-size: x-small;">et libgl-dev libglu-dev libxi-dev</span></span></span></span></i></span></span><i> </i></span><br />
<br />
To initialize houdini environment variables and commands:<br />
<br />
<br />
<span style="color: lime;"><span style="background-color: black;"><span style="font-size: x-small;"><i>$ cd /opt/hfs12.5.371/</i></span></span></span><br />
<span style="color: lime;"><span style="background-color: black;"><span style="font-size: x-small;"><i>$ source houdini_setup</i></span></span></span><br />
<span style="color: lime;"><span style="background-color: black;"><span style="font-size: x-small;"><i>The Houdini 12.5.371 environment has been initialized.</i></span></span></span><br />
<br />
<div style="text-align: justify;">
Now you'll be able to use the command <span style="font-size: x-small;"><i>houdini, houdinifx, hcustom,<span style="font-size: x-small;"> etc.</span></i></span> </div>
<div style="text-align: justify;">
The command hcustom is a little wrapper to g++ that will take care of searching for houdini sdk headers and linking with the correct libraries:</div>
<span style="color: lime;"><span style="background-color: black;"><br /></span></span>
<span style="color: lime;"><span style="background-color: black;"><span style="font-size: x-small;"><i>$ hcustom SOP_mynode.C</i></span></span></span><br />
<br />
... if we need OpenVDB (see below for installing OpenVDB headers)<br />
<br />
<span style="color: lime;"><span style="background-color: black;"><span style="font-size: x-small;"><i>$ hcustom -I /tmp/OpenVDB/include/</i></span></span></span><br />
<br />
When you try to compile the first time you might get this error:<br />
<br />
<span style="color: lime;"><span style="background-color: black;"><span style="font-size: x-small;"><i>... </i></span></span></span><br />
<span style="color: lime;"><span style="background-color: black;"><span style="font-size: x-small;"><i>... </i></span></span></span><br />
<span style="color: lime;"><span style="background-color: black;"><span style="font-size: x-small;"><i>/usr/bin/ld: cannot find -lXi</i></span></span></span><br />
<br />
<div style="text-align: justify;">
which might be related to the fact yum wasn't able to install the library <i>Xi-devel</i> in the first step. In that happens, try this:</div>
<br />
<span style="color: lime;"><span style="background-color: black;"><span style="font-size: x-small;"><i>sudo yum install libXi-devel </i></span></span></span><br />
<br />
<br />
<br />
<span style="font-size: large;">OpenVDB</span><br />
<br />
<div style="text-align: justify;">
If you plan to use OpenVDB you've to install the OpenVDB headers , cause they are not shipped with Houdini (12.5.xxx) so far. </div>
<div style="text-align: justify;">
<a href="http://www.openvdb.org/download/openvdb_1_1_1_library.zip" target="_blank">Download the library</a> and unzip it somewhere (for instance in /home/$USER/Download).</div>
<span style="color: lime;"><span style="background-color: black;"><span style="font-size: x-small;"><br /></span></span></span>
<i><span style="color: lime;"><span style="background-color: black;"><span style="font-size: x-small;">$ cd /home/alex/Download/openvdb</span></span></span></i><br />
<i><span style="color: lime;"><span style="background-color: black;"><span style="font-size: x-small;">$ make clean</span></span></span></i><br />
<i><span style="color: lime;"><span style="background-color: black;"><span style="font-size: x-small;">$ make install</span></span></span></i><br />
<br />
... this will install the libraries into /tmp/OpenVDB<br />
<br />
You might get the following errors:<br />
<br />
<u>error:</u><br />
<br />
<i>io/Compression.cc:35:18: error: zlib.h: No such file or directory</i><br />
<u>fix</u>:<br />
<ul>
<li>edit the file /openvdb/io/Compression.cc</li>
<li> change the line<br />from <span style="font-size: x-small;"><i><br />#include <zlib.h></i></span>to<span style="font-size: x-small;"><i><br />#include </opt/hfs12.5.371/toolkit/include/zlib/zlib.h></i></span></li>
</ul>
<u><br /></u>
<u>error</u>:<br />
<i>cmd/openvdb_view/Viewer.h:50:21: error: GL/glfw.h: No such file or director</i>y<br />
<u>fix:</u><br />
<ul>
<li>edit the file /openvdb/Makefile</li>
<li> search for line starting with<i><br />install:</i><br />and remove <i>vdb_view</i> (it's on the same line of "install :")</li>
</ul>
<br />
<u>error:</u><br />
/bin/bash: doxygen: command not found<br />
<u>fix: </u><br />
<ul>
<li><span style="color: lime;"><span style="background-color: black;"><span style="font-size: x-small;"><i>$ sudo yum install doxygen</i></span></span></span></li>
</ul>
<br />
<br />Unknownnoreply@blogger.com0Los Angeles, CA, USA34.0522342 -118.243684933.2099567 -119.5345784 34.8945117 -116.95279140000001tag:blogger.com,1999:blog-5626905874114803923.post-42962025145393680932013-05-20T01:20:00.002-07:002013-06-24T15:53:39.697-07:00stuff growing and crawling - Hyphae - OTL version<div style="text-align: justify;">
<div style="text-align: justify;">
I've been playing with path finding algorithms recently and I came up with an OTL that creates a venation structure given root points, seed points and an optional collision surface.</div>
<div style="text-align: justify;">
This can be used to create venations on, inside surfaces, and I guess some more cool stuff that at the moment I cannot even think of !</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
This is a quick demonstration video of the otl (I speeded it up 2x so you don't fall asleep while you watch)</div>
<br />
<iframe allowfullscreen="" frameborder="0" height="500" mozallowfullscreen="" src="http://player.vimeo.com/video/66939336?portrait=0" webkitallowfullscreen="" width="500"></iframe> </div>
<br />
<br />
A few days ago I was reading <a href="http://www.fxguide.com/featured/rise-of-the-effects/" target="_blank">this article</a> cause I was interested in reproducing Jack Frost effect from Rise of the Guardians.<br />
This line of the article really caught my attention:
<br />
<blockquote class="tr_bq">
<div style="text-align: justify;">
<span style="font-family: 'times new roman', times;"><i>Our developer on that would take a model, whatever the frost needed to grow on, and he would run a cellular automata simulation across it</i></span></div>
</blockquote>
<div style="text-align: justify;">
Cellular automata ? what the hell is that ? So I started researching a bit and as it always happens...the by-products of the research are usually even more interesting than the destination. <br />
<br />
So I came across this video:</div>
<div style="text-align: justify;">
<iframe allowfullscreen="" frameborder="0" height="500" mozallowfullscreen="" src="http://player.vimeo.com/video/25604611?portrait=0" webkitallowfullscreen="" width="500"></iframe> </div>
<div style="text-align: justify;">
<a href="http://vimeo.com/25604611">Hyphae - growth process diagram in 2D</a> from <a href="http://vimeo.com/nervoussystem">Nervous System</a> on <a href="http://vimeo.com/">Vimeo</a>.</div>
<div style="text-align: justify;">
<br /></div>
<br />
<div style="text-align: justify;">
Check out <a href="http://n-e-r-v-o-u-s.com/blog/">this blog</a> out when you have time, there is a lot of really interesting stuff, presented in a really nice way. </div>
<div style="text-align: justify;">
Researching a bit more I found the details of the algorithm in this amazing paper:</div>
<br />
<div dir="ltr">
<div style="text-align: justify;">
<a href="http://algorithmicbotany.org/papers/venation.sig2005.pdf">http://algorithmicbotany.org/papers/venation.sig2005.pdf</a><br />
<br />
(thank you Adam Runions, Martin Fuhrer, Brendan Lane, Pavol Federl, Anne−Gaëlle Rolland−Lagan, and Przemyslaw Prusinkiewicz for this amazing paper !!!)</div>
<div style="text-align: justify;">
<br /></div>
</div>
<div style="text-align: justify;">
This paper blew my mind. Bye Jack Frost (for now), welcome Hyphae ! </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
So I quickly implemented a prototype in Houdini.</div>
<div style="text-align: justify;">
<br /></div>
<h3>
The Algorithm</h3>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Given this initial data:</div>
<ul>
<li style="text-align: justify;"><span style="line-height: 15px;">hormone points (H)</span></li>
<li style="text-align: justify;">roots points (R)</li>
</ul>
<div style="text-align: justify;">
The target of the algorithm is to add points to R till the branches reach all the points in H. When one of the R branches reaches one of the H points, that H point will be removed by the group H and not used anymore for the next iteration.</div>
<div style="text-align: justify;">
So the way I implemented it in Houdini is the following:</div>
<ol>
<li><div style="text-align: justify;">
<i>for each point h in H</i></div>
<ol>
<li style="text-align: justify;"><i>find the closest point cp in R and store (on h) its index (integer CLIDX) , location (vector CLPOS) and normalized vector pointing to it from h (vector CLDIR)</i></li>
<li style="text-align: justify;"><i>if the cp is too close to h flag it for deletion (int IGNORE=1)</i></li>
</ol>
</li>
<li style="text-align: justify;"><i>delete all points in H where IGNORE==1</i></li>
<li style="text-align: justify;"><i>in H create groups GR_* based on CLIDX (using partition sop)</i></li>
<li><div style="text-align: justify;">
<i>for each group gr in GR_*</i></div>
<ol>
<li style="text-align: justify;"><i>find the average CLDIR and normalize it --> AVGCLDIR</i></li>
<li style="text-align: justify;"><i>create a new point r=CLPOS-AVGCLDIR</i></li>
<li style="text-align: justify;"><i>merge the new r point into R</i></li>
</ol>
</li>
<li style="text-align: justify;"><i>goto 1</i></li>
</ol>
<span style="text-align: justify;"><b>Test 1</b></span><br />
<div style="text-align: justify;">
"let's see if it works" </div>
<div style="text-align: justify;">
H (hormones) = 100 points scattered by a sphere volume centered at the origin </div>
<div style="text-align: justify;">
R (roots) = 1 point at the origin<br />
<br /></div>
<div style="text-align: justify;">
<iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/PnIfVC1tBsk" width="400"></iframe>
</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<b> Test 2 </b></div>
<div style="text-align: justify;">
"hell yeah it does ! ...how about more roots ??" </div>
<div style="text-align: justify;">
H = same as before </div>
<div style="text-align: justify;">
R = 3 points (manually placed randomly in the space not too far from the sphere I used to generate H)</div>
<div style="text-align: justify;">
<br /></div>
<iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/ysIpBbqCx9s" width="400"></iframe>
<br />
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<b>Test 3 </b></div>
<div style="text-align: justify;">
"this is amazing. How about a lot more roots ?"</div>
<div style="text-align: justify;">
H = same as before </div>
<div style="text-align: justify;">
R = points scattered from the surface of a sphere larger than the one I used to generate H</div>
<div style="text-align: justify;">
<br /></div>
<iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/A8hVtduaNdA" width="400"></iframe><br />
<span style="text-align: justify;"><br /></span>
<span style="text-align: justify;">Uhmm...that is not so cool. Why ?</span><br />
<span style="text-align: justify;"><br /></span>
<span style="text-align: justify;"><u>Problem 1:</u> </span><br />
<span style="text-align: justify;">Well, to start we notice that not all of the points R actually grow. This is because of the step 1.1 in the algorithm ! Only one point between all R is chosen to be the closest one to a specific point in H. So some point of R might not be the closest to ANY H since the beginning.</span><br />
<span style="text-align: justify;"><br /></span>
<br />
<div style="text-align: justify;">
Solution: </div>
<div style="text-align: justify;">
1. we could decrease the number of R but that's cheating , isn't it.</div>
<div style="text-align: justify;">
2. we can increase the radius and number of the H distribution so that the outer H points are closer to the roots.
</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<u>Problem 2: </u><br />
The veins grow radially towards the center, but as soon as they find a hormone, they stop without splitting any new branch. So we don't get that awesome intricate complexity we like so much and that doesn't let us sleep on night. The reason for this is point 2 in the algorithm. What happens is that we have so many veins now, that they take care of 'killing' the few H very quickly (and more or less at the same time, cause the distribution of the chasers (R) is similar to the distribution of the chased (H), since both are spherical distributions).<br />
<br /></div>
<div style="text-align: justify;">
Solution: </div>
<div style="text-align: justify;">
We need to add more H. The best option is to add more density the more we move towards the center of the spherical distribution of H. A quick way to do that is use Poly to VDB sop, check "fill interior" and scatter from this.
</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<u>Problem 3: </u><br />
Lines are too straight ! We like chaos, variation !</div>
<div style="text-align: justify;">
<br />
Solution:<br />
How about adding in the algorithm some noise ?<br />
Let's modify the step 4.B in the algorithm adding some noise fuction of the position.<br />
Something like this:<br />
4.2 --> r=CLPOS-AVGCLDIR+noise(CLPOS)<br />
<br />
<b>Test 4</b><br />
<br />
H = 5000 points scattered from VDB volume with interior filled.<br />
R = same as test 3<br />
I've added a noise function as described in the solution of problem 3 above.<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/ReTtJmnLKfc" width="400"></iframe><br />
<br />
<h3>
Collision Objects</h3>
<div>
This algorithm allows creating any sort of volumetric venation , which is really cool.</div>
<div>
So I wanted to test it on a real complex model, and see if I could create the effect of venations growing and crawling over the surface of an object.</div>
<div>
<br /></div>
<div>
So I took the model of an arm from internet , I scattered points over the surface of the arm (H points), and I placed one single root point on the tip of the index finger. When I ran the simulation I noticed that, even if the result was mind blowing, there was a big problem : the venation was growing even "inside" the arm not only on the surface. I had to expect this of course, since the each loop iteration of the algorithm is performed on ALL the H points. So it doesn't really matter if the root is initially on the index finger tip : it will be affected even from the H points on the fore arm. So the venation would grow regardless.</div>
<div>
You can see it even from another point of view : the algorithm is not aware about the surface of any model ! All we feed into him are point clouds.</div>
<div>
<br /></div>
<div>
So I decided to include in the algorithm the concept of Collision Objects: when a root point enters in a collition object (SDF sample value < 0) then it will be pushed out of the collision object along the normal of the collision object in that point (SDF gradient value).</div>
<div>
<br /></div>
<div>
So all we need is modify once more the step 4.2 of the algorithm:</div>
<div>
<br /></div>
<div>
4.2 --> r=CLPOS - AVGCLDIR + noise(CLPOS) + CVEC</div>
<div>
<br /></div>
<div>
where</div>
<div>
CVEC = if ( sample (CLPOS,cobj) < 0 , -sample(CLPOS, cobj)*gradient(CLPOS,cobj) , 0 )</div>
<div>
cobj is the collision object</div>
<div>
<br /></div>
<div>
Now CVEC component will make sure that the roots will not grow into the collision object.<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/vg4IU4r2lFI" width="420"></iframe>
</div>
<div>
<br /></div>
<div>
What if we want to make sure the roots never leave the surface ?</div>
<div>
<br /></div>
<div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-mbifPN_kJ84/UZnKswo_QnI/AAAAAAAAG78/iydOryHiUuo/s1600/inner_arm_issue.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="195" src="http://1.bp.blogspot.com/-mbifPN_kJ84/UZnKswo_QnI/AAAAAAAAG78/iydOryHiUuo/s320/inner_arm_issue.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><span style="font-size: small; text-align: justify;">For instance in this case, we don't want to see roots growing in the air. We want them to stick on the surface.</span></td></tr>
</tbody></table>
That's easy to fix : we just have to remove the condition to the CVEC component.<br />
In other words, now the roots will be pushed outside of the collision object if they're inside, and towards the collision object if they're outside.<br />
<br />
CVEC = -sample(CLPOS, cobj)*gradient(CLPOS,cobj)<br />
<br />
This will make sure that the roots stick to the collision object.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-JQcKpDPYjFk/UZncScVfTvI/AAAAAAAAG8c/u3jw73_Wfxo/s1600/inner_arm_issue_fixed.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="188" src="http://4.bp.blogspot.com/-JQcKpDPYjFk/UZncScVfTvI/AAAAAAAAG8c/u3jw73_Wfxo/s320/inner_arm_issue_fixed.jpg" width="320" /></a></div>
<br />
In the following example, I duplicated the arm , poly-extruded it out so that the copy is slightly bigger than the arm. Then I selected the hand and a few more polygons on the arm and poly-extruded it again so that the selection is inside the original geometry. Then used it (the copy) as a collision object.<br />
This way I created very quickly a small path on the arm to allow the root to reach the hand which is free to be filled with veins.<br />
This is the collision object (in red) and the arm (in yellow):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-mWUb_dszFf8/UZnWt_6JpwI/AAAAAAAAG8M/6hwOZkQJjdg/s1600/collision.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="http://3.bp.blogspot.com/-mWUb_dszFf8/UZnWt_6JpwI/AAAAAAAAG8M/6hwOZkQJjdg/s320/collision.jpg" width="250" /></a></div>
<br />
This is a quick test<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/SwMuOTE2Gg0" width="560"></iframe>
<br />
IMPORTANT NOTE:<br />
The Hyphae algorithm is great to create vines / venations structures using STATIC point clouds. What I mean is that, moving one single H or R point in the whole system can radically change the outcoming shape.<br />
<br />
So, if you plan to have H, or R or both, animated and expect to have a smooth animation of the resulting venation system, this is not going to happen. The vines will be radically different between two adjacent frames. (this is great if you have to create some electricity effect, or lightnings, or voltaic arcs a la Frankenstein etc..).<br />
<br />
The workaround to smoothly animate the resulting venation is to generate it on static point clouds, then deform it using some other deformer (Lattice for instance).<br />
<br />
<br />
<br /></div>
</div>
Unknownnoreply@blogger.com7Los Angeles, CA, USA34.0522342 -118.243684933.2099567 -119.5345784 34.8945117 -116.95279140000001tag:blogger.com,1999:blog-5626905874114803923.post-50811104790958812292013-05-19T17:21:00.003-07:002013-06-24T01:46:51.187-07:00stuff growing and crawling - L-Systems<div style="text-align: justify;">
In this nth experiment I wanted to create a way to wrap a growing geometry on the surface of another geometry without relying upon UVs on the target geometry.</div>
<div style="text-align: justify;">
The OTL I created takes 3 inputs:</div>
<ol style="text-align: justify;">
<li>lsystem (this will be wrapped on top of 2)</li>
<li>poly geometry</li>
<li>one point</li>
</ol>
<div style="text-align: center;">
<div style="text-align: center;">
<a href="http://www.alessandropepe.com/wp-content/uploads/2013/05/otl_screengrab.jpg"><img alt="otl_screengrab" class="alignnone size-medium wp-image-255" height="237" src="http://www.alessandropepe.com/wp-content/uploads/2013/05/otl_screengrab-300x237.jpg" title="" width="300" /></a></div>
</div>
<h1 style="text-align: justify;">
<span style="font-size: small;">
L-System (points and edges)</span></h1>
<div style="text-align: justify;">
The L-System provided as first input can be any L-System where the root point of a new branch overlaps the point it generates from. In other words if you 'window' select the first point of a branch in your L-System you should end up selecting 2 points.
Inside the OTL the L-System is pre-processed adding point attributes (like an attr that specifies if a certain point is a branch root, or the direction of each segment) that will make traversing it's hierarchy later on way faster.
Something like this:</div>
<div style="text-align: justify;">
<br />
<iframe allowfullscreen="" frameborder="0" height="476" mozallowfullscreen="" src="http://player.vimeo.com/video/66213213?loop=1" webkitallowfullscreen="" width="500"></iframe>
</div>
<h1 style="text-align: justify;">
<span style="font-size: small;">
Wrapped Geometry (any geometry)</span></h1>
<div style="text-align: justify;">
The OTL wraps the original L-System around this geometry. It can be any geometry. This geometry will be converted into a VDB SDF in the OTL, sample and gradient volume vops are used to quickly find the closest point on this surface. Because of this approach, the OTL doesn't require any UV coordinate to locate positions on this geometry and it's super fast since the SDF is calculate only once at the beginning of the whole simulation and contains already all the info to calculate the closest point on the surface with just a couple of multiplications and sums.</div>
<h1 style="text-align: justify;">
<span style="font-size: small;">
Start Point (one point)</span></h1>
<div style="text-align: justify;">
The closest point of the Start Point on the Wrapped Geometry is where the root of the 'wrapped' L-System will sit. This point must be as close as possible to the the Wrapped Geometry surface (in order to have it within the Wrapped Geometry SDF VDB voxels).</div>
<h1 style="text-align: justify;">
<span style="font-size: small;">
How it works</span></h1>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.alessandropepe.com/wp-content/uploads/2013/05/otl_parameters.jpg" style="margin-left: auto; margin-right: auto; text-align: center;"><img alt="otl_parameters" class="alignnone size-medium wp-image-267" height="73" src="http://www.alessandropepe.com/wp-content/uploads/2013/05/otl_parameters-300x73.jpg" width="300" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><span style="font-size: small;">simple otl interface</span></td></tr>
</tbody></table>
<span style="font-size: small; font-weight: normal;">The original L-System is flattened on the XY plane before being processed. The root (the first point calculated) of the wrapped L-System (WL) is the closest point of the Start Point (SP) on the Wrapped Geometry (WG).</span><span style="font-size: small; font-weight: normal;">The initial direction is given by the vector specified in the OTL UI (see image above).</span><span style="font-size: small; font-weight: normal;">Every 'next' point position is calculated starting from the position of the previous point. For this reason I used a foreach loop making sure to uncheck the parameter "merge", since I want the same data to be processed and provided to the next cycle after adding a new point.</span><span style="font-size: small; font-weight: normal;">To calculate the closest point on the surface from a certain point in the world space, I've used VDB SDF. SDF are already a blessing provided by Houdini but they've always had a certain level of imprecision. VDB SDF are way faster to calculate, and much more precise (thank you DreamWorks Animation for this gift !).
The following is the VOP SOP structure to calculate the closest point on surface, given an SDF volume and a sample location in world space:</span><br />
<span style="font-size: small; font-weight: normal;"><br /></span>
<br />
<div style="text-align: justify;">
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.alessandropepe.com/wp-content/uploads/2013/05/cpos_vopsop.jpg" style="margin-left: 1em; margin-right: 1em;"><img alt="cpos_vopsop" class="alignnone size-medium wp-image-271" height="81" src="http://www.alessandropepe.com/wp-content/uploads/2013/05/cpos_vopsop-300x81.jpg" width="300" /></a></div>
</div>
<div style="text-align: justify;">
<br />
In this example I've created the wrapped L-System, and then used a Ray Sop to make sure each point really was on the surface, once per frame (Ray Sop is very fast).</div>
<div style="text-align: justify;">
I made sure to preserve the Width attribute calculated by default in the L-System Sop and I used it in the PolyWire Sop to drive the width of the Tubes as explained in the PolyWire documentation:</div>
<blockquote class="tr_bq">
<span style="color: grey;"><em>....This node works like the <a href="http://www.sidefx.com/docs/houdini12.5/nodes/sop/wire"><span style="color: grey;">Wireframe node</span></a>, but this node creates more complex tube geometry from curves, with smoother bends and intersections than Wireframe, especially for <a href="http://www.sidefx.com/docs/houdini12.5/nodes/sop/lsystem"><span style="color: grey;">L-systems</span></a>.</em></span>
<span style="color: grey;"><em>The four numerical parameters support all the local variables of the Point operation, plus the LSYSTEM specific variables of $WIDTH, $SEGS, $DIV, $LAGE, $GEN, and $ARC....</em></span></blockquote>
<div style="text-align: justify;">
<br />
WL has been calculated on a static WG (I choose the first frame of the hand animation). Then I used a Lattice Sop to wrap WL on the animated version of WG. Before applying the Lattice Sop I've calculated and stored the density of the points on a point attribute on the animated WG via VOP SOP where I used Point Cloud vex nodes to access the same geometry and return the number of points within a certain radius. Then I've used the density attribute to drive the size of the metaballs internally used by Lattice Sop.</div>
<div style="text-align: justify;">
Note:
L-Systems are perfect to create intricate veins systems. Because L-Systems are a parallel rewriting systems, their size might get huge very fast. For instance in the previous example the original L-System had 95 generations and ~66k points. Nevertheless the generation of the L-System itself was very fast (less than 1s per frame for 95 generations).<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="328" mozallowfullscreen="" src="http://player.vimeo.com/video/66213214?loop=1" webkitallowfullscreen="" width="500"></iframe><br />
<br />
<iframe allowfullscreen="" frameborder="0" height="328" mozallowfullscreen="" src="http://player.vimeo.com/video/66213216?loop=1" webkitallowfullscreen="" width="500"></iframe>
</div>
OTL processing time to calculate the Wrapped L-System in this example:
<br />
<br />
<div style="text-align: justify;">
Frames 1-84 (83 frames) : less than 2 minutes<br />
Frames 85-91 (7 frames) : ~1 minute
<br />
Frames 92-96 (4 frames) : ~1 minute
<br />
Frames 97-99 (2 frames) : ~1 minute<br />
Frames 100-102 (2 frames) : ~1 minute
<br />
Frames 103-104 (1 frame): ~1 minute<br />
Frame 116 : ~2 minutes<br />
Frame 122 : ~3 minutes<br />
Frame 131 : ~5 minutes<br />
Frame 138: ~10 minutes<br />
Frame 145: ~21 minutes<br />
<br /></div>
<div style="text-align: justify;">
Time to calculate 145 frames ~ 3h 40m.</div>
on an Intel Core i7 3.0Ghz - 4 cores - 8 logical
All VOP SOPs are set to 1 thread per proc.<br />
<div style="text-align: justify;">
<br />
As you can see , processing times increase exponentially when the number of L-System generation starts getting crazy so if the idea is, for instance, to cover a full animated character with veins, the best approach is divide and conquer, instead of using one single massive L-System.</div>
<div style="text-align: justify;">
Now, I guess this is just a way to accomplish this effect, I am sure there are many others I've not thought about. So, feel free to comment and pitch ideas !</div>
<div style="text-align: justify;">
<br /></div>
Unknownnoreply@blogger.com1Los Angeles, CA, USA34.0522342 -118.243684933.2099567 -119.5345784 34.8945117 -116.95279140000001