<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[heaust 🌪️ blog]]></title><description><![CDATA[heaust 🌪️ blog]]></description><link>https://blog.heaust.org</link><generator>RSS for Node</generator><lastBuildDate>Thu, 08 Oct 2026 01:42:35 GMT</lastBuildDate><atom:link href="https://blog.heaust.org/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Chapter 3: Fading | Heaust's POV]]></title><description><![CDATA["The Ships Are Gone" - Aftermath of Cyzicus
.
.
.
"ʍǟӄɛ ǟռ օʄʄɛʀ, ʍǟӄɛ ǟ ȶʀǟɖɛ
ֆȶʀɛռɢȶɦ, ʍǟɢɨƈ, ɦօռօʀ
ֆքʊռ ɨռ ֆɨʟʋɛʀ, ƈǟʀʋɛɖ ɨռ ʝǟɖɛ
ʟɛȶ ʏօʊʀ ֆօʊʟ ɮɛƈօʍɛ ʏօʊʀ քʏʀɛ
աɦɛռ ʄʀǟɢʍɛռȶֆ ɮɛʟɨɛ ʏօʊʀ ɦɛǟʀȶ'ֆ ɛռȶɨʀɛ"
**
My ears rang in the darkness,I could bare...]]></description><link>https://blog.heaust.org/eow-chapter-3</link><guid isPermaLink="true">https://blog.heaust.org/eow-chapter-3</guid><category><![CDATA[Fantasy ]]></category><category><![CDATA[scifi]]></category><category><![CDATA[novel]]></category><dc:creator><![CDATA[Heaust]]></dc:creator><pubDate>Fri, 20 Jan 2023 06:45:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1674382619780/6a2c6cec-c85b-45ce-ade0-dbf5a80bc0cf.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong><em>"The Ships Are Gone"</em> - Aftermath of Cyzicus</strong></p>
<p>.</p>
<p>.</p>
<p>.</p>
<p>"ʍǟӄɛ ǟռ օʄʄɛʀ, ʍǟӄɛ ǟ ȶʀǟɖɛ</p>
<p>ֆȶʀɛռɢȶɦ, ʍǟɢɨƈ, ɦօռօʀ</p>
<p>ֆքʊռ ɨռ ֆɨʟʋɛʀ, ƈǟʀʋɛɖ ɨռ ʝǟɖɛ</p>
<p>ʟɛȶ ʏօʊʀ ֆօʊʟ ɮɛƈօʍɛ ʏօʊʀ քʏʀɛ</p>
<p>աɦɛռ ʄʀǟɢʍɛռȶֆ ɮɛʟɨɛ ʏօʊʀ ɦɛǟʀȶ'ֆ ɛռȶɨʀɛ"</p>
<p>**</p>
<p>My ears rang in the darkness,<br />I could barely make out the words</p>
<p><em>Think, Think</em></p>
<p>The last thing I remember was a glass bell on the ceiling...<br />I was suffocating, hyperventilating and then the feeling of something heavy on my chest...</p>
<p>I felt strangely calm, I was in the flow state of mind.</p>
<p>It was only now  that I realized that the voice wasn't in my head<br />It was around me...<br />and getting stronger</p>
<p>It was in a strange melody that I heard it,<br />It was a whisper, yet it filled me entirely,<br />It sounded strange, out of this world,<br />almost faerie, frightening,<br />it was honest and It chilled me to the bone.</p>
<p><em>I wonder if this is a part of the MCT, some new Chrono-tech</em></p>
<p><em>f̳͓̩͇̎͌ͣ̽́aͨͧͤ҉̙̹̞͇̙͓̠ͅ.̧̱͉̌.̧̩͇͉̱͔̙̥͛̏̂͂.̨̻͇̦͎͖͙̮̱͛̃̀ ̘͈̘̥ͤ̾͒ͪ͘ì͍̯̩̅́͟l̢͈̟̺̳̦̫ͫ̍e̴̜̳̙ͮ͒ͫ̑.͍̘̞̰̳͔͙͕̆͗̍͟.̫̞͉̖̌̌͡.̵̦͇͍̖̹͎͑ ͨ̓̑̎͏̳̥d̨̞̰͊ͅͅ</em></p>
<p><em>No... I remember, I failed, I was dying, it's certain, I'm going to die like this</em></p>
<p><em>Think, Think</em></p>
<p><em>The cause is likely a breakdown of the nervous and cardiovascular system<br />from an intense turbulent chrono-particle concentration  
</em></p>
<p><em>I was even being given chest compressions, but that won't work<br />I need to regulate the flow of the particles since my nervous system is a mess  
</em></p>
<p><em>the transfer Crystal !!<br />My nervous system couldn't become a pseudo-chrono-Xtal... so what!<br />I have a literal one right here, please give me enough time to reprogram this!!</em></p>
<p>I opened a terminal on the crystal somewhere nearby me</p>
<p><strong>-------<br />Choose language: javascript<br />-------</strong></p>
<p>I didn't have enough time to worry about data types, so I chose JS</p>
<p><strong>-------<br />Initializing runtime environment: 100%<br />Accessing auxiliary scripts: 3% ^C<br />~Emergency Override: loading the main function in vim<br />-------</strong></p>
<p>First I wiped out previous configurations clean</p>
<p>I used an already implemented linked lattice structure<br />I made the crystal into 2 nodes, my body into another and the environment into the final one,<br />it's usually pretty dangerous to do it like this, it would destroy<br />the crystal for sure and me most likely, but I had no choice and this wasn't usually  </p>
<p>I willed the particles to go into the environment through the crystal's first node<br />and into my body from my surroundings through its second<br />Good, A flow was established, and the pressure on my heart was gone<br />I just needed someone to restart it now</p>
<p>...</p>
<p><em>Someone restart it now, please</em></p>
<p><em>...</em></p>
<p><em>Someone...</em></p>
<p><em>Anyone...</em></p>
<p><em>I'm going to die like this...</em></p>
<p>"Please Someone Help MEEEEEEEE!!!!" I tried to scream</p>
<p><em>In the end... is it useless after all?</em></p>
<p><em>Am I going to die here?</em></p>
<p><em>I'm going to die, aren't I?  
</em></p>
<p>"ɖօ ʏօʊ աǟռȶ ȶօ ʟɨʋɛ?"</p>
<p>The voice around me echoed violently inside my head<br />I could hear clearly what it said<br />It was asking me, to make a trade, It was offering me<br />offering me anything I wanted, right now my desire to live<br />It was conveyed, it was understood</p>
<p>Maybe I was just going crazy, hallucinations near the end<br />of one's life weren't uncommon after all, but for some reason,<br />I trusted it, I felt it wasn't lying, and it really would give me anything</p>
<p>"Yes! Please! Whatever you are, Get me out of this mess!"</p>
<p>I felt as if the voice nodded (don't ask)</p>
<p>And...</p>
<p>nothing happened,</p>
<p>...</p>
<p>it was a joke after all,</p>
<p>I didn't know what I expected,</p>
<p>It was the end,</p>
<p>I felt my consciousness slipping,</p>
<p>I wanted to cry</p>
<p>In the end, even in this, I was the minority 10%</p>
<p>what an absolute joke!<br />And with a cold numb rage, I slipped into nothingness.</p>
<hr />
<p><a target="_blank" href="https://blog.heaust.org/eow-chapter-2">Prologue</a> &lt;- <a target="_blank" href="https://heaust.hashnode.dev/eow-toc">Table of Contents</a> -&gt; Next Chapter</p>
<p>[If you need to contact me, you can comment below or email me at <a target="_blank" href="mailto:heaust.azure@gmail.com">heaust.azure@gmail.com</a>]</p>
]]></content:encoded></item><item><title><![CDATA[Chapter 2: Embarking | Aston's POV]]></title><description><![CDATA[IDX vibrates beside the pillow
looks at it with sleepy eyes_____
Name: Aston Blake
Scriptor Rank: 665 million
Provider Type: Rare
Chronos Density: Abnormally High
Magic Aptitude Estimation: Abnormally High
Provider Pay Grade: S+ Tier
_____
I still ca...]]></description><link>https://blog.heaust.org/eow-chapter-2</link><guid isPermaLink="true">https://blog.heaust.org/eow-chapter-2</guid><category><![CDATA[scifi]]></category><dc:creator><![CDATA[Heaust]]></dc:creator><pubDate>Fri, 20 Jan 2023 06:33:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1674382651073/7b0d0a9e-ed67-4474-94c1-9e8fb54af912.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>IDX vibrates beside the pillow</em></p>
<p><em>looks at it with sleepy eyes</em><br /><strong>_____</strong></p>
<p><strong>Name: Aston Blake</strong></p>
<p><strong>Scriptor Rank: 665 million</strong></p>
<p><strong>Provider Type: Rare</strong></p>
<p><strong>Chronos Density: Abnormally High</strong></p>
<p><strong>Magic Aptitude Estimation: Abnormally High</strong></p>
<p><strong>Provider Pay Grade: S+ Tier</strong></p>
<p><strong>_____</strong></p>
<p>I still can't believe these readings, they scream Noble and that's what people call us "The Nobles". Beings with an extremely high density of Chronos and an abundance of wealth as a reward.</p>
<p>I woke up to a feeling of something moving in my pants {It was the IDX don't get happy too early}.</p>
<p><strong>_____</strong></p>
<p><strong>Aston Blake,</strong></p>
<p><strong>Report to your assigned MCT venue,</strong></p>
<p><strong>Failure to report in 20:00 minutes will result in your disqualification for this round.</strong></p>
<p><strong>Do Not Reply.</strong></p>
<p><strong>Regards,<br />Chrono-X<br />_____</strong></p>
<p>I panicked like an introvert who has been called on stage and can't figure out why</p>
<p>Oh god! Oh god! Oh god! I have to rush!</p>
<p>I just leaped off of the bed and landed on the floor using my elbow as my point of initial contact</p>
<p>Oww, no time to waste gotta get ready, where are my things when I need them?</p>
<p>I tried to find my toothbrush but failed so I just drank a lot of water</p>
<p>Struggled to find appropriate formal clothes, and found a nice half coat and a white shirt with some decent black trousers and some formal black shoes</p>
<p>Made some poached eggs for breakfast, and had them with a glass of chilled milk (just the regular)</p>
<p>Headed out locking the house, forgot I can just teleport by transfer crystal at the MCT Venue, unlocked the house went back in</p>
<p><em>Uses transport crystal</em></p>
<p>It's so dark when we are mid-transport, I wonder if there are ways to light up this darkness</p>
<p>I was lost in thoughts with my eyes closed, suddenly the foreground lit up</p>
<p>Ok, that wasn't a lot of people, they had such elegance, such manners, and such boring behavior almost all of them were nobles.</p>
<p>I was completely lost, looking at the building's structure in awe, they really outdid themselves with this site.</p>
<p>A humble girl noticed my lost expression and directed me to the help desk</p>
<p>I approached the desk, "Excuse me, I am kind of lost and don't know where to go from here"</p>
<p>The woman at the desk looked me over.</p>
<p>"May I have your IDX please?"</p>
<p>"Here", I cheerfully handed it to her</p>
<p>She went . . . . .</p>
<p>She legit stood up and made a run for it to the support room.</p>
<p>As I stood there awkwardly, I wonder what she saw</p>
<p>Two helpers arrived from the same support room and told me they are there to direct me to the test chamber</p>
<p>I was skeptical of the special treatment</p>
<p>After that being a little scared {<em>it was my first time}</em>, I went into the directed room</p>
<p>I thought there would be monitoring devices, tubes, and a whole bunch of interesting devices strapped to me</p>
<p>But I was told to stand under a huge glass bell-like thing for 3 mins</p>
<p>and that was it, bummer, I was kinda looking forward to interacting with all kinds of new chrono-tech</p>
<p>I came out of the test chamber, the help desk lady came back wearing an expression of disbelief</p>
<p>She asked me about how I felt, I told her not so good after how cold she was just before</p>
<p>She apologized and asked me how I felt after the test, I said I feel normal</p>
<p>The shock she wore was exaggerated to cartoonish proportions at this point.</p>
<p>She said "Your Chronos Density was the highest recorded till now, even the recorder hit its limiter, Your Density is immeasurable as it has Ultra Dynamic flow which lets you convert and generate Chronos at impossible speeds, We had to put our Chrono radiators at full power for verifying if you have what it says on the IDX"</p>
<p>"to be honest I felt nothing at all", I remarked</p>
<p>She parted her lips to speak, only no voice came out.</p>
<p>Actually, in hindsight she might have said something after all, I was too distracted.</p>
<p>My eyes were drawn to the corner of the room,</p>
<p>I saw someone being transported over on a hover trolley, an accident?</p>
<p>For some reason, I felt my heart sink and a strange feeling of unbearable irrational grief washed over me.</p>
<p>I was angry, I was afraid, I was seeing red and I never felt so helpless in my entire life, I broke into tears, I gasped, I flailed, I screamed.</p>
<p>I was shocked at my reaction and what I felt that I could only consider a gross overreaction.</p>
<p>If only I knew then.</p>
<hr />
<p><a target="_blank" href="https://blog.heaust.org/eow-chapter-1">Prologue</a> &lt;- <a target="_blank" href="https://heaust.hashnode.dev/eow-toc">Table of Contents</a> -&gt; <a target="_blank" href="https://blog.heaust.org/eow-chapter-3">Next Chapter</a></p>
<p>[If you need to contact me, you can comment below or email me at <a target="_blank" href="mailto:heaust.azure@gmail.com">heaust.azure@gmail.com</a>]</p>
]]></content:encoded></item><item><title><![CDATA[Post Processing in ThreeJs | Make your Scene Pop]]></title><description><![CDATA[Introduction 🫱🏽‍🫲🏿
This chapter will be all about how to enhance the renders you get from ThreeJs and give them some flair ✨
This can all be done with the resources ThreeJs provides as well : )
We're going to learn about Post-Processing, as the n...]]></description><link>https://blog.heaust.org/threejs-chapter-5</link><guid isPermaLink="true">https://blog.heaust.org/threejs-chapter-5</guid><category><![CDATA[3d]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[ThreeJS]]></category><category><![CDATA[frontend]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Heaust]]></dc:creator><pubDate>Sat, 16 Apr 2022 10:25:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1650104090430/qGtGLnbv1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction 🫱🏽‍🫲🏿</h2>
<p>This chapter will be all about how to enhance the renders you get from ThreeJs and give them some flair ✨</p>
<p>This can all be done with the resources ThreeJs provides as well : )</p>
<p>We're going to learn about Post-Processing, as the name suggests, it's processing that happens <em>after</em> the scene has been rendered.</p>
<p>Essentially, we're taking the rendered scene and applying a filter to it, like you would in your native phone camera, Instagram, Snapchat etc.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650097946180/wTX6f7Yni.jpeg" alt="Instagram filter illustration" /></p>
<h2 id="heading-prerequisite">Prerequisite ✔️</h2>
<p>We covered how to make a scattering of cubes and how to make them light up on click in the <a target="_blank" href="https://heaust.hashnode.dev/threejs-chapter-4">last chapter</a>, we won't go over that again, here's the boilerplate we're using,</p>
<pre><code><span class="hljs-operator">&lt;</span>div style<span class="hljs-operator">=</span><span class="hljs-string">"position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;"</span> class<span class="hljs-operator">=</span><span class="hljs-string">"threeD"</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span>script async src<span class="hljs-operator">=</span><span class="hljs-string">"https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span>script <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"importmap"</span><span class="hljs-operator">&gt;</span>
    {
        <span class="hljs-string">"imports"</span>: {
            <span class="hljs-string">"three"</span>: <span class="hljs-string">"https://unpkg.com/three/build/three.module.js"</span>
        }
    }
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span>script <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"module"</span><span class="hljs-operator">&gt;</span>
        <span class="hljs-keyword">import</span> <span class="hljs-operator">*</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">THREE</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"three"</span>;
        <span class="hljs-keyword">import</span> { <span class="hljs-title">OrbitControls</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"https://unpkg.com/three/examples/jsm/controls/OrbitControls.js"</span>;

        const init <span class="hljs-operator">=</span> (
            targetClass <span class="hljs-operator">=</span> <span class="hljs-string">"threeD"</span>
        ) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
            const target <span class="hljs-operator">=</span> document.getElementsByClassName(targetClass);
            <span class="hljs-keyword">if</span> (target) {
                <span class="hljs-comment">// Renderer and Scene Setup</span>
                const renderer <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.WebGLRenderer({ alpha: <span class="hljs-literal">true</span> });
                const Cwidth <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    <span class="hljs-keyword">return</span> document.getElementsByClassName(targetClass)[<span class="hljs-number">0</span>].clientWidth;
                };
                const Cheight <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    <span class="hljs-keyword">return</span> document.getElementsByClassName(targetClass)[<span class="hljs-number">0</span>].clientHeight;
                };
                renderer.shadowMap.enabled <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
                renderer.setPixelRatio(window.devicePixelRatio);
                renderer.setSize(Cwidth(), Cheight());
                renderer.physicallyCorrectLights <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
                renderer.domElement.setAttribute(<span class="hljs-string">"id"</span>, <span class="hljs-string">"three-canvas"</span>);

                target[<span class="hljs-number">0</span>].appendChild(renderer.domElement);
                const scene <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Scene();
                const ambientLight <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">2</span>);
                scene.add(ambientLight);

                <span class="hljs-comment">// Camera Setup</span>
                const aspect <span class="hljs-operator">=</span> Cwidth() <span class="hljs-operator">/</span> Cheight();
                const fov <span class="hljs-operator">=</span> <span class="hljs-number">60</span>;
                const near <span class="hljs-operator">=</span> <span class="hljs-number">1.0</span>;
                const far <span class="hljs-operator">=</span> <span class="hljs-number">200.0</span>;
                const camera <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(fov, aspect, near, far);
                camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">30</span>, <span class="hljs-number">0</span>);
                camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

                <span class="hljs-comment">// Setting Up Orbital Controls </span>
                const controls <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> OrbitControls(camera, renderer.domElement);
                controls.update();

                <span class="hljs-comment">// Setting up Store for Raycasting</span>
                let raycastStore <span class="hljs-operator">=</span> [];
                const addToRCStore <span class="hljs-operator">=</span> (obj) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> raycastStore.<span class="hljs-built_in">push</span>(obj);
                const removeFromRCStore <span class="hljs-operator">=</span> (obj) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    <span class="hljs-keyword">for</span> (let i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> raycastStore.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
                        <span class="hljs-keyword">if</span> (raycastStore[i] <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> obj) raycastStore.splice(i, <span class="hljs-number">1</span>);
                    }
                }

                <span class="hljs-comment">// Setting up RayCaster</span>
                let ndcMouseX <span class="hljs-operator">=</span> <span class="hljs-number">-1</span>;
                let ndcMouseY <span class="hljs-operator">=</span> <span class="hljs-number">-1</span>;
                let rc <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Raycaster();

                document.addEventListener(<span class="hljs-string">"mousemove"</span>, (e) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    ndcMouseX <span class="hljs-operator">=</span> (e.clientX <span class="hljs-operator">/</span> window.innerWidth) <span class="hljs-operator">*</span> <span class="hljs-number">2</span> <span class="hljs-operator">-</span> <span class="hljs-number">1</span>;
                    ndcMouseY <span class="hljs-operator">=</span> <span class="hljs-operator">-</span>(e.clientY <span class="hljs-operator">/</span> window.innerHeight) <span class="hljs-operator">*</span> <span class="hljs-number">2</span> <span class="hljs-operator">+</span> <span class="hljs-number">1</span>;
                });

                const CheckRC <span class="hljs-operator">=</span> (camera, onIntersection, onEmpty <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { }) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    rc.setFromCamera(<span class="hljs-keyword">new</span> THREE.Vector3(ndcMouseX, ndcMouseY, <span class="hljs-number">0</span>), camera);
                    let intersects <span class="hljs-operator">=</span> rc.intersectObjects(raycastStore);
                    <span class="hljs-keyword">if</span> (intersects.<span class="hljs-built_in">length</span> <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>) {
                        onIntersection(intersects);
                    } <span class="hljs-keyword">else</span> {
                        onEmpty();
                    }
                }

                const RCJob <span class="hljs-operator">=</span> setInterval(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    CheckRC(
                        camera,
                        () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { document.body.style.cursor <span class="hljs-operator">=</span> <span class="hljs-string">"pointer"</span>; },
                        () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { document.body.style.cursor <span class="hljs-operator">=</span> <span class="hljs-string">"auto"</span>; }
                    );
                }, <span class="hljs-number">100</span>); <span class="hljs-comment">// Start an Hover Job</span>

                document.addEventListener(<span class="hljs-string">"mousedown"</span>, () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    CheckRC(
                        camera,
                        (intersects) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                            const col <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">"#b62886"</span>);
                            <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>intersects[<span class="hljs-number">0</span>].object.material.color.equals(col)) {
                                intersects[<span class="hljs-number">0</span>].object.material.color <span class="hljs-operator">=</span> col;
                                intersects[<span class="hljs-number">0</span>].object.material.emissive <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">"#AAAAAA"</span>);
                            } <span class="hljs-keyword">else</span> {
                                intersects[<span class="hljs-number">0</span>].object.material.color <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">"#639af6"</span>);;
                                intersects[<span class="hljs-number">0</span>].object.material.emissive <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">"#000000"</span>);
                            }
                        }
                    );
                }); <span class="hljs-comment">// Start a Click Job</span>

                <span class="hljs-comment">// Adding Stuff to Our World  </span>
                <span class="hljs-comment">// Add Point Light</span>
                const light <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.PointLight(
                    <span class="hljs-number">0xffffff</span> <span class="hljs-comment">/* Color */</span>,
                    <span class="hljs-number">80</span> <span class="hljs-comment">/* Intensity */</span>,
                    <span class="hljs-number">100</span> <span class="hljs-comment">/* Maximum Range of Light */</span>
                );
                light.position.set(<span class="hljs-number">30</span>, <span class="hljs-number">30</span>, <span class="hljs-number">30</span>);
                scene.add(light);

                <span class="hljs-comment">// Add Primitive Shapes</span>
                const randomIntFromInterval <span class="hljs-operator">=</span> (min, max) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { <span class="hljs-comment">// min and max included </span>
                    <span class="hljs-keyword">return</span> Math.floor(Math.random() <span class="hljs-operator">*</span> (max <span class="hljs-operator">-</span> min <span class="hljs-operator">+</span> <span class="hljs-number">1</span>) <span class="hljs-operator">+</span> min)
                }

                const spawnRandomCube <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    <span class="hljs-comment">// Make Shape</span>
                    const size <span class="hljs-operator">=</span> randomIntFromInterval(<span class="hljs-number">2</span>, <span class="hljs-number">7</span>);
                    const geometry <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.BoxBufferGeometry(
                        size <span class="hljs-comment">/** height */</span>,
                        size <span class="hljs-comment">/** width */</span>,
                        size <span class="hljs-comment">/** length */</span>);
                    <span class="hljs-comment">// Make Texture</span>
                    const material <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({ color: <span class="hljs-number">0x639af6</span> });
                    <span class="hljs-comment">// Make Cube</span>
                    const cube <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Mesh(geometry, material);
                    <span class="hljs-comment">// Random Position</span>
                    cube.position.set(
                        randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>),
                        randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>),
                        randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>)
                    );
                    <span class="hljs-comment">// Random Angle</span>
                    cube.rotation.set(
                        THREE.Math.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>)),
                        THREE.Math.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>)),
                        THREE.Math.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>))
                    )
                    <span class="hljs-comment">// Add to Scene</span>
                    scene.add(cube);
                    addToRCStore(cube);
                }

                <span class="hljs-keyword">for</span> (let i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> <span class="hljs-number">200</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) spawnRandomCube();

                <span class="hljs-comment">// Play Animation</span>
                const RAF <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    requestAnimationFrame(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                        <span class="hljs-comment">// Animations</span>

                        <span class="hljs-comment">// Recursively Render</span>
                        renderer.render(scene, camera);
                        RAF();
                    });
                };

                RAF();

                <span class="hljs-comment">// Responsiveness</span>
                window.addEventListener(
                    <span class="hljs-string">"resize"</span>,
                    () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                        camera.aspect <span class="hljs-operator">=</span> Cwidth() <span class="hljs-operator">/</span> Cheight();
                        renderer.setSize(Cwidth(), Cheight());
                        camera.updateProjectionMatrix();
                    },
                    <span class="hljs-literal">false</span>
                );
            }
        };

        init();
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
</code></pre><h2 id="heading-concept">Concept 🤔</h2>
<p>We'll have to setup an effects Composer which we can basically pass effects to, "effects" here are called Passes <em>( eg: Glitch Effect is called a Glitch Pass )</em></p>
<p>Render Effect or Render Pass is our base render, so we definitely need that, and then all the effects that we add will be applied in the order that we mention them in our code.</p>
<p>So if we do something like this in our code,</p>
<pre><code>composer.addPass( renderPass );
<span class="hljs-comment">/** some code */</span>
composer.addPass( bloomPass );
<span class="hljs-comment">/** some more code */</span>
composer.addPass( glitchPass );
</code></pre><p>We'll get the following effect,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650099558049/HvnUtW6CN.png" alt="Order Of application of Effects, illustration" /></p>
<p>Also, now that our Composer is actually giving us the final result, Composer should be one that handles the final rendering, not the WebGLRenderer. </p>
<h2 id="heading-setting-up">Setting Up 🏗️</h2>
<p>We'll need EffectComposer and RenderPass to use any  post-processing effect, so let's add them to our imports,</p>
<pre><code><span class="hljs-keyword">import</span> { <span class="hljs-title">EffectComposer</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"https://unpkg.com/three/examples/jsm/postprocessing/EffectComposer.js"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">RenderPass</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"https://unpkg.com/three/examples/jsm/postprocessing/RenderPass.js"</span>;
</code></pre><p>And now just above <code>// Play Animation</code>, we setup our post processing,</p>
<p>Let's make our composer and hand it the Render Pass,</p>
<pre><code><span class="hljs-comment">// Post Processing</span>
const composer <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> EffectComposer(renderer); <span class="hljs-comment">// make composer</span>
const renderPass <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> RenderPass(scene, camera); <span class="hljs-comment">// make render pass</span>
composer.addPass(renderPass); <span class="hljs-comment">// add render pass</span>
</code></pre><p>And now, since our Composer will actually give us the real final result, it should be the one that handles the final rendering (painting on the HTML Canvas),</p>
<p>So let's replace</p>
<pre><code>renderer.render(scene, camera);
</code></pre><p>in our <code>// Play Animation</code> section with</p>
<pre><code>composer.render(scene, camera);
</code></pre><p>Now our Setup is complete!!</p>
<p>Right now things should look exactly as they did before, but now we can easily add more effects, so let's try it :D</p>
<h2 id="heading-adding-bloom-effect">Adding Bloom Effect 🌟</h2>
<p>Alright, let's first import the Bloom effect constructor from ThreeJs,</p>
<pre><code><span class="hljs-keyword">import</span> { <span class="hljs-title">UnrealBloomPass</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"https://unpkg.com/three/examples/jsm/postprocessing/UnrealBloomPass.js"</span>;
</code></pre><p>Now in our <code>Post Processing</code> section, we can configure and add our Bloom : )</p>
<p>Let's make our Bloom effect with some config,</p>
<pre><code>const bloomPass <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> UnrealBloomPass(<span class="hljs-keyword">new</span> THREE.Vector2(Cwidth(), Cheight()));
<span class="hljs-comment">/** You can tweak the settings below to get best effect you desire */</span>
bloomPass.threshold <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
bloomPass.strength <span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.8</span>;
bloomPass.radius <span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.5</span>;
bloomPass.exposure <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
</code></pre><p>Now that we have our Bloom Effect prepared, let's add it to the composer,</p>
<pre><code>composer.addPass(bloomPass);
</code></pre><p>And we're done! that's it :D</p>
<p>Here's how it looks,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650101862257/waV3Ln-Tn.gif" alt="Bloom Effect Demo" /></p>
<p>The bloom effect essentially works on the emission / emissive property of the material, if we didn't give our cubes any emission there wouldn't be this glow-y  effect.</p>
<p>Here's the ThreeJs demo on Bloom where you can play with all the settings and see what does what. <em>(Click the image below to be redirected)</em></p>
<p><a target="_blank" href="https://threejs.org/examples/webgl_postprocessing_unreal_bloom.html"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650102405308/NOJAE8vLu.gif" alt="ofblum.gif" /></a></p>
<h2 id="heading-summary">Summary 📝</h2>
<p>So in this chapter we saw how to setup an Effects Composer and apply cool filters on our renders to make them really pop out :D</p>
<p><a target="_blank" href="https://github.com/mrdoob/three.js/tree/dev/examples/jsm/postprocessing">Here's</a> the collection of all the effects ThreeJs offers built in, use these for now, somewhere down the line we'll learn how to make our own : )</p>
<p>Here's all the code for this chapter,</p>
<pre><code><span class="hljs-operator">&lt;</span>div style<span class="hljs-operator">=</span><span class="hljs-string">"position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;"</span> class<span class="hljs-operator">=</span><span class="hljs-string">"threeD"</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span>script async src<span class="hljs-operator">=</span><span class="hljs-string">"https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span>script <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"importmap"</span><span class="hljs-operator">&gt;</span>
    {
        <span class="hljs-string">"imports"</span>: {
            <span class="hljs-string">"three"</span>: <span class="hljs-string">"https://unpkg.com/three/build/three.module.js"</span>
        }
    }
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span>script <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"module"</span><span class="hljs-operator">&gt;</span>
        <span class="hljs-keyword">import</span> <span class="hljs-operator">*</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">THREE</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"three"</span>;
        <span class="hljs-keyword">import</span> { <span class="hljs-title">OrbitControls</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"https://unpkg.com/three/examples/jsm/controls/OrbitControls.js"</span>;
        <span class="hljs-keyword">import</span> { <span class="hljs-title">UnrealBloomPass</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"https://unpkg.com/three/examples/jsm/postprocessing/UnrealBloomPass.js"</span>;
        <span class="hljs-keyword">import</span> { <span class="hljs-title">EffectComposer</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"https://unpkg.com/three/examples/jsm/postprocessing/EffectComposer.js"</span>;
        <span class="hljs-keyword">import</span> { <span class="hljs-title">RenderPass</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"https://unpkg.com/three/examples/jsm/postprocessing/RenderPass.js"</span>;
        const init <span class="hljs-operator">=</span> (
            targetClass <span class="hljs-operator">=</span> <span class="hljs-string">"threeD"</span>
        ) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
            const target <span class="hljs-operator">=</span> document.getElementsByClassName(targetClass);
            <span class="hljs-keyword">if</span> (target) {
                <span class="hljs-comment">// Renderer and Scene Setup</span>
                const renderer <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.WebGLRenderer({ alpha: <span class="hljs-literal">true</span> });
                const Cwidth <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    <span class="hljs-keyword">return</span> document.getElementsByClassName(targetClass)[<span class="hljs-number">0</span>].clientWidth;
                };
                const Cheight <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    <span class="hljs-keyword">return</span> document.getElementsByClassName(targetClass)[<span class="hljs-number">0</span>].clientHeight;
                };
                renderer.shadowMap.enabled <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
                renderer.setPixelRatio(window.devicePixelRatio);
                renderer.setSize(Cwidth(), Cheight());
                renderer.physicallyCorrectLights <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
                renderer.domElement.setAttribute(<span class="hljs-string">"id"</span>, <span class="hljs-string">"three-canvas"</span>);

                target[<span class="hljs-number">0</span>].appendChild(renderer.domElement);
                const scene <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Scene();
                const ambientLight <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">2</span>);
                scene.add(ambientLight);

                <span class="hljs-comment">// Camera Setup</span>
                const aspect <span class="hljs-operator">=</span> Cwidth() <span class="hljs-operator">/</span> Cheight();
                const fov <span class="hljs-operator">=</span> <span class="hljs-number">60</span>;
                const near <span class="hljs-operator">=</span> <span class="hljs-number">1.0</span>;
                const far <span class="hljs-operator">=</span> <span class="hljs-number">200.0</span>;
                const camera <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(fov, aspect, near, far);
                camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">30</span>, <span class="hljs-number">0</span>);
                camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

                <span class="hljs-comment">// Setting Up Orbital Controls </span>
                const controls <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> OrbitControls(camera, renderer.domElement);
                controls.update();

                <span class="hljs-comment">// Setting up Store for Raycasting</span>
                let raycastStore <span class="hljs-operator">=</span> [];
                const addToRCStore <span class="hljs-operator">=</span> (obj) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> raycastStore.<span class="hljs-built_in">push</span>(obj);
                const removeFromRCStore <span class="hljs-operator">=</span> (obj) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    <span class="hljs-keyword">for</span> (let i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> raycastStore.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
                        <span class="hljs-keyword">if</span> (raycastStore[i] <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> obj) raycastStore.splice(i, <span class="hljs-number">1</span>);
                    }
                }

                <span class="hljs-comment">// Setting up RayCaster</span>
                let ndcMouseX <span class="hljs-operator">=</span> <span class="hljs-number">-1</span>;
                let ndcMouseY <span class="hljs-operator">=</span> <span class="hljs-number">-1</span>;
                let rc <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Raycaster();

                document.addEventListener(<span class="hljs-string">"mousemove"</span>, (e) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    ndcMouseX <span class="hljs-operator">=</span> (e.clientX <span class="hljs-operator">/</span> window.innerWidth) <span class="hljs-operator">*</span> <span class="hljs-number">2</span> <span class="hljs-operator">-</span> <span class="hljs-number">1</span>;
                    ndcMouseY <span class="hljs-operator">=</span> <span class="hljs-operator">-</span>(e.clientY <span class="hljs-operator">/</span> window.innerHeight) <span class="hljs-operator">*</span> <span class="hljs-number">2</span> <span class="hljs-operator">+</span> <span class="hljs-number">1</span>;
                });

                const CheckRC <span class="hljs-operator">=</span> (camera, onIntersection, onEmpty <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { }) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    rc.setFromCamera(<span class="hljs-keyword">new</span> THREE.Vector3(ndcMouseX, ndcMouseY, <span class="hljs-number">0</span>), camera);
                    let intersects <span class="hljs-operator">=</span> rc.intersectObjects(raycastStore);
                    <span class="hljs-keyword">if</span> (intersects.<span class="hljs-built_in">length</span> <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>) {
                        onIntersection(intersects);
                    } <span class="hljs-keyword">else</span> {
                        onEmpty();
                    }
                }

                const RCJob <span class="hljs-operator">=</span> setInterval(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    CheckRC(
                        camera,
                        () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { document.body.style.cursor <span class="hljs-operator">=</span> <span class="hljs-string">"pointer"</span>; },
                        () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { document.body.style.cursor <span class="hljs-operator">=</span> <span class="hljs-string">"auto"</span>; }
                    );
                }, <span class="hljs-number">100</span>); <span class="hljs-comment">// Start an Hover Job</span>

                document.addEventListener(<span class="hljs-string">"mousedown"</span>, () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    CheckRC(
                        camera,
                        (intersects) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                            const col <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">"#b62886"</span>);
                            <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>intersects[<span class="hljs-number">0</span>].object.material.color.equals(col)) {
                                intersects[<span class="hljs-number">0</span>].object.material.color <span class="hljs-operator">=</span> col;
                                intersects[<span class="hljs-number">0</span>].object.material.emissive <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">"#AAAAAA"</span>);
                            } <span class="hljs-keyword">else</span> {
                                intersects[<span class="hljs-number">0</span>].object.material.color <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">"#639af6"</span>);;
                                intersects[<span class="hljs-number">0</span>].object.material.emissive <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">"#000000"</span>);
                            }
                        }
                    );
                }); <span class="hljs-comment">// Start a Click Job</span>

                <span class="hljs-comment">// Adding Stuff to Our World  </span>
                <span class="hljs-comment">// Add Point Light</span>
                const light <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.PointLight(
                    <span class="hljs-number">0xffffff</span> <span class="hljs-comment">/* Color */</span>,
                    <span class="hljs-number">80</span> <span class="hljs-comment">/* Intensity */</span>,
                    <span class="hljs-number">100</span> <span class="hljs-comment">/* Maximum Range of Light */</span>
                );
                light.position.set(<span class="hljs-number">30</span>, <span class="hljs-number">30</span>, <span class="hljs-number">30</span>);
                scene.add(light);

                <span class="hljs-comment">// Add Primitive Shapes</span>
                const randomIntFromInterval <span class="hljs-operator">=</span> (min, max) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { <span class="hljs-comment">// min and max included </span>
                    <span class="hljs-keyword">return</span> Math.floor(Math.random() <span class="hljs-operator">*</span> (max <span class="hljs-operator">-</span> min <span class="hljs-operator">+</span> <span class="hljs-number">1</span>) <span class="hljs-operator">+</span> min)
                }

                const spawnRandomCube <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    <span class="hljs-comment">// Make Shape</span>
                    const size <span class="hljs-operator">=</span> randomIntFromInterval(<span class="hljs-number">2</span>, <span class="hljs-number">7</span>);
                    const geometry <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.BoxBufferGeometry(
                        size <span class="hljs-comment">/** height */</span>,
                        size <span class="hljs-comment">/** width */</span>,
                        size <span class="hljs-comment">/** length */</span>);
                    <span class="hljs-comment">// Make Texture</span>
                    const material <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({ color: <span class="hljs-number">0x639af6</span> });
                    <span class="hljs-comment">// Make Cube</span>
                    const cube <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Mesh(geometry, material);
                    <span class="hljs-comment">// Random Position</span>
                    cube.position.set(
                        randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>),
                        randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>),
                        randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>)
                    );
                    <span class="hljs-comment">// Random Angle</span>
                    cube.rotation.set(
                        THREE.Math.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>)),
                        THREE.Math.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>)),
                        THREE.Math.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>))
                    )
                    <span class="hljs-comment">// Add to Scene</span>
                    scene.add(cube);
                    addToRCStore(cube);
                }

                <span class="hljs-keyword">for</span> (let i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> <span class="hljs-number">200</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) spawnRandomCube();

                <span class="hljs-comment">// Post Processing</span>
                const composer <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> EffectComposer(renderer); <span class="hljs-comment">// make composer</span>
                const renderPass <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> RenderPass(scene, camera); <span class="hljs-comment">// make render pass</span>
                composer.addPass(renderPass); <span class="hljs-comment">// add render pass</span>

                const bloomPass <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> UnrealBloomPass(<span class="hljs-keyword">new</span> THREE.Vector2(Cwidth(), Cheight()));
                bloomPass.threshold <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
                bloomPass.strength <span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.8</span>;
                bloomPass.radius <span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.5</span>;
                bloomPass.exposure <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;

                composer.addPass(bloomPass);



                <span class="hljs-comment">// Play Animation</span>
                const RAF <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    requestAnimationFrame(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                        <span class="hljs-comment">// Animations</span>

                        <span class="hljs-comment">// Recursively Render</span>
                        composer.render(scene, camera);
                        RAF();
                    });
                };

                RAF();

                <span class="hljs-comment">// Responsiveness</span>
                window.addEventListener(
                    <span class="hljs-string">"resize"</span>,
                    () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                        camera.aspect <span class="hljs-operator">=</span> Cwidth() <span class="hljs-operator">/</span> Cheight();
                        renderer.setSize(Cwidth(), Cheight());
                        camera.updateProjectionMatrix();
                    },
                    <span class="hljs-literal">false</span>
                );
            }
        };

        init();
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
</code></pre><h2 id="heading-conclusion">Conclusion 🍂</h2>
<p>So now the objects in our scene look great!, but does the background always have to be this dark depressing void though?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650103684021/5xf3xM-uh.png" alt="dark depressing void" /></p>
<p>Sure we can always do {alpha: true} in our WebGLRenderer initialization (and we have) and add a background image... but static background images are boring, we're doing 3D here! we won't accept anything less for any of our creations : )</p>
<p>So we'll learn how to easily import a 3D google-street-view-like environment into our scene to give some substance to it.</p>
<p>So do follow up on the chapter <strong>ThreeJs: Skyboxes and HDRIs</strong>!</p>
<p>Thank you so much for reading and as always, I hope you learned something : )</p>
]]></content:encoded></item><item><title><![CDATA[ThreeJs: How to Click]]></title><description><![CDATA[Introduction 🫱🏽‍🫲🏿
ThreeJs renders everything on a plain HTML Canvas, that raises an interesting question, how do we interact with individual objects inside your 3D environment ?
We can't identify a click as we usually do with the DOM as the targ...]]></description><link>https://blog.heaust.org/threejs-chapter-4</link><guid isPermaLink="true">https://blog.heaust.org/threejs-chapter-4</guid><category><![CDATA[ThreeJS]]></category><category><![CDATA[frontend]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[3d]]></category><category><![CDATA[Web Design]]></category><dc:creator><![CDATA[Heaust]]></dc:creator><pubDate>Fri, 15 Apr 2022 09:03:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1650013119713/segLjh_72.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction 🫱🏽‍🫲🏿</h2>
<p>ThreeJs renders everything on a plain HTML Canvas, that raises an interesting question, how do we interact with individual objects inside your 3D environment ?</p>
<p>We can't identify a click as we usually do with the DOM as the target of the click will always be the Canvas.</p>
<p>In this article we'll go over the concept and implementation of a clever clicking method, the ThreeJs way!</p>
<h2 id="heading-prerequisite">Prerequisite ✔️</h2>
<p>We went over the process of setting up a bunch of scattered cubes with ThreeJs in the <a target="_blank" href="https://heaust.hashnode.dev/threejs-chapter-3">last chapter</a>, we won't go over that again here.</p>
<p>This is the boilerplate we're using, </p>
<pre><code><span class="hljs-operator">&lt;</span>div style<span class="hljs-operator">=</span><span class="hljs-string">"position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;"</span> class<span class="hljs-operator">=</span><span class="hljs-string">"threeD"</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
<span class="hljs-operator">&lt;</span>script async src<span class="hljs-operator">=</span><span class="hljs-string">"https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
<span class="hljs-operator">&lt;</span>script <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"importmap"</span><span class="hljs-operator">&gt;</span>
{
    <span class="hljs-string">"imports"</span>: {
        <span class="hljs-string">"three"</span>: <span class="hljs-string">"https://unpkg.com/three/build/three.module.js"</span>
    }
}
<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
<span class="hljs-operator">&lt;</span>script <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"module"</span><span class="hljs-operator">&gt;</span>
    <span class="hljs-keyword">import</span> <span class="hljs-operator">*</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">THREE</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"three"</span>;
    <span class="hljs-keyword">import</span> { <span class="hljs-title">OrbitControls</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"https://unpkg.com/three/examples/jsm/controls/OrbitControls.js"</span>;

    const init <span class="hljs-operator">=</span> (
        targetClass <span class="hljs-operator">=</span> <span class="hljs-string">"threeD"</span>
    ) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
        const target <span class="hljs-operator">=</span> document.getElementsByClassName(targetClass);
        <span class="hljs-keyword">if</span> (target) {
            <span class="hljs-comment">// Renderer and Scene Setup</span>
            const renderer <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.WebGLRenderer({ alpha: <span class="hljs-literal">true</span> });
            const Cwidth <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                <span class="hljs-keyword">return</span> document.getElementsByClassName(targetClass)[<span class="hljs-number">0</span>].clientWidth;
            };
            const Cheight <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                <span class="hljs-keyword">return</span> document.getElementsByClassName(targetClass)[<span class="hljs-number">0</span>].clientHeight;
            };
            renderer.shadowMap.enabled <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(Cwidth(), Cheight());
            renderer.physicallyCorrectLights <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
            renderer.domElement.setAttribute(<span class="hljs-string">"id"</span>, <span class="hljs-string">"three-canvas"</span>);

            target[<span class="hljs-number">0</span>].appendChild(renderer.domElement);
            const scene <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Scene();
            const ambientLight <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">2</span>);
            scene.add(ambientLight);

            <span class="hljs-comment">// Camera Setup</span>
            const aspect <span class="hljs-operator">=</span> Cwidth() <span class="hljs-operator">/</span> Cheight();
            const fov <span class="hljs-operator">=</span> <span class="hljs-number">60</span>;
            const near <span class="hljs-operator">=</span> <span class="hljs-number">1.0</span>;
            const far <span class="hljs-operator">=</span> <span class="hljs-number">200.0</span>;
            const camera <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(fov, aspect, near, far);
            camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">30</span>, <span class="hljs-number">0</span>);
            camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

            <span class="hljs-comment">// Setting Up Orbital Controls </span>
            const controls <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> OrbitControls(camera, renderer.domElement);
            controls.update();

            <span class="hljs-comment">// Adding Stuff to Our World  </span>
            <span class="hljs-comment">// Add Point Light</span>
            const light <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.PointLight(
                <span class="hljs-number">0xffffff</span> <span class="hljs-comment">/* Color */</span>,
                <span class="hljs-number">80</span> <span class="hljs-comment">/* Intensity */</span>,
                <span class="hljs-number">100</span> <span class="hljs-comment">/* Maximum Range of Light */</span>
            );
            light.position.set(<span class="hljs-number">30</span>, <span class="hljs-number">30</span>, <span class="hljs-number">30</span>);
            scene.add(light);

            <span class="hljs-comment">// Add Primitive Shapes</span>
            const randomIntFromInterval <span class="hljs-operator">=</span> (min, max) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { <span class="hljs-comment">// min and max included </span>
                <span class="hljs-keyword">return</span> Math.floor(Math.random() <span class="hljs-operator">*</span> (max <span class="hljs-operator">-</span> min <span class="hljs-operator">+</span> <span class="hljs-number">1</span>) <span class="hljs-operator">+</span> min)
            }

            const spawnRandomCube <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                <span class="hljs-comment">// Make Shape</span>
                const size <span class="hljs-operator">=</span> randomIntFromInterval(<span class="hljs-number">2</span>, <span class="hljs-number">7</span>);
                const geometry <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.BoxBufferGeometry(
                    size <span class="hljs-comment">/** height */</span>,
                    size <span class="hljs-comment">/** width */</span>,
                    size <span class="hljs-comment">/** length */</span>);
                <span class="hljs-comment">// Make Texture</span>
                const material <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({ color: <span class="hljs-number">0x639af6</span> });
                <span class="hljs-comment">// Make Cube</span>
                const cube <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Mesh(geometry, material);
                <span class="hljs-comment">// Random Position</span>
                cube.position.set(
                    randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>),
                    randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>),
                    randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>)
                );
                <span class="hljs-comment">// Random Angle</span>
                cube.rotation.set(
                    THREE.Math.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>)),
                    THREE.Math.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>)),
                    THREE.Math.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>))
                )
                <span class="hljs-comment">// Add to Scene</span>
                scene.add(cube);
            }

            <span class="hljs-keyword">for</span> (let i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> <span class="hljs-number">200</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) spawnRandomCube();

            <span class="hljs-comment">// Play Animation</span>
            const RAF <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                requestAnimationFrame(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    <span class="hljs-comment">// Animations</span>

                    <span class="hljs-comment">// Recursively Render</span>
                    renderer.render(scene, camera);
                    RAF();
                });
            };

            RAF();

            <span class="hljs-comment">// Responsiveness</span>
            window.addEventListener(
                <span class="hljs-string">"resize"</span>,
                () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    camera.aspect <span class="hljs-operator">=</span> Cwidth() <span class="hljs-operator">/</span> Cheight();
                    renderer.setSize(Cwidth(), Cheight());
                    camera.updateProjectionMatrix();
                },
                <span class="hljs-literal">false</span>
            );
        }
    };

    init();
<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
</code></pre><h2 id="heading-concept">Concept 🤔</h2>
<p>Let's go back to the basics,</p>
<p>We know that whatever the camera sees is being rendered on the canvas,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650004908054/drbjDxkpV.png" alt="Camera - Canvas Equivalence" /></p>
<p>So visually, there's no difference if the cursor was being rendered by browser on top of the canvas or on just above the camera by ThreeJs, so let's pretend the latter for a minute here, in that case...</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650006308525/mFH_wvRyW.png" alt="Cursor in a ThreeJs Scene" /></p>
<p>So now that we know where the cursor is half of our problem is solved!</p>
<p>Now all we have to do is Cast a ray from where the cursor is supposed to be perpendicular to our camera's plane,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650007073673/OVL3cWc8T.png" alt="Ray cast from cursor, illustration" /></p>
<p>Now whatever intersects with that ray, we can say is the object our cursor is "hovering" over : )</p>
<p>We can do this Ray casting thing over and over constantly to check what our cursor is hovering over.</p>
<p>We can now, having the hover object, easily do "click actions", "hover actions" etc with any objects in the Scene.</p>
<p><strong>A few things to keep in mind here though, you have to keep Ray Casting over and over to stay updated on what the "hover object" is in real time.</strong></p>
<p><strong>Ray Casting is really expensive so doing it too frequently and too much might cause lag, so in our example here we're doing it every 100 ms.</strong></p>
<p>If you're not too concerned with performance you can make it more frequently, just be aware of the trade off.</p>
<h2 id="heading-implementation">Implementation ⚙️</h2>
<p><em>This will be right after we set up our Orbit Controls</em></p>
<p>First we set up a store of items we want to respond to.</p>
<p>It'll just be an array of those objects, but let's give it a proper structure to make it easy to add and remove items.</p>
<pre><code>    <span class="hljs-comment">// Setting up Store for Raycasting</span>
    let raycastStore <span class="hljs-operator">=</span> [];
    const addToRCStore <span class="hljs-operator">=</span> (obj) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> raycastStore.<span class="hljs-built_in">push</span>(obj);
    const removeFromRCStore <span class="hljs-operator">=</span> (obj) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
        <span class="hljs-keyword">for</span> (let i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> raycastStore.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            <span class="hljs-keyword">if</span> (raycastStore[i] <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> obj) raycastStore.splice(i, <span class="hljs-number">1</span>);
        }
    }
</code></pre><p>now whenever you do <code>scene.add(/** An Object you wanna respond to */)</code>, be sure to also do <code>addToRCStore(/** The object */)</code> and <code>removeFromRCStore(/** The Object */)</code> when you no longer want to respond to it.</p>
<p>Now let's setup the Ray Caster that ThreeJs offers and initialize the variables that'll keep track of the mouse position.</p>
<pre><code>    <span class="hljs-comment">// Setting up RayCaster</span>
    let ndcMouseX <span class="hljs-operator">=</span> <span class="hljs-number">-1</span>;
    let ndcMouseY <span class="hljs-operator">=</span> <span class="hljs-number">-1</span>;
    let rc <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Raycaster();
</code></pre><p>We'll refresh the mouse positions whenever the mouse moves,</p>
<pre><code>    document.addEventListener(<span class="hljs-string">"mousemove"</span>, (e) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
        ndcMouseX <span class="hljs-operator">=</span> (e.clientX <span class="hljs-operator">/</span> window.innerWidth) <span class="hljs-operator">*</span> <span class="hljs-number">2</span> <span class="hljs-operator">-</span> <span class="hljs-number">1</span>;
        ndcMouseY <span class="hljs-operator">=</span> <span class="hljs-operator">-</span>(e.clientY <span class="hljs-operator">/</span> window.innerHeight) <span class="hljs-operator">*</span> <span class="hljs-number">2</span> <span class="hljs-operator">+</span> <span class="hljs-number">1</span>;
    });
</code></pre><p>Now notice we aren't exactly keeping track of the mouse pointer but some function of it.</p>
<p>That's because the browser gives us the mouse position values in pixels as units, from 0 to width or height,</p>
<p>But ThreeJs, at render time, scales everything down to a cube of -1 to 1</p>
<p>So we need to make a transformation like so,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650011177518/UKWA0BghR.png" alt="Screen to Camera dimensions transform" /></p>
<p>Now that that's done, let's actually make a function that checks for intersections and does something if an intersection is found and some other thing when it isn't.</p>
<pre><code>const CheckRC <span class="hljs-operator">=</span> (camera, onIntersection, onEmpty <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { }) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    rc.setFromCamera(<span class="hljs-keyword">new</span> THREE.Vector3(ndcMouseX, ndcMouseY, <span class="hljs-number">0</span>), camera);
    let intersects <span class="hljs-operator">=</span> rc.intersectObjects(raycastStore);
    <span class="hljs-keyword">if</span> (intersects.<span class="hljs-built_in">length</span> <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>) {
        onIntersection(intersects);
    } <span class="hljs-keyword">else</span> {
        onEmpty();
    }
}
</code></pre><p>Now we have a function that performs 1 Ray Cast for us and performs required actions.</p>
<p>This enables us to perform a myriad of responses, for example let's change cursor to pointer on hover of an object,</p>
<pre><code>    const RCJob <span class="hljs-operator">=</span> setInterval(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
        CheckRC(
            camera,
            () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { document.body.style.cursor <span class="hljs-operator">=</span> <span class="hljs-string">"pointer"</span>; },
            () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { document.body.style.cursor <span class="hljs-operator">=</span> <span class="hljs-string">"auto"</span>; }
        );
    }, <span class="hljs-number">100</span>); <span class="hljs-comment">// Start an Hover Job</span>
</code></pre><p>Or lets make an object glow on Click,</p>
<pre><code>    document.addEventListener(<span class="hljs-string">"mousedown"</span>, () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
        CheckRC(
            camera,
            (intersects) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                const col <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">"#b62886"</span>);
                <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>intersects[<span class="hljs-number">0</span>].object.material.color.equals(col)) {
                    intersects[<span class="hljs-number">0</span>].object.material.color <span class="hljs-operator">=</span> col;
                    intersects[<span class="hljs-number">0</span>].object.material.emissive <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">"#AAAAAA"</span>);
                } <span class="hljs-keyword">else</span> {
                    intersects[<span class="hljs-number">0</span>].object.material.color <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">"#639af6"</span>);;
                    intersects[<span class="hljs-number">0</span>].object.material.emissive <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">"#000000"</span>);
                }
            }
        );
    }); <span class="hljs-comment">// Start a Click Job</span>
</code></pre><p>After all that, this is what we get,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650012272257/xJXXmC3Av.gif" alt="demo of hover and click" /></p>
<h1 id="heading-summary">Summary</h1>
<p>So in this Chapter we learned how to interact with the objects inside our ThreeJs with our cursor with a clever Ray Casting setup,</p>
<p>Here's the full code for you to setup,</p>
<pre><code><span class="hljs-operator">&lt;</span>div style<span class="hljs-operator">=</span><span class="hljs-string">"position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;"</span> class<span class="hljs-operator">=</span><span class="hljs-string">"threeD"</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span>script async src<span class="hljs-operator">=</span><span class="hljs-string">"https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span>script <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"importmap"</span><span class="hljs-operator">&gt;</span>
    {
        <span class="hljs-string">"imports"</span>: {
            <span class="hljs-string">"three"</span>: <span class="hljs-string">"https://unpkg.com/three/build/three.module.js"</span>
        }
    }
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span>script <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"module"</span><span class="hljs-operator">&gt;</span>
        <span class="hljs-keyword">import</span> <span class="hljs-operator">*</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">THREE</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"three"</span>;
        <span class="hljs-keyword">import</span> { <span class="hljs-title">OrbitControls</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"https://unpkg.com/three/examples/jsm/controls/OrbitControls.js"</span>;

        const init <span class="hljs-operator">=</span> (
            targetClass <span class="hljs-operator">=</span> <span class="hljs-string">"threeD"</span>
        ) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
            const target <span class="hljs-operator">=</span> document.getElementsByClassName(targetClass);
            <span class="hljs-keyword">if</span> (target) {
                <span class="hljs-comment">// Renderer and Scene Setup</span>
                const renderer <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.WebGLRenderer({ alpha: <span class="hljs-literal">true</span> });
                const Cwidth <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    <span class="hljs-keyword">return</span> document.getElementsByClassName(targetClass)[<span class="hljs-number">0</span>].clientWidth;
                };
                const Cheight <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    <span class="hljs-keyword">return</span> document.getElementsByClassName(targetClass)[<span class="hljs-number">0</span>].clientHeight;
                };
                renderer.shadowMap.enabled <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
                renderer.setPixelRatio(window.devicePixelRatio);
                renderer.setSize(Cwidth(), Cheight());
                renderer.physicallyCorrectLights <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
                renderer.domElement.setAttribute(<span class="hljs-string">"id"</span>, <span class="hljs-string">"three-canvas"</span>);

                target[<span class="hljs-number">0</span>].appendChild(renderer.domElement);
                const scene <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Scene();
                const ambientLight <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">2</span>);
                scene.add(ambientLight);

                <span class="hljs-comment">// Camera Setup</span>
                const aspect <span class="hljs-operator">=</span> Cwidth() <span class="hljs-operator">/</span> Cheight();
                const fov <span class="hljs-operator">=</span> <span class="hljs-number">60</span>;
                const near <span class="hljs-operator">=</span> <span class="hljs-number">1.0</span>;
                const far <span class="hljs-operator">=</span> <span class="hljs-number">200.0</span>;
                const camera <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(fov, aspect, near, far);
                camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">30</span>, <span class="hljs-number">0</span>);
                camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

                <span class="hljs-comment">// Setting Up Orbital Controls </span>
                const controls <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> OrbitControls(camera, renderer.domElement);
                controls.update();

                <span class="hljs-comment">// Setting up Store for Raycasting</span>
                let raycastStore <span class="hljs-operator">=</span> [];
                const addToRCStore <span class="hljs-operator">=</span> (obj) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> raycastStore.<span class="hljs-built_in">push</span>(obj);
                const removeFromRCStore <span class="hljs-operator">=</span> (obj) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    <span class="hljs-keyword">for</span> (let i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> raycastStore.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
                        <span class="hljs-keyword">if</span> (raycastStore[i] <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> obj) raycastStore.splice(i, <span class="hljs-number">1</span>);
                    }
                }

                <span class="hljs-comment">// Setting up RayCaster</span>
                let ndcMouseX <span class="hljs-operator">=</span> <span class="hljs-number">-1</span>;
                let ndcMouseY <span class="hljs-operator">=</span> <span class="hljs-number">-1</span>;
                let rc <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Raycaster();

                document.addEventListener(<span class="hljs-string">"mousemove"</span>, (e) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    ndcMouseX <span class="hljs-operator">=</span> (e.clientX <span class="hljs-operator">/</span> window.innerWidth) <span class="hljs-operator">*</span> <span class="hljs-number">2</span> <span class="hljs-operator">-</span> <span class="hljs-number">1</span>;
                    ndcMouseY <span class="hljs-operator">=</span> <span class="hljs-operator">-</span>(e.clientY <span class="hljs-operator">/</span> window.innerHeight) <span class="hljs-operator">*</span> <span class="hljs-number">2</span> <span class="hljs-operator">+</span> <span class="hljs-number">1</span>;
                });

                const CheckRC <span class="hljs-operator">=</span> (camera, onIntersection, onEmpty <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { }) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    rc.setFromCamera(<span class="hljs-keyword">new</span> THREE.Vector3(ndcMouseX, ndcMouseY, <span class="hljs-number">0</span>), camera);
                    let intersects <span class="hljs-operator">=</span> rc.intersectObjects(raycastStore);
                    <span class="hljs-keyword">if</span> (intersects.<span class="hljs-built_in">length</span> <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>) {
                        onIntersection(intersects);
                    } <span class="hljs-keyword">else</span> {
                        onEmpty();
                    }
                }

                const RCJob <span class="hljs-operator">=</span> setInterval(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    CheckRC(
                        camera,
                        () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { document.body.style.cursor <span class="hljs-operator">=</span> <span class="hljs-string">"pointer"</span>; },
                        () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { document.body.style.cursor <span class="hljs-operator">=</span> <span class="hljs-string">"auto"</span>; }
                    );
                }, <span class="hljs-number">100</span>); <span class="hljs-comment">// Start an Hover Job</span>

                document.addEventListener(<span class="hljs-string">"mousedown"</span>, () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    CheckRC(
                        camera,
                        (intersects) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                            const col <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">"#b62886"</span>);
                            <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>intersects[<span class="hljs-number">0</span>].object.material.color.equals(col)) {
                                intersects[<span class="hljs-number">0</span>].object.material.color <span class="hljs-operator">=</span> col;
                                intersects[<span class="hljs-number">0</span>].object.material.emissive <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">"#AAAAAA"</span>);
                            } <span class="hljs-keyword">else</span> {
                                intersects[<span class="hljs-number">0</span>].object.material.color <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">"#639af6"</span>);;
                                intersects[<span class="hljs-number">0</span>].object.material.emissive <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">"#000000"</span>);
                            }
                        }
                    );
                }); <span class="hljs-comment">// Start a Click Job</span>

                <span class="hljs-comment">// Adding Stuff to Our World  </span>
                <span class="hljs-comment">// Add Point Light</span>
                const light <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.PointLight(
                    <span class="hljs-number">0xffffff</span> <span class="hljs-comment">/* Color */</span>,
                    <span class="hljs-number">80</span> <span class="hljs-comment">/* Intensity */</span>,
                    <span class="hljs-number">100</span> <span class="hljs-comment">/* Maximum Range of Light */</span>
                );
                light.position.set(<span class="hljs-number">30</span>, <span class="hljs-number">30</span>, <span class="hljs-number">30</span>);
                scene.add(light);

                <span class="hljs-comment">// Add Primitive Shapes</span>
                const randomIntFromInterval <span class="hljs-operator">=</span> (min, max) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { <span class="hljs-comment">// min and max included </span>
                    <span class="hljs-keyword">return</span> Math.floor(Math.random() <span class="hljs-operator">*</span> (max <span class="hljs-operator">-</span> min <span class="hljs-operator">+</span> <span class="hljs-number">1</span>) <span class="hljs-operator">+</span> min)
                }

                const spawnRandomCube <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    <span class="hljs-comment">// Make Shape</span>
                    const size <span class="hljs-operator">=</span> randomIntFromInterval(<span class="hljs-number">2</span>, <span class="hljs-number">7</span>);
                    const geometry <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.BoxBufferGeometry(
                        size <span class="hljs-comment">/** height */</span>,
                        size <span class="hljs-comment">/** width */</span>,
                        size <span class="hljs-comment">/** length */</span>);
                    <span class="hljs-comment">// Make Texture</span>
                    const material <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({ color: <span class="hljs-number">0x639af6</span> });
                    <span class="hljs-comment">// Make Cube</span>
                    const cube <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Mesh(geometry, material);
                    <span class="hljs-comment">// Random Position</span>
                    cube.position.set(
                        randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>),
                        randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>),
                        randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>)
                    );
                    <span class="hljs-comment">// Random Angle</span>
                    cube.rotation.set(
                        THREE.Math.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>)),
                        THREE.Math.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>)),
                        THREE.Math.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>))
                    )
                    <span class="hljs-comment">// Add to Scene</span>
                    scene.add(cube);
                    addToRCStore(cube);
                }

                <span class="hljs-keyword">for</span> (let i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> <span class="hljs-number">200</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) spawnRandomCube();

                <span class="hljs-comment">// Play Animation</span>
                const RAF <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    requestAnimationFrame(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                        <span class="hljs-comment">// Animations</span>

                        <span class="hljs-comment">// Recursively Render</span>
                        renderer.render(scene, camera);
                        RAF();
                    });
                };

                RAF();

                <span class="hljs-comment">// Responsiveness</span>
                window.addEventListener(
                    <span class="hljs-string">"resize"</span>,
                    () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                        camera.aspect <span class="hljs-operator">=</span> Cwidth() <span class="hljs-operator">/</span> Cheight();
                        renderer.setSize(Cwidth(), Cheight());
                        camera.updateProjectionMatrix();
                    },
                    <span class="hljs-literal">false</span>
                );
            }
        };

        init();
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
</code></pre><h1 id="heading-conclusion">Conclusion</h1>
<p>Now we're really getting into the 3D world and how we can interact with it.</p>
<p>One thing you probably noticed in here is that the glow of the cubes looks less like glow and more like a change of color, and you would be right.</p>
<p>To make the glow more "glow"-like we need to apply a post-processing step called bloom.</p>
<p>We'll see that and everything Post-Processing in the next article <a target="_blank" href="https://heaust.hashnode.dev/threejs-chapter-5"><strong>Post Processing in ThreeJs | Make your Scene Pop</strong></a></p>
<p>So see you there! As always, hope you learned something : )</p>
<p>[If you need to contact me, you can comment down below, or email me at heaust.azure@gmail.com]</p>
]]></content:encoded></item><item><title><![CDATA[Let's have some fun with ThreeJs | Basics]]></title><description><![CDATA[Introduction 🫱🏽‍🫲🏿
In this article, we'll mess with a bunch of ThreeJs features and try to make interesting things using purely ThreeJs.
We'll create lots of shapes, tinker with all sorts of material options to make them look a certain way, and g...]]></description><link>https://blog.heaust.org/threejs-chapter-3</link><guid isPermaLink="true">https://blog.heaust.org/threejs-chapter-3</guid><category><![CDATA[ThreeJS]]></category><category><![CDATA[3d]]></category><category><![CDATA[frontend]]></category><category><![CDATA[WebGL]]></category><category><![CDATA[Web Design]]></category><dc:creator><![CDATA[Heaust]]></dc:creator><pubDate>Sun, 05 Dec 2021 08:20:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1638692245698/SoQsTtsu3.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction 🫱🏽‍🫲🏿</h2>
<p>In this article, we'll mess with a bunch of ThreeJs features and try to make interesting things using purely ThreeJs.</p>
<p>We'll create lots of shapes, tinker with all sorts of material options to make them look a certain way, and go over some time saving optimizations!</p>
<p>So let's get into it!</p>
<p>(PS: If you just want the code, head to the <strong>Summary</strong> Section.)</p>
<h2 id="heading-prerequisite">Prerequisite ✔️</h2>
<p>We went over the process of setting up ThreeJs in the <a target="_blank" href="https://heaust.hashnode.dev/threejs-chapter-2">last chapter</a>, we won't go over that again here.</p>
<p>This is the boilerplate we're using,</p>
<pre><code><span class="hljs-operator">&lt;</span>div
    style<span class="hljs-operator">=</span><span class="hljs-string">"position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;"</span>
    class<span class="hljs-operator">=</span><span class="hljs-string">"threeD-Canvas"</span>
<span class="hljs-operator">&gt;</span><span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
<span class="hljs-operator">&lt;</span>script async src<span class="hljs-operator">=</span><span class="hljs-string">"https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
<span class="hljs-operator">&lt;</span>script <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"importmap"</span><span class="hljs-operator">&gt;</span>
{
    <span class="hljs-string">"imports"</span>: {
        <span class="hljs-string">"three"</span>: <span class="hljs-string">"https://unpkg.com/three/build/three.module.js"</span>
    }
}
<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
<span class="hljs-operator">&lt;</span>script <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"module"</span><span class="hljs-operator">&gt;</span>
    <span class="hljs-keyword">import</span> <span class="hljs-operator">*</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">THREE</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"three"</span>;

    const init <span class="hljs-operator">=</span> (
        targetClass <span class="hljs-operator">=</span> <span class="hljs-string">"threeD-Canvas"</span>
    ) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
        const target <span class="hljs-operator">=</span> document.getElementsByClassName(targetClass);
        <span class="hljs-keyword">if</span> (target) {
            <span class="hljs-comment">// Renderer and Scene Setup</span>
            const renderer <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.WebGLRenderer({ alpha: <span class="hljs-literal">true</span> });
            const Cwidth <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                <span class="hljs-keyword">return</span> document.getElementsByClassName(targetClass)[<span class="hljs-number">0</span>].clientWidth;
            };
            const Cheight <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                <span class="hljs-keyword">return</span> document.getElementsByClassName(targetClass)[<span class="hljs-number">0</span>].clientHeight;
            };
            renderer.shadowMap.enabled <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(Cwidth(), Cheight());
            renderer.physicallyCorrectLights <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
            renderer.domElement.setAttribute(<span class="hljs-string">"id"</span>, <span class="hljs-string">"three-canvas"</span>);

            target[<span class="hljs-number">0</span>].appendChild(renderer.domElement);
            const scene <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Scene();
            const ambientLight <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">2</span>);
            scene.add(ambientLight);

            <span class="hljs-comment">// Camera Setup</span>
            const aspect <span class="hljs-operator">=</span> Cwidth() <span class="hljs-operator">/</span> Cheight();
            const fov <span class="hljs-operator">=</span> <span class="hljs-number">60</span>;
            const near <span class="hljs-operator">=</span> <span class="hljs-number">1.0</span>;
            const far <span class="hljs-operator">=</span> <span class="hljs-number">200.0</span>;
            const camera <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(fov, aspect, near, far);
            camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">30</span>, <span class="hljs-number">0</span>);
            camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

            <span class="hljs-comment">// Adding Stuff to Our World  </span>
            <span class="hljs-comment">// Add Point Light</span>
            const light <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.PointLight(
                <span class="hljs-number">0xffffff</span> <span class="hljs-comment">/* Color */</span>,
                <span class="hljs-number">30</span> <span class="hljs-comment">/* Intensity */</span>,
                <span class="hljs-number">100</span> <span class="hljs-comment">/* Maximum Range of Light */</span>
            );
            light.position.set(<span class="hljs-number">30</span>, <span class="hljs-number">30</span>, <span class="hljs-number">30</span>);
            scene.add(light);

            <span class="hljs-comment">// Add Primitive Shapes</span>

            <span class="hljs-comment">// Play Animation</span>
            const RAF <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                requestAnimationFrame(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    <span class="hljs-comment">// Animations</span>

                    <span class="hljs-comment">// Recursively Render</span>
                    renderer.render(scene, camera);
                    RAF();
                });
            };

            RAF();

            <span class="hljs-comment">// Responsiveness</span>
            window.addEventListener(
                <span class="hljs-string">"resize"</span>,
                () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    camera.aspect <span class="hljs-operator">=</span> Cwidth() <span class="hljs-operator">/</span> Cheight();
                    renderer.setSize(Cwidth(), Cheight());
                    camera.updateProjectionMatrix();
                },
                <span class="hljs-literal">false</span>
            );
        }
    };

    init();

<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
</code></pre><p>So just copy-paste this in an html file, and you should be good to follow along.</p>
<p>If you want the details of setting up ThreeJs, checkout <a target="_blank" href="https://heaust.hashnode.dev/threejs-chapter-2">chapter 2</a>.</p>
<h2 id="heading-playing-with-a-cube">Playing with a Cube 🧊</h2>
<p>The snippets discussed here should go below the <code>// Add Primitive Shapes</code> in the boilerplate if you're following along (unless stated otherwise).</p>
<p>Naturally let's start with,</p>
<h3 id="heading-spawning-a-cube">Spawning a Cube 🧊💥</h3>
<p>Reiterating what we discussed in <a target="_blank" href="https://heaust.hashnode.dev/threejs-chapter-1">chapter 1</a>,</p>
<p>A 3D object, called a <strong>Mesh</strong>, is made of <strong>Geometry</strong> (the shape) and <strong>Material</strong> (the texture, which is how the surface looks)</p>
<p>So what we'll do is create a Cube's Geometry,</p>
<pre><code>const geometry <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.BoxBufferGeometry(
    <span class="hljs-number">7</span>, <span class="hljs-comment">/** height */</span>
    <span class="hljs-number">7</span>, <span class="hljs-comment">/** width */</span>
    <span class="hljs-number">7</span>  <span class="hljs-comment">/** length */</span>);
</code></pre><p>a Material,</p>
<pre><code>const material <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial( { color: <span class="hljs-number">0x639af6</span> } );
</code></pre><p>and make a Mesh using those,</p>
<pre><code>const cube <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Mesh( geometry, material );
</code></pre><p>And we just made a 3D cube!</p>
<p>Let's add it to the scene now so we can see it,</p>
<pre><code>scene.<span class="hljs-keyword">add</span>( <span class="hljs-keyword">cube</span> );
</code></pre><p>And we have a cube now!
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638499573355/aJEOXjj9R.png" alt="Top view of Cube" /></p>
<p>Wait... isn't that a rectangle?</p>
<p>Nope it's still a cube, we're just looking at it directly from the top,</p>
<h3 id="heading-transforms">Transforms 🔄</h3>
<p>Let's rotate it a bit so it actually looks 3D,</p>
<pre><code>cube.rotation.x <span class="hljs-operator">=</span> THREE.MathUtils.degToRad(<span class="hljs-number">60</span>);
cube.rotation.y <span class="hljs-operator">=</span> THREE.MathUtils.degToRad(<span class="hljs-number">60</span>);
</code></pre><p>Since ThreeJs uses Radians as units for angles, we had to use <code>THREE.MathUtils.degToRad(/** Angle in Degrees */)</code> to convert 60° into (π/3) Radians</p>
<p>Let's look at our cube now,
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638501616748/dq1Ge8fz8.png" alt="Rotated Cube" /></p>
<p>Looks much better!</p>
<p>As we saw, we can rotate an object simply by changing,</p>
<ul>
<li><code>object.rotation.x</code></li>
<li><code>object.rotation.y</code></li>
<li><code>object.rotation.z</code></li>
</ul>
<p>Similarly we change change its position by changing,</p>
<ul>
<li><code>object.position.x</code></li>
<li><code>object.position.y</code></li>
<li><code>object.position.z</code></li>
</ul>
<p>Or alternatively, we can do it by using functions,</p>
<p>like for rotation,</p>
<ul>
<li><code>object.rotateX(/** Angle in Radian */)</code></li>
<li><code>object.rotateY(/** Angle in Radian */)</code></li>
<li><code>object.rotateZ(/** Angle in Radian */)</code></li>
</ul>
<p>and for position,</p>
<ul>
<li><code>object.translateX(/** Number */)</code></li>
<li><code>object.translateY(/** Number */)</code></li>
<li><code>object.translateZ(/** Number */)</code></li>
</ul>
<p>Now keep in mind that there's some differences between the 2 methods,</p>
<p>Specifically,</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Comparison</td><td><code>object.property.x = /** Value*/;</code></td><td><code>object.changePropertyX(/** Value */);</code></td></tr>
</thead>
<tbody>
<tr>
<td>Axis</td><td>Uses Global Axes</td><td>Uses Object's Axes</td></tr>
<tr>
<td>Transform</td><td>Sets object's property to the value</td><td>Increases Object's property by that value</td></tr>
<tr>
<td>Test Code in <code>// Animations</code> Section</td><td><code>cube.rotation.x += 0.05;</code></td><td><code>cube.rotateX(0.05);</code></td></tr>
<tr>
<td>Result</td><td><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638503582543/gBucUWtD5.gif" alt=" Rotation by object.rotation.x " /></td><td><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638503614806/N8ZEXijLMc.gif" alt="Rotation by object.rotateX( )" /></td></tr>
<tr>
<td>Test Code in <code>// Animations</code> Section</td><td><code>cube.position.x += 0.05;</code></td><td><code>cube.translateX(0.05);</code></td></tr>
<tr>
<td>Result</td><td><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638669869229/Dp12ArRlC.gif" alt="translation by object.position.x += 0.05;" /></td><td><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638669871551/ocmcL8I1p.gif" alt="translation by object.translateX(0.05);" /></td></tr>
</tbody>
</table>
</div><p>So be mindful of what you want to use and when.</p>
<p>We can also change the object's size by changing,</p>
<ul>
<li><code>object.scale.x</code></li>
<li><code>object.scale.y</code></li>
<li><code>object.scale.z</code></li>
</ul>
<p>For example,</p>
<pre><code><span class="hljs-attr">cube.scale.x</span> = <span class="hljs-number">0.5</span><span class="hljs-comment">;</span>
</code></pre><p>halves the cube along its X axis,
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638670486886/zHbVP0btl.png" alt="Cube with half width" /></p>
<p>Now that we can easily throw our cube around, let's see if we can tinker with how it looks,</p>
<h3 id="heading-material">Material</h3>
<p>We can change a range of properties in our object's material to get different results.</p>
<p>The general syntax would be this,</p>
<pre><code>object.material.property <span class="hljs-operator">=</span> <span class="hljs-comment">/** Value */</span>;
</code></pre><p>These are some specific properties we can change,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638674844272/yVy1pDBbl.gif" alt="Changing cube materials" /></p>
<p>here are some examples of how we'd go about changing these,</p>
<pre><code>cube.material.color <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">"#0f9d58"</span>);
cube.material.metalness <span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.2</span>; <span class="hljs-comment">/** 0 to 1*/</span>
cube.material.roughness <span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.5</span>; <span class="hljs-comment">/** 0 to 1 */</span>
cube.material.transparent <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
cube.material.opacity <span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.3</span>; <span class="hljs-comment">/** 0 to 1 */</span>
cube.material.wireframe <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
</code></pre><p>ThreeJs has an awesome documentation that explains every property and even gives you a playground to tinker with every property with a simple GUI,</p>
<p>Definitely check that out <a target="_blank" href="https://threejs.org/docs/#api/en/materials/MeshStandardMaterial">here</a>.</p>
<p>So we can throw our Cube around and style it in interesting ways,</p>
<p>Still, if it isn't interactive, it might as well be an animation right?</p>
<p>So let's really step into the 3rd dimension and learn to move around!</p>
<h3 id="heading-orbital-controls">Orbital Controls</h3>
<p>ThreeJs provides us a very helpful module to make the camera orbit around a target with click and drag.</p>
<p>We can import it like this,</p>
<pre><code><span class="hljs-keyword">import</span> { <span class="hljs-title">OrbitControls</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"https://unpkg.com/three/examples/jsm/controls/OrbitControls"</span>;
</code></pre><p>or if you're using webpack it'd be,</p>
<pre><code><span class="hljs-keyword">import</span> { <span class="hljs-title">OrbitControls</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"three/examples/jsm/controls/OrbitControls"</span>;
</code></pre><p>Now to actually use it, we need to initialize and update it,</p>
<p>So let's do that after the camera setup, right before <code>// Adding Stuff to Our World</code>,</p>
<pre><code>const controls <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> OrbitControls( camera, renderer.domElement );
controls.update();
</code></pre><p>And we're done!
We can,</p>
<ul>
<li><strong>Click and Drag</strong> to Orbit</li>
<li><strong>Shift + Click and Drag</strong> to Move</li>
<li><strong>Scroll</strong> to Zoom in and out.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638676733489/NT6BHHS2R.gif" alt="Orbital Controls" /></p>
<h2 id="heading-sky-full-of-cubes">Sky Full of Cubes 🌌</h2>
<p>Okay we can do a ton of stuff with a cube now.... what about TWO cubes though, or even THREE?</p>
<p>What if we went even FURTHER?</p>
<p>First let's make a <a target="_blank" href="https://stackoverflow.com/questions/4959975/generate-random-number-between-two-numbers-in-javascript">random function</a>,</p>
<pre><code>const randomIntFromInterval <span class="hljs-operator">=</span> (min, max) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { <span class="hljs-comment">// min and max included </span>
  <span class="hljs-keyword">return</span> Math.floor(Math.random() <span class="hljs-operator">*</span> (max <span class="hljs-operator">-</span> min <span class="hljs-operator">+</span> <span class="hljs-number">1</span>) <span class="hljs-operator">+</span> min)
}
</code></pre><p>and do what we did when we first spawned a cube, except this time with random values,</p>
<pre><code>const spawnRandomCube <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    <span class="hljs-comment">// Make Shape</span>
    const size <span class="hljs-operator">=</span> randomIntFromInterval(<span class="hljs-number">2</span>, <span class="hljs-number">7</span>);
    const geometry <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.BoxBufferGeometry(
        size <span class="hljs-comment">/** height */</span>,
        size <span class="hljs-comment">/** width */</span>,
        size <span class="hljs-comment">/** length */</span>);
    <span class="hljs-comment">// Make Texture</span>
    const material <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({ color: <span class="hljs-number">0x639af6</span> });
    <span class="hljs-comment">// Make Cube</span>
    const cube <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Mesh(geometry, material);
    <span class="hljs-comment">// Random Position</span>
    cube.position.set(
        randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>),
        randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>),
        randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>)
    );
    <span class="hljs-comment">// Random Angle</span>
    cube.rotation.set(
        THREE.MathUtils.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>)),
        THREE.MathUtils.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>)),
        THREE.MathUtils.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>))
    )
    <span class="hljs-comment">// Add to Scene</span>
    scene.add(cube);
}
</code></pre><p>now Let's repeat it 10000 times!!</p>
<pre><code><span class="hljs-keyword">for</span> (let i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> <span class="hljs-number">10000</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) spawnRandomCube();
</code></pre><p>This is what we get,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638683270996/sX-EXwxwI.gif" alt="A lot of cubes floating in empty space" /></p>
<p>Something feels off though,</p>
<p>Let's Check on how much time takes for us to do this,</p>
<pre><code>console.time(<span class="hljs-string">"Spawning Cubes"</span>);
<span class="hljs-keyword">for</span> (let i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> <span class="hljs-number">10000</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) spawnRandomCube();
console.timeEnd(<span class="hljs-string">"Spawning Cubes"</span>);
</code></pre><p> The time taken should be logged in the console,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638683663965/4xIBy8NSM.png" alt="took 247 milliseconds to complete" /></p>
<p>Woah... that's a lot...</p>
<p>It isn't really ideal that this task alone takes a fourth of a second to complete,</p>
<p>Can we do something to speed the process up? Any glaring optimizations we skipped over?</p>
<p>The problem is, we're making the same Material and Geometry over and over each time we spawn a cube,</p>
<p>since it's the same exact Geometry and Material, we don't necessarily need to do that,</p>
<p>So let's just make it once and use it anytime we need it next, like this,</p>
<pre><code><span class="hljs-comment">// Make Texture</span>
const material <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({ color: <span class="hljs-number">0x639af6</span> });
<span class="hljs-comment">// Make Shape</span>
const geometry <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.BoxBufferGeometry(
    <span class="hljs-number">1</span> <span class="hljs-comment">/** height */</span>,
    <span class="hljs-number">1</span> <span class="hljs-comment">/** width */</span>,
    <span class="hljs-number">1</span> <span class="hljs-comment">/** length */</span>);

const spawnRandomCube <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    <span class="hljs-comment">// Make Cube</span>
    const cube <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Mesh(geometry, material);
    const scale <span class="hljs-operator">=</span> randomIntFromInterval(<span class="hljs-number">2</span>, <span class="hljs-number">7</span>);
    cube.scale.set(scale, scale, scale)
    <span class="hljs-comment">// Random Position</span>
    cube.position.set(
        randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>),
        randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>),
        randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>)
    );
    <span class="hljs-comment">// Random Angle</span>
    cube.rotation.set(
        THREE.MathUtils.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>)),
        THREE.MathUtils.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>)),
        THREE.MathUtils.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>))
    )
    scene.add(cube);
}
</code></pre><p>And just like that, we achieve a much better result!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638683107280/VB32si_S4.png" alt="took 49 milliseconds to complete" /></p>
<p>This process of using the same Geometry and / or Material to create many similar objects is called <strong>Instancing</strong>.</p>
<p>This was just an example to understand how instancing works though,</p>
<p>ThreeJs Provides a way to use Instancing that's much more optimized than our little setup here,</p>
<p>We can utilize it like so,</p>
<pre><code>console.time(<span class="hljs-string">"Spawning Cubes"</span>);
<span class="hljs-comment">// Make Texture</span>
const material <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({ color: <span class="hljs-number">0x639af6</span> });
<span class="hljs-comment">// Make Shape</span>
const geometry <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.BoxBufferGeometry(
    <span class="hljs-number">1</span> <span class="hljs-comment">/** height */</span>,
    <span class="hljs-number">1</span> <span class="hljs-comment">/** width */</span>,
    <span class="hljs-number">1</span> <span class="hljs-comment">/** length */</span>);
<span class="hljs-comment">// Creating 10000 Instanced Cubes</span>
const InstancedCubes <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.InstancedMesh(geometry, material, <span class="hljs-number">10000</span>);
<span class="hljs-comment">// Creating helper 3D object to get matrix from</span>
let tmp <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Object3D();
<span class="hljs-comment">// Iterating Over All the Cubes that are Created</span>
<span class="hljs-keyword">for</span> (let i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> InstancedCubes.count; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
    <span class="hljs-comment">/** Transforming the helper 3D Object */</span>
    const scale <span class="hljs-operator">=</span> randomIntFromInterval(<span class="hljs-number">2</span>, <span class="hljs-number">7</span>);
    tmp.scale.set(scale, scale, scale);
    <span class="hljs-comment">// Random Position</span>
    tmp.position.set(
        randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>),
        randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>),
        randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>)
    );
    <span class="hljs-comment">// Random Angle</span>
    tmp.rotation.set(
        THREE.MathUtils.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>)),
        THREE.MathUtils.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>)),
        THREE.MathUtils.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>))
    );
    <span class="hljs-comment">// Updating the helper 3D Object's Matrix</span>
    tmp.updateMatrix();
    <span class="hljs-comment">// Setting the Cube's Matrix to the same as our helper</span>
    InstancedCubes.setMatrixAt(i, tmp.matrix);
};
<span class="hljs-comment">// Telling ThreeJs we just changed the matrices of the Cubes</span>
<span class="hljs-comment">// So it can update them sooner rather than later</span>
InstancedCubes.instanceMatrix.needsUpdate <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
<span class="hljs-comment">// Adding the Instanced Cubes to the Scene</span>
scene.add(InstancedCubes);
console.timeEnd(<span class="hljs-string">"Spawning Cubes"</span>);
</code></pre><p>Every 3D Object has a matrix that stores its transforms, we can't directly transform Instanced Cubes, but we Can change their matrix, so that's what we did.</p>
<p>We got to know what the matrix was by creating an empty 3D object and transforming it as we wanted and copying its matrix.</p>
<p>And we get the same result as we did previously (as seen from a different angle this time),</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638686367935/eTV_5kFJL.gif" alt="A lot of Cubes in the Void" /></p>
<p>with less than <strong>5%</strong> of our original time,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638685795947/e8ggA7meZ.png" alt="took 12 milliseconds" /></p>
<p><strong>Instancing</strong> is a method that saves both <strong>Memory</strong> and <strong>Time</strong>.</p>
<p>If Instancing can be utilized in your use case, there's no reason to not use it.</p>
<p>And that's it! We've reached the end! Hope you had fun : )</p>
<p>Let's go over what we learned,</p>
<h2 id="heading-summary">Summary</h2>
<p>So in this Article we learned,</p>
<ul>
<li>How to Create ThreeJs Primitive Shapes</li>
<li>How to Make and Style Materials</li>
<li>How to Make and Transform a 3D Object</li>
<li>How to make ThreeJs interactive with Orbital Controls</li>
<li>How to create a scattering of similar Objects</li>
<li>What is Instancing, Why it's useful and How to use it</li>
</ul>
<p>If we repeat what we did with Spheres, we'll get something like this,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638690422370/UDY-VdYUV.png" alt="A Scattering of Balls" /></p>
<p>To achieve what we did above, just copy paste the following into your HTML,</p>
<pre><code><span class="hljs-operator">&lt;</span>div
style<span class="hljs-operator">=</span><span class="hljs-string">"position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;"</span>
class<span class="hljs-operator">=</span><span class="hljs-string">"threeD"</span>
<span class="hljs-operator">&gt;</span><span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
<span class="hljs-operator">&lt;</span>script async src<span class="hljs-operator">=</span><span class="hljs-string">"https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
<span class="hljs-operator">&lt;</span>script <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"importmap"</span><span class="hljs-operator">&gt;</span>
{
    <span class="hljs-string">"imports"</span>: {
        <span class="hljs-string">"three"</span>: <span class="hljs-string">"https://unpkg.com/three/build/three.module.js"</span>
    }
}
<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
<span class="hljs-operator">&lt;</span>script <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"module"</span><span class="hljs-operator">&gt;</span>
    <span class="hljs-keyword">import</span> <span class="hljs-operator">*</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">THREE</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"three"</span>;
    <span class="hljs-keyword">import</span> { <span class="hljs-title">OrbitControls</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"https://unpkg.com/three/examples/jsm/controls/OrbitControls"</span>;

    const init <span class="hljs-operator">=</span> (
        targetClass <span class="hljs-operator">=</span> <span class="hljs-string">"threeD"</span>
    ) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
        const target <span class="hljs-operator">=</span> document.getElementsByClassName(targetClass);
        <span class="hljs-keyword">if</span> (target) {
            <span class="hljs-comment">// Renderer and Scene Setup</span>
            const renderer <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.WebGLRenderer({ alpha: <span class="hljs-literal">true</span> });
            const Cwidth <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                <span class="hljs-keyword">return</span> document.getElementsByClassName(targetClass)[<span class="hljs-number">0</span>].clientWidth;
            };
            const Cheight <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                <span class="hljs-keyword">return</span> document.getElementsByClassName(targetClass)[<span class="hljs-number">0</span>].clientHeight;
            };
            renderer.shadowMap.enabled <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(Cwidth(), Cheight());
            renderer.physicallyCorrectLights <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
            renderer.domElement.setAttribute(<span class="hljs-string">"id"</span>, <span class="hljs-string">"three-canvas"</span>);

            target[<span class="hljs-number">0</span>].appendChild(renderer.domElement);
            const scene <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Scene();
            const ambientLight <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">2</span>);
            scene.add(ambientLight);

            <span class="hljs-comment">// Camera Setup</span>
            const aspect <span class="hljs-operator">=</span> Cwidth() <span class="hljs-operator">/</span> Cheight();
            const fov <span class="hljs-operator">=</span> <span class="hljs-number">60</span>;
            const near <span class="hljs-operator">=</span> <span class="hljs-number">1.0</span>;
            const far <span class="hljs-operator">=</span> <span class="hljs-number">200.0</span>;
            const camera <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(fov, aspect, near, far);
            camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">30</span>, <span class="hljs-number">0</span>);
            camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

            <span class="hljs-comment">// Setting Up Orbital Controls </span>
            const controls <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> OrbitControls(camera, renderer.domElement);
            controls.update();

            <span class="hljs-comment">// Adding Stuff to Our World  </span>
            <span class="hljs-comment">// Add Point Light</span>
            const light <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.PointLight(
                <span class="hljs-number">0xffffff</span> <span class="hljs-comment">/* Color */</span>,
                <span class="hljs-number">80</span> <span class="hljs-comment">/* Intensity */</span>,
                <span class="hljs-number">100</span> <span class="hljs-comment">/* Maximum Range of Light */</span>
            );
            light.position.set(<span class="hljs-number">30</span>, <span class="hljs-number">30</span>, <span class="hljs-number">30</span>);
            scene.add(light);

            <span class="hljs-comment">// Add Primitive Shapes</span>
            const randomIntFromInterval <span class="hljs-operator">=</span> (min, max) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { <span class="hljs-comment">// min and max included </span>
                <span class="hljs-keyword">return</span> Math.floor(Math.random() <span class="hljs-operator">*</span> (max <span class="hljs-operator">-</span> min <span class="hljs-operator">+</span> <span class="hljs-number">1</span>) <span class="hljs-operator">+</span> min)
            }

            console.time(<span class="hljs-string">"Spawning Spheres"</span>);
            <span class="hljs-comment">// Make Texture</span>
            const material <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
                color: <span class="hljs-number">0x639af6</span>,
                metalness: <span class="hljs-number">0</span><span class="hljs-number">.2</span>,
                roughness: <span class="hljs-number">0</span><span class="hljs-number">.8</span>,
            });
            <span class="hljs-comment">// Make Shape</span>
            const geometry <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.SphereBufferGeometry(
                <span class="hljs-number">1</span> <span class="hljs-comment">/** Radius */</span>,
            );
            <span class="hljs-comment">// Creating 500 Instanced Spheres</span>
            const InstancedSpheres <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.InstancedMesh(geometry, material, <span class="hljs-number">500</span>);
            <span class="hljs-comment">// Creating helper 3D object to get matrix from</span>
            let tmp <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Object3D();
            <span class="hljs-comment">// Iterating Over All the Spheres that are Created</span>
            <span class="hljs-keyword">for</span> (let i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> InstancedSpheres.count; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
                <span class="hljs-comment">/** Transforming the helper 3D Object */</span>
                const scale <span class="hljs-operator">=</span> randomIntFromInterval(<span class="hljs-number">2</span>, <span class="hljs-number">7</span>);
                tmp.scale.set(scale, scale, scale);
                <span class="hljs-comment">// Random Position</span>
                tmp.position.set(
                    randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>),
                    randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>),
                    randomIntFromInterval(<span class="hljs-number">-80</span>, <span class="hljs-number">80</span>)
                );
                <span class="hljs-comment">// Random Angle</span>
                tmp.rotation.set(
                    THREE.MathUtils.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>)),
                    THREE.MathUtils.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>)),
                    THREE.MathUtils.degToRad(randomIntFromInterval(<span class="hljs-number">0</span>, <span class="hljs-number">90</span>))
                );
                <span class="hljs-comment">// Updating the helper 3D Object's Matrix</span>
                tmp.updateMatrix();
                <span class="hljs-comment">// Setting the Sphere's Matrix to the same as our helper</span>
                InstancedSpheres.setMatrixAt(i, tmp.matrix);
            };
            <span class="hljs-comment">// Telling ThreeJs we just changed the matrices of the Spheres</span>
            <span class="hljs-comment">// So it can update them sooner rather than later</span>
            InstancedSpheres.instanceMatrix.needsUpdate <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
            <span class="hljs-comment">// Adding the Instanced Spheres to the Scene</span>
            scene.add(InstancedSpheres);
            console.timeEnd(<span class="hljs-string">"Spawning Spheres"</span>);

            <span class="hljs-comment">// Play Animation</span>
            const RAF <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                requestAnimationFrame(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    <span class="hljs-comment">// Animations</span>

                    <span class="hljs-comment">// Recursively Render</span>
                    renderer.render(scene, camera);
                    RAF();
                });
            };

            RAF();

            <span class="hljs-comment">// Responsiveness</span>
            window.addEventListener(
                <span class="hljs-string">"resize"</span>,
                () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                    camera.aspect <span class="hljs-operator">=</span> Cwidth() <span class="hljs-operator">/</span> Cheight();
                    renderer.setSize(Cwidth(), Cheight());
                    camera.updateProjectionMatrix();
                },
                <span class="hljs-literal">false</span>
            );
        }
    };

    init();
<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
</code></pre><h2 id="heading-conclusion">Conclusion</h2>
<p>We've only worked with Cubes here, but you can go <a target="_blank" href="https://threejs.org/docs/?q=geometry">here</a> and see a list of all the Geometries (Shapes) ThreeJs has to offer and the details on how to use them as well,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638689478213/y74mnsNHS.png" alt="ThreeJs Geometries list sidebar" /></p>
<p>If you're using, them though, be sure to use <strong><a target="_blank" href="https://threejs.org/docs/?q=geomet#api/en/core/BufferGeometry">Buffer Geometries</a></strong> instead of normal ones, Buffer Geometries are more optimized and you can only use instancing with Buffer Geometries.</p>
<p>To do that you just have to add "Buffer" between the Geometry names,</p>
<p>So for example, use <strong>ConeBufferGeometry</strong> instead of <strong>ConeGeometry</strong>, <strong>BoxBufferGeometry</strong> instead of <strong>BoxGeometry</strong> and so on.</p>
<hr />
<p>Thank you so much for reading!</p>
<p>Our ThreeJs adventure continues in the next article, where we'll take our little setup here, and see how we can select individual spheres and make them light up like little bulbs!</p>
<p>So see you in <a target="_blank" href="https://heaust.hashnode.dev/threejs-chapter-4"><strong>ThreeJs: How to Click</strong></a>, I hope you learned something : )</p>
<p>[If you need to contact me, you can comment down below, or email me at heaust.azure@gmail.com]</p>
]]></content:encoded></item><item><title><![CDATA[Chapter 1: A New Beginning | Heaust's POV]]></title><description><![CDATA[" God is Dead " - Friedrich Nietzsche
.
.
.
I looked down at my IDX (eXperience and ID).
____________
Name: Heaust Azure
Scriptor Rank: 14 million
Provider Type: Unique
Chronos Density: Very Low
Magic Aptitude Estimation: Average
Provider Pay Grade: ...]]></description><link>https://blog.heaust.org/eow-chapter-1</link><guid isPermaLink="true">https://blog.heaust.org/eow-chapter-1</guid><dc:creator><![CDATA[Heaust]]></dc:creator><pubDate>Sat, 27 Nov 2021 17:23:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1674382687816/9c089ce1-bdaa-43d3-aa39-3ad6a1ee340c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong><em>" God is Dead " - Friedrich Nietzsche</em></strong></p>
<p>.</p>
<p>.</p>
<p>.</p>
<p>I looked down at my IDX (eXperience and ID).</p>
<h4 id="heading-xf9cx1xfxf9cx1xfxf9cx1xfxf9cx1xf">____________</h4>
<h4 id="heading-name-heaust-azure">Name: Heaust Azure</h4>
<h4 id="heading-scriptor-rank-14-million">Scriptor Rank: 14 million</h4>
<h4 id="heading-provider-type-unique">Provider Type: Unique</h4>
<h4 id="heading-chronos-density-very-low">Chronos Density: Very Low</h4>
<h4 id="heading-magic-aptitude-estimation-average">Magic Aptitude Estimation: Average</h4>
<h4 id="heading-provider-pay-grade-minimum">Provider Pay Grade: Minimum</h4>
<h4 id="heading-xf9cx1xfxf9cx1xfxf9cx1xfxf9cx1xf-1">____________</h4>
<p>My Scriptor Rank, the measure of my programming ability, went up by 2 million since the last test.</p>
<p>A scriptor rank of 14 million is generous since almost all of the world competes, more importantly it would let me take the Magic Conversion Therapy despite my low provider pay grade, so I was in a pleasant mood.</p>
<p>Even though, I had a provider type that was above even "Rare", the highest I've ever seen or heard anyone get, that only meant my heart produced all types of chronos but since it didn't produce very much of it and stack that with the difficulty of separating the chronos from each other, It wasn't much cost effective to say at the very least, resulting my provider pay to be "Minimum" which was minimum wage...</p>
<p>The Magic Aptitude Estimation is a new addition, "Average" didn't bother me much, everyone starts somewhere right?</p>
<p>Magic Conversion Therapy (MCT) claims to make the entire nervous system like a pseudo chrono-crystal, so theoretically those who had gone through it can control chronos by just thinking!</p>
<p>The concepts behind it are so complex that one person can't hope to wrap their minds around all the technicalities  given their whole life. That said the Adventurers (those who successfully undergo MCT) are expected to know how things work and they need it to be practically digestible since most of their time will go to training.</p>
<p>This was solved by the "Magic Theory", the use of chrono-control by Adventurers is called Magic and the entire theory is just a system to navigate the concepts without getting into technicalities. It is widely popular and only extremely passionate research graduates go into the actual technicalities.</p>
<p>My parents were against this decision since the test has a 10% absolute mortality rate, "absolute" meaning they couldn't be revived with chrono-tech. I'm independent now though, so it is my decision and I know the risks but I simply can't sit on the sidelines, perhaps that's why they're called Adventurers.</p>
<p>As I was lost in thought my IDX buzzed</p>
<h4 id="heading-xf9cx1xfxf9cx1xfxf9cx1xfxf9cx1xf-2">____________</h4>
<h4 id="heading-heaust-azure">Heaust Azure,</h4>
<h4 id="heading-please-report-to-your-assigned-mct-venue">Please report to your assigned MCT venue,</h4>
<h4 id="heading-failure-to-report-in-2000-minutes-will-result-in-your-disqualification-for-this-round">Failure to report in 20:00 minutes will result in your disqualification for this round.</h4>
<h4 id="heading-do-not-reply">Do Not Reply.</h4>
<h4 id="heading-regards">Regards,</h4>
<h4 id="heading-chrono-x">Chrono-X</h4>
<h4 id="heading-xf9cx1xfxf9cx1xfxf9cx1xfxf9cx1xf-3">____________</h4>
<p>The timer started counting down, Finally, I took out the state of the art specialised chrono-crystal I was gifted for my birthday, It's called a transfer-crystal, it can teleport people to the very few places in the world that have a transfer-receiver installed.</p>
<p>The MCT venue happens to be one of them.</p>
<p>I activated my crystal and the view around me changed, my small apartment room was now gone.</p>
<p>I was standing in a wide circular hall, looking up I found the ceiling to be a dome, the architecture was roman, as was characteristic of important government buildings.</p>
<p>Around me were 3.. 6.. 8.. 12.. 16.. 17 people, most of them clearly extremely well off and had a behavior that screamed nobility, the people with the top pay grades, I was probably the only one there that had been accepted because of their scriptor rank and not their pay grade.</p>
<p>I walked towards the central information desk and stood before a lady that looked to be in her 20s. She had the uniform and hat characteristic of a help desk server, her badge read "Emiris Parson"</p>
<p>Before I had the chance to greet her she held out her hand, "Your IDX please?", she said smiling.</p>
<p>"Oh sure here you go", I handed her my IDX.</p>
<p>She ran it through the verification sensor and hesitated for a short moment,</p>
<p>"You can still opt out if you want.", her tone was nervous, I wondered what she saw on her screen.</p>
<p>"I would rather continue if that's okay", I dismissed it to be routine verification, as I said that I saw her smile turn almost anxious.</p>
<p>"No really you should reconsi-", she halted mid sentence as she peeked over my shoulder</p>
<p>I turned my head and didn't see anything out of the ordinary, just people making their way to the desk.</p>
<p>"Door Number 16, the last one clockwise", she directed coldly, she wasn't smiling anymore.</p>
<p>I didn't think much of it, there was a lot of discrimination based on pay grade so I thought it was that.</p>
<p>I was a fool.</p>
<p>I should've heeded her warning.</p>
<p>I should've got out of that place.</p>
<p>I should've ran till my feet bled and then should've kept running.</p>
<p>But what I did next was the worst possible thing I could've done.</p>
<p>I took my IDX back and walked to door number 16.</p>
<p>The door scanned my IDX and I went through.</p>
<hr />
<p><a target="_blank" href="https://heaust.hashnode.dev/eow-toc">Prologue</a> &lt;- <a target="_blank" href="https://heaust.hashnode.dev/eow-toc">Table of Contents</a> -&gt; <a target="_blank" href="https://blog.heaust.org/eow-chapter-2">Next Chapter</a></p>
<p>[If you need to contact me, you can comment below or email me at heaust.azure@gmail.com]</p>
]]></content:encoded></item><item><title><![CDATA[Prologue]]></title><description><![CDATA[All was silent, except for the sound of two beating hearts...
All was dark, except for a certain faint emerald glow...
At the edge of the world an agonized howl started it's march with a brilliant white light followed by what one could, if they so he...]]></description><link>https://blog.heaust.org/eow-prologue</link><guid isPermaLink="true">https://blog.heaust.org/eow-prologue</guid><dc:creator><![CDATA[Heaust]]></dc:creator><pubDate>Sat, 27 Nov 2021 17:23:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1674382789136/0e14977c-80e4-443f-a540-8e7f712b7ead.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>All was silent, except for the sound of two beating hearts...</p>
<p>All was dark, except for a certain faint emerald glow...</p>
<p>At the edge of the world an agonized howl started it's march with a brilliant white light followed by what one could, if they so heard it, make out to be the faint sound of a sharply drawn breath.</p>
<p>And then</p>
<p>All was silent.</p>
<p>All was dark.</p>
<p>This is not a story of justice, It may be a story of vengeance, it is certainly not one of redemption, it is a story of a festering world and the catastrophe that follows as consequence. It is a story of humanity's forgetfulness and it's longing for the clarity... of disaster.</p>
<p>.</p>
<p>.</p>
<p>.</p>
<p>It's the year 2077,</p>
<p>12 years ago the discovery of the magic particle chronos, a particle that was thought to have the ability to manipulate time, revolutionised technology, it was soon found out that the particles came in varieties much like the atoms and much like the atoms they had an entire chemistry of interaction but unlike them there were only 16 of the chronos.</p>
<p>The chronos was everywhere, it didn't interact with matter directly, but it did so significantly, it was especially concentrated in living beings, the human heart was found to have another major function, to produce and regulate chronos, lungs could absorb or get rid of chronos.</p>
<p>Some martial arts enthusiasts likened it to "chi", and as time, whatever that may be, would tell... they weren't far off.</p>
<p>A specific crystal, later named to be chrono-crystal, showed promise to control and co-ordinate the chronos, provided it be appropriately programmed. The void of it's lattice stored chronos and the vertices regulated its reactions.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638031704517/3Q0VaG7uw.png" alt="Chronos Particles" /></p>
<p>And so the horizons of the sciences expanded, Chemistry, Physics, Geology, Programming, technology, even the modern human society would undergo drastic changes</p>
<p>The chronos had it's applications everywhere, chrono-crystals started being mass manufactured by the Industry, all societal power shifted to the Corporations who had made the right decisions.</p>
<p>Where did the chronos stored inside the crystals come from?</p>
<p>The human heart is an excellent producer of the chronos, and the lungs and nerve endings can help rid the chronos...</p>
<p>And so people were mass employed at slightly over minimum wage to be strapped to an extractor and have a substance sucked out their body that they weren't aware so far even did anything functional.</p>
<p>There were those who didn't agree, but most did.</p>
<p>There was also the service of programming the crystals...</p>
<p>Schools doubled down on education on the chronos, working the students day and night, utilizing all the recent chrono-tech to try and remove the adverse affects of sleep deprivation.</p>
<p>Chrono-tech was expensive, and most people needed to be chronos provider on top of a regular job to be able to afford it. The people who could provide more chronos were payed more, those that could provided the rare types were payed handsomely.</p>
<p>Eventually the ideals of the corporation and the governments clashed...</p>
<p>Chrono-X, the largest chrono-crystal manufacturer, declared war.</p>
<p>Against the latest chrono-weaponry the world didn't stand a chance.</p>
<p>Nations surrendered when even nuclear weapons were frozen.</p>
<p>The world was now united under a global government, it was of course a people's government, since the sub-organisation of Chrono-X that made the highest profit controlled most of the power.</p>
<p>So it still barely resembled a democracy.</p>
<p>Just when the dust was beginning to settle down,</p>
<p>The world was yet again shaken to it's core...</p>
<hr />
<p><a target="_blank" href="https://heaust.hashnode.dev/eow-toc">Table of Contents</a> -&gt; <a target="_blank" href="https://heaust.hashnode.dev/eow-chapter-1">Next Chapter</a></p>
<p>[If you need to contact me, you can comment below or email me at heaust.azure@gmail.com]</p>
]]></content:encoded></item><item><title><![CDATA[Table Of Contents + Author's Note]]></title><description><![CDATA[Hi, Everyone I'm going to start a Sci-Fi Novel Series because I always wanted to XD
This is a Science Fiction and High Fantasy Novel that takes inspiration from the MMORPG genre of games and Software Development and wraps it all into a neat little pa...]]></description><link>https://blog.heaust.org/eow-toc</link><guid isPermaLink="true">https://blog.heaust.org/eow-toc</guid><dc:creator><![CDATA[Heaust]]></dc:creator><pubDate>Sat, 27 Nov 2021 17:22:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1674382763027/91eb4a33-ec2b-43e7-ba78-630cac456c1d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi, Everyone I'm going to start a Sci-Fi Novel Series because I always wanted to XD</p>
<p>This is a Science Fiction and High Fantasy Novel that takes inspiration from the MMORPG genre of games and Software Development and wraps it all into a neat little package. </p>
<p>So Cover Art's Not Ready Yet, I'll eventually make one, but for now please bear with no cover :P</p>
<p>First couple chapters I'd already written once on watt-pad, but since I forgot the password of that account and I'm gonna write software articles here anyway,</p>
<p>It's more convenient for me to put everything here.</p>
<p>Please provide feedback wherever you can ^^
The hard details of future chapters isn't set in stone yet, only basic plot and world-building, so feel free to suggest ways I can improve on those too and I'll implement them : )</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a target="_blank" href="https://heaust.hashnode.dev/eow-prologue">Prologue</a></li>
<li><a target="_blank" href="https://heaust.hashnode.dev/eow-chapter-1">Chapter 1</a></li>
<li><a target="_blank" href="https://heaust.hashnode.dev/eow-chapter-2">Chapter 2</a></li>
<li><a target="_blank" href="https://heaust.hashnode.dev/eow-chapter-3">Chapter 3</a></li>
</ul>
<p>[If you need to contact me, you can comment below or email me at heaust.azure@gmail.com]</p>
]]></content:encoded></item><item><title><![CDATA[Make a 3D Spinning Logo with ThreeJs]]></title><description><![CDATA[Introduction 🫱🏽‍🫲🏿
This article will acquaint you with some ThreeJs Boilerplate, Basic Animation, and Importing Models.
If you don't want a rundown and just want the code, head to the end.
Let's get started!
Getting ThreeJs ⬇️
First we'll need to...]]></description><link>https://blog.heaust.org/threejs-chapter-2</link><guid isPermaLink="true">https://blog.heaust.org/threejs-chapter-2</guid><category><![CDATA[3d]]></category><category><![CDATA[ThreeJS]]></category><category><![CDATA[frontend]]></category><category><![CDATA[development]]></category><category><![CDATA[basics]]></category><dc:creator><![CDATA[Heaust]]></dc:creator><pubDate>Tue, 09 Nov 2021 15:00:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1636469917571/U4IKcCMSvC.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction 🫱🏽‍🫲🏿</h2>
<p>This article will acquaint you with some ThreeJs Boilerplate, Basic Animation, and Importing Models.</p>
<p>If you don't want a rundown and just want the code, head to the end.</p>
<p>Let's get started!</p>
<h2 id="heading-getting-threejs">Getting ThreeJs ⬇️</h2>
<p>First we'll need to actually get the ThreeJs Package so we can work with it.</p>
<p>If you're working with webpack, you can just</p>
<pre><code>yarn <span class="hljs-keyword">add</span> three
</code></pre><p>OR</p>
<pre><code>npm <span class="hljs-keyword">install</span> <span class="hljs-comment">--save three</span>
</code></pre><p>and then it's a simple matter of</p>
<pre><code><span class="hljs-keyword">import</span> <span class="hljs-operator">*</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">THREE</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'three'</span>;
</code></pre><p>You can also do this if you're using react or any of its derivative lessons, but it'd be more appropriate to use <a target="_blank" href="https://github.com/pmndrs/react-three-fiber"><strong>React-Three-Fiber</strong></a> for that, tutorials for that coming soon as well : )</p>
<p>If you're using vanilla JS you can do</p>
<pre><code><span class="hljs-operator">&lt;</span>script async src<span class="hljs-operator">=</span><span class="hljs-string">"https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
<span class="hljs-operator">&lt;</span>script <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"importmap"</span><span class="hljs-operator">&gt;</span>
{
    <span class="hljs-string">"imports"</span>: {
        <span class="hljs-string">"three"</span>: <span class="hljs-string">"https://unpkg.com/three/build/three.module.js"</span>
    }
}
<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
<span class="hljs-operator">&lt;</span>script <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"module"</span><span class="hljs-operator">&gt;</span>
    <span class="hljs-keyword">import</span> <span class="hljs-operator">*</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">THREE</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'three'</span>;

    <span class="hljs-comment">/** CODE HERE */</span>
<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
</code></pre><h2 id="heading-checking-for-webgl">Checking for WebGL ✔️</h2>
<p>For ThreeJs to Render (with WebGL Renderer), the browser needs to have a WebGL implementation,</p>
<p>You can check if it does or not by,</p>
<pre><code><span class="hljs-comment">/* If using Webpack */</span>
<span class="hljs-keyword">import</span> { <span class="hljs-title">WEBGL</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"three/examples/jsm/WebGL.js"</span>;
<span class="hljs-comment">/* If using vanilla */</span>
<span class="hljs-keyword">import</span> { <span class="hljs-title">WEBGL</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"https://cdn.skypack.dev/three/examples/jsm/WebGL.js"</span>;

<span class="hljs-keyword">if</span> ( WEBGL.isWebGLAvailable() ) {
<span class="hljs-comment">/* It does, Go ahead */</span>
} <span class="hljs-keyword">else</span> {
    const warning <span class="hljs-operator">=</span> WEBGL.getWebGLErrorMessage();
    document.getElementById( <span class="hljs-string">'container'</span> ).appendChild( warning );
}
</code></pre><p>Most browsers already have WebGL compatibility though so usually you wouldn't need to do this.</p>
<h2 id="heading-renderer-and-scene-setup">Renderer and Scene Setup 🌎</h2>
<p>Let's Create the Renderer First</p>
<pre><code>const renderer <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.WebGLRenderer({ alpha: <span class="hljs-literal">true</span> });
</code></pre><p>The <code>{ alpha: true }</code> makes the empty parts of the scene transparent instead of black.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636434914101/E1A3D-dO4.png" alt="alpha true / false illustration" /></p>
<p>Do we want Shadow Maps? Nope. It's a very basic scene and we don't like shadows anyway.</p>
<p><em>(If you don't know what Shadow Maps are, we discusses them in <a target="_blank" href="https://heaust.hashnode.dev/threejs-chapter-1">Chapter 1</a>, in Lighting and Shadows.)</em></p>
<pre><code><span class="hljs-attr">renderer.shadowMap.enabled</span> = <span class="hljs-literal">false</span><span class="hljs-comment">;</span>
</code></pre><p>Now we set pixel ratio to prevent blurring for HiDPI devices,</p>
<pre><code>renderer.setPixelRatio(window.devicePixelRatio);
</code></pre><p>Now we set the size of our canvas,</p>
<pre><code>renderer.setSize(window.innerWidth, window.innerHeight);
</code></pre><p>Let's go with lights that mimic the real world lights,</p>
<pre><code><span class="hljs-attr">renderer.physicallyCorrectLights</span> = <span class="hljs-literal">true</span><span class="hljs-comment">;</span>
</code></pre><p>Now let's give our canvas element an id for easy future interaction,</p>
<pre><code>renderer.domElement.setAttribute(<span class="hljs-string">"id"</span>, <span class="hljs-string">"three-canvas"</span>);
</code></pre><p>We need append the Canvas to the body <em>(or any HTML element you desire, point is we need to have the canvas in our actual HTML)</em> to actually see it,</p>
<pre><code>document.body.appendChild(renderer.domElement);
</code></pre><p>Time to set the scene now! Let's create our 3D world,</p>
<pre><code>const scene <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Scene();
</code></pre><p>Let's make an ambient light,</p>
<pre><code>const ambientLight <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.AmbientLight(
  <span class="hljs-number">0xffffff</span>, <span class="hljs-comment">/* Color */</span>
  <span class="hljs-number">1</span> <span class="hljs-comment">/* Intensity */</span>
);
</code></pre><p>and add it to the scene so it isn't pitch black,</p>
<pre><code>scene.add(ambientLight);
</code></pre><p>And we're all done with setting up the renderer and the scene!</p>
<p><strong>There's a universe now, a 3D world, there's no one in that world to see it though, which is kind of sad. Let's Fix that!</strong></p>
<h2 id="heading-camera-setup">Camera Setup 📸</h2>
<p>So the camera needs to have,</p>
<ul>
<li>FOV</li>
<li>Aspect Ratio</li>
<li>Near</li>
<li>Far</li>
</ul>
<p>Aspect Ratio is width:height of the Camera and Field of View (FOV) is how much of the world the Camera can see.</p>
<p>We covered what Near and Far are in <a target="_blank" href="https://heaust.hashnode.dev/threejs-chapter-1">Chapter 1</a>, The Camera.</p>
<p>So Let's define these,</p>
<pre><code>const fov <span class="hljs-operator">=</span> <span class="hljs-number">60</span>;
const aspect <span class="hljs-operator">=</span> window.innerWidth <span class="hljs-operator">/</span> window.innerHeight;
const near <span class="hljs-operator">=</span> <span class="hljs-number">20.0</span>;
const far <span class="hljs-operator">=</span> <span class="hljs-number">200.0</span>;
</code></pre><p>Now we need to Make a Camera with those specs,</p>
<pre><code>const camera <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(fov, aspect, near, far);
</code></pre><p>Let's place our camera 50 metres up on the Z-Axis,</p>
<pre><code><span class="hljs-attribute">camera</span>.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">50</span>, <span class="hljs-number">0</span>);
</code></pre><p>and make it look at the origin</p>
<pre><code><span class="hljs-attribute">camera</span>.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
</code></pre><p>Our Camera is Setup and it looks something like this</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636450625809/aWCUO30vh.png" alt="Camera Illustration" /></p>
<p><strong> Let's now have Something to populate the Scene </strong></p>
<h2 id="heading-importing-a-gltf-model">Importing a GLTF Model ⌛</h2>
<p>For this we need to import GLTFLoader from ThreeJs.</p>
<p>You can do this with webpack by,</p>
<pre><code><span class="hljs-keyword">import</span> { <span class="hljs-title">GLTFLoader</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"three/examples/jsm/loaders/GLTFLoader"</span>;
</code></pre><p>or with vanilla by,</p>
<pre><code><span class="hljs-keyword">import</span> { <span class="hljs-title">GLTFLoader</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"https://cdn.skypack.dev/three/examples/jsm/loaders/GLTFLoader"</span>;
</code></pre><p>I've already made a ThreeJs logo in blender and exported it as GLTF <em>(which means the format is .glb or .gltf)</em></p>
<p>Here's how you export your model as GLTF in Blender</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636451302828/XoUBTZVuA.png" alt="Blender GLTF Export illustration" /></p>
<p>Importing a model takes time, So the method that imports a model also takes a callback on what to do with the model once it's imported.</p>
<p>Let's see how that works,</p>
<p>We'll define a variable that'll hold the model in this scope,</p>
<pre><code><span class="hljs-built_in">let</span> gltfmodel;
</code></pre><p>Let's make a GLTF loader now,</p>
<pre><code><span class="hljs-keyword">const</span> gltfLoader = <span class="hljs-keyword">new</span> GLTFLoader();
</code></pre><p>The <code>.load()</code> method on the loader, has the file path as the 1st argument and the callback function as the 2nd,</p>
<p>Let's load in our model now,</p>
<pre><code>gltfLoader.load(<span class="hljs-string">"./path/to/model/file.glb"</span>, (gltf) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
</code></pre><p>here, gltf will be the model data that'll be provided to our function, you can think of <code>gltf.scene</code> to be our actual model</p>
<p>Let's place our model at origin so the Camera stares directly at it,</p>
<pre><code>    <span class="hljs-selector-tag">gltf</span><span class="hljs-selector-class">.scene</span><span class="hljs-selector-class">.position</span><span class="hljs-selector-class">.set</span>(0, 0, 0);
</code></pre><p>Set its rotation to none,</p>
<pre><code>    <span class="hljs-selector-tag">gltf</span><span class="hljs-selector-class">.scene</span><span class="hljs-selector-class">.rotation</span><span class="hljs-selector-class">.set</span>(0, 0, 0);
</code></pre><p>And Scale it up a bit in all directions,</p>
<pre><code>    <span class="hljs-selector-tag">gltf</span><span class="hljs-selector-class">.scene</span><span class="hljs-selector-class">.scale</span><span class="hljs-selector-class">.set</span>(5, 5, 5);
</code></pre><p>Now that our model is exactly how we want it, let's add it to the scene,</p>
<pre><code>    scene.add(gltf.scene);
</code></pre><p>Since the model doesn't yet exist outside this callback function scope, let's assign it to the <code>gltfmodel</code> variable we created earlier so we can later tinker with it later,</p>
<pre><code>    gltfmodel <span class="hljs-operator">=</span> gltf.scene;
});
</code></pre><p>Now our Canvas looks like this,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636453139945/vNoUcAPvg.png" alt="Static model with ambient light illustration" /></p>
<p>let's add a Point Light to make it look a little better, more 3D,</p>
<pre><code><span class="hljs-comment">// Creating the Light</span>
const light <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.PointLight(
  <span class="hljs-number">0xffff00</span>, <span class="hljs-comment">/* Color */</span>
  <span class="hljs-number">30</span>, <span class="hljs-comment">/* Intensity */</span>
  <span class="hljs-number">100</span> <span class="hljs-comment">/* Maximum Range of Light */</span>
);

<span class="hljs-comment">// Placing it at some co-ordinates, here (30, 30, 30)</span>
light.position.set(<span class="hljs-number">30</span>, <span class="hljs-number">30</span>, <span class="hljs-number">30</span>);

<span class="hljs-comment">// Adding it to the scene</span>
scene.add(light);
</code></pre><p>Now it looks much better,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636453599451/AcfJ5Hlti.png" alt="Static model with ambient light with point light illustration" /></p>
<p>But it's still... still.</p>
<p>If all we wanted was this, we could've just had an image.</p>
<p><strong>So let's animate it</strong></p>
<h2 id="heading-rendering-and-animation">Rendering and Animation 🏃</h2>
<p>Every time we want the Canvas to display the new frame, we need to call <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame"><code>requestAnimationFrame</code></a> with a callback that asks ThreeJs renderer to Render.</p>
<p>And since we need to call a new <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame"><code>requestAnimationFrame</code></a> every time the previous finishes, we can just do a recursive implementation, like so,</p>
<pre><code>const RAF <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  requestAnimationFrame(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    <span class="hljs-comment">// Recursively Render</span>
    renderer.render(scene, camera); <span class="hljs-comment">/* ThreeJs renders */</span>
    RAF(); <span class="hljs-comment">/* new requestAnimationFrame is called */</span>
  });
};
</code></pre><p>and then just call it once,</p>
<pre><code><span class="hljs-selector-tag">RAF</span>();
</code></pre><p>Moreover, we can do small increments / decrements in Rotation, Scale, Position and other properties before each render to get an animation effect, for example,</p>
<pre><code>const RAF <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  requestAnimationFrame(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    <span class="hljs-comment">/* We're increasing the Z rotation value of our
    model by 0.01 radians every frame */</span>

    <span class="hljs-keyword">if</span> (gltfmodel) gltfmodel.rotation.z <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.01</span>;

    <span class="hljs-comment">// Getting our model in the parent scope comes in use now</span>

    renderer.render(scene, camera);
    RAF();
  });
};
</code></pre><p>We need to be careful of one thing though, see how we're checking if the model exists before we increment it in the above example?</p>
<p>That's because <strong>loading a model takes time</strong>, and the model might not even be created when the 1st frame renders, so to avoid any errors, we check if it exists first.</p>
<p>And Just like that, we have a Spinning 3D Logo,
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636455292437/5RhN7Z1zl.gif" alt="Spinning 3D Logo GIF" /></p>
<h2 id="heading-responsiveness">Responsiveness ⚖️</h2>
<p>Remember all the things we defined earlier related to the width and the height of the canvas?</p>
<p>Yeah we gotta change all that every time the window (the canvas) gets resized.</p>
<p>So let's do that with a resize event listener,</p>
<pre><code>window.addEventListener(
  <span class="hljs-string">"resize"</span>,
  () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    <span class="hljs-comment">/* Redefining things with new width and height */</span>
    camera.aspect <span class="hljs-operator">=</span> window.innerWidth <span class="hljs-operator">/</span> window.innerHeight;
    renderer.setSize(window.innerWidth, window.innerHeight);

    <span class="hljs-comment">/* Updating the Camera */</span>
    camera.updateProjectionMatrix();
  },
  <span class="hljs-literal">false</span>
);
</code></pre><h2 id="heading-summary">Summary 📝</h2>
<p>We did it! Our very first ThreeJs App : )</p>
<p>Here's a function that'll do everything we did here,</p>
<pre><code><span class="hljs-keyword">import</span> <span class="hljs-operator">*</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">THREE</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">GLTFLoader</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"https://unpkg.com/three/examples/jsm/loaders/GLTFLoader"</span>;
<span class="hljs-comment">/* Or */</span>
<span class="hljs-comment">/*
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
*/</span>
<span class="hljs-comment">/* if you're using webpack */</span>

const init <span class="hljs-operator">=</span> (
  modelPath <span class="hljs-operator">=</span> <span class="hljs-string">"/assets/model.glb"</span>,
  size <span class="hljs-operator">=</span> <span class="hljs-number">5</span>,
  targetClass <span class="hljs-operator">=</span> <span class="hljs-string">"threeD-Logo"</span>
) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  const target <span class="hljs-operator">=</span> document.getElementsByClassName(targetClass);
  <span class="hljs-keyword">if</span> (target) {
    <span class="hljs-comment">// Renderer and Scene Setup</span>
    const renderer <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.WebGLRenderer({ alpha: <span class="hljs-literal">true</span> });
    const Cwidth <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
      <span class="hljs-keyword">return</span> document.getElementsByClassName(targetClass)[<span class="hljs-number">0</span>].clientWidth;
    };
    const Cheight <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
      <span class="hljs-keyword">return</span> document.getElementsByClassName(targetClass)[<span class="hljs-number">0</span>].clientHeight;
    };
    renderer.shadowMap.enabled <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(Cwidth(), Cheight());
    renderer.physicallyCorrectLights <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
    renderer.domElement.setAttribute(<span class="hljs-string">"id"</span>, <span class="hljs-string">"three-canvas"</span>);

    target[<span class="hljs-number">0</span>].appendChild(renderer.domElement);
    const scene <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Scene();
    const ambientLight <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">1</span>);
    scene.add(ambientLight);

    <span class="hljs-comment">// Camera Setup</span>
    const aspect <span class="hljs-operator">=</span> Cwidth() <span class="hljs-operator">/</span> Cheight();
    const fov <span class="hljs-operator">=</span> <span class="hljs-number">60</span>;
    const near <span class="hljs-operator">=</span> <span class="hljs-number">1.0</span>;
    const far <span class="hljs-operator">=</span> <span class="hljs-number">200.0</span>;
    const camera <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(fov, aspect, near, far);
    camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">30</span>, <span class="hljs-number">0</span>);
    camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

    <span class="hljs-comment">// Load GLTF Model</span>
    let gltfmodel;
    const gltfLoader <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> GLTFLoader();
    gltfLoader.load(modelPath, (gltf) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
      gltf.scene.scale.set(size, size, size);
      gltf.scene.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
      gltf.scene.rotation.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
      scene.add(gltf.scene);
      gltfmodel <span class="hljs-operator">=</span> gltf.scene;
    });

    <span class="hljs-comment">// Add Point Light</span>
    const light <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.PointLight(
      <span class="hljs-number">0xffff00</span> <span class="hljs-comment">/* Color */</span>,
      <span class="hljs-number">30</span> <span class="hljs-comment">/* Intensity */</span>,
      <span class="hljs-number">100</span> <span class="hljs-comment">/* Maximum Range of Light */</span>
    );
    light.position.set(<span class="hljs-number">30</span>, <span class="hljs-number">30</span>, <span class="hljs-number">30</span>);
    scene.add(light);

    <span class="hljs-comment">// Play Animation</span>
    const RAF <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
      requestAnimationFrame(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
        <span class="hljs-comment">// Recursively Render</span>
        <span class="hljs-keyword">if</span> (gltfmodel) gltfmodel.rotation.z <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.01</span>;
        renderer.render(scene, camera);
        RAF();
      });
    };

    RAF();

    <span class="hljs-comment">// Responsiveness</span>
    window.addEventListener(
      <span class="hljs-string">"resize"</span>,
      () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
        camera.aspect <span class="hljs-operator">=</span> Cwidth() <span class="hljs-operator">/</span> Cheight();
        renderer.setSize(Cwidth(), Cheight());
        camera.updateProjectionMatrix();
      },
      <span class="hljs-literal">false</span>
    );
  }
};

init();
</code></pre><p>You need to have an element already present in the html, preferably an empty div, with a specific class, "threeD-Logo" by default but you change it as the function's 3rd argument.</p>
<p><em>( If multiple such elements are present, the first one will be chosen )</em></p>
<p>The function's 1st argument is the model's path, and the 2nd is the Scaling factor.</p>
<p>The Canvas, instead of being window-wide, occupies the target div instead, this offers much better control.</p>
<p>PS: If you put this in a script tag, remember to add <code>type="module"</code> to that tag, like so, <code>&lt;script type="module"&gt; /** CODE HERE */ &lt;/script&gt;</code></p>
<h2 id="heading-afterword">Afterword</h2>
<p>Hope you liked this article, We'll take a look at ThreeJs primitive shapes, textures, and more in the next article!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635860996939/4T_FsI6hx.png?auto=compress,format&amp;format=webp" alt="ThreeJs Primitives illustration" /></p>
<p>We'll see that we don't need to import models to get visually appealing websites, Everything is right there with ThreeJs!</p>
<p>See you in <strong><a target="_blank" href="https://heaust.hashnode.dev/threejs-chapter-3">Fun With ThreeJs</a></strong>, I hope you learned something : )</p>
<p>[If you need to contact me, you can comment down below or email me at heaust.azure@gmail.com]</p>
]]></content:encoded></item><item><title><![CDATA[ThreeJs Essentials | Before you Start]]></title><description><![CDATA[Introduction
This chapter will acquaint you with the inner workings of the ThreeJs library so you're already familiar with the concepts when we start coding,
Even if you skip all the following articles of this series, this article alone will help you...]]></description><link>https://blog.heaust.org/threejs-chapter-1</link><guid isPermaLink="true">https://blog.heaust.org/threejs-chapter-1</guid><category><![CDATA[ThreeJS]]></category><category><![CDATA[webdev]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[frontend]]></category><category><![CDATA[3d]]></category><dc:creator><![CDATA[Heaust]]></dc:creator><pubDate>Fri, 05 Nov 2021 03:33:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1636081291271/lRqpC3Tu6.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>This chapter will acquaint you with the inner workings of the ThreeJs library so you're already familiar with the concepts when we start coding,</p>
<p>Even if you skip all the following articles of this series, this article alone will help you make informed decisions throughout your Journey!</p>
<p> <a target="_blank" href="https://www.freepik.com/vectors/creative"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633846787584/1wD_i9PAQ.png" alt="Creative vector created by pikisuperstar - www.freepik.com" /></a> </p>
<p>First we'll look at different kinds of rendering engines and how ThreeJs actually renders your 3D world onto a 2D Canvas on your screen.</p>
<p>Then we'll dive into the three fundamentals,</p>
<ul>
<li>Camera</li>
<li>3D Models</li>
<li>Lighting and Shadows</li>
</ul>
<p>We'll then get to see how the images you set to be textures get shrunk and magnified,
and then conclude right after with a concise summary of the article with a little teaser for the next one 😊</p>
<p>So let's get into it ^^</p>
<h2 id="heading-how-does-threejs-render">How does ThreeJs Render? 📝</h2>
<p>There are 3 main kinds of rendering techniques,</p>
<ul>
<li>Rasterization &lt;-</li>
<li>Ray Casting</li>
<li>Path Tracing</li>
</ul>
<p><a target="_blank" href="https://www.youtube.com/watch?v=LAsnQoBUG4Q">RAY TRACING and other RENDERING METHODS</a> by  <a target="_blank" href="https://www.youtube.com/channel/UCZsHHoDLK0s6r44ifx0DnXQ">Andrey Lebrov</a> is a great video that explains all these better than I do here, if you watch that, you can safely skip this section.</p>
<p>If videos aren't your thing though, stick with me here, here we go!</p>
<h3 id="heading-rasterization">Rasterization</h3>
<blockquote>
<p>Rasterization (or rasterisation) is the task of taking an image described in a vector graphics format (shapes) and converting it into a raster image (a series of pixels, dots or lines, which, when displayed together, create the image which was represented via shapes). - <em><a target="_blank" href="https://en.wikipedia.org/wiki/Rasterisation">wikipedia</a> </em></p>
</blockquote>
<p><strong>ThreeJs uses Rasterization</strong> to render, mainly because it's the only one that can provide <strong>real-time rendering</strong> without owning a beefy high-cost PC.</p>
<p>It makes sense too, considering we want our 3D web experience to be widely accessible, we Need an app you can run with,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633849311953/AiKr-Qjq_.png" alt="RTX Off" /></p>
<p>If you've ever used Blender, the <a target="_blank" href="https://docs.blender.org/manual/en/latest/render/eevee/introduction.html">Eevee</a> engine uses Rasterization.</p>
<p>It works by storing every object's image along with their distance from the camera and then drawing the pixels that are closest to the camera and ignoring the rest.</p>
<p>The Blazing fast speed of renders don't come without a price though, Blender provides a breakdown of the limitations that Eevee faces  <a target="_blank" href="https://docs.blender.org/manual/en/latest/render/eevee/limitations.html">here</a>.</p>
<p>Largely the drawbacks are an inability to compute,</p>
<ul>
<li>Physically accurate shadows</li>
<li>Recursive reflections</li>
<li>Refraction</li>
<li>Bounced lights</li>
<li>Caustics</li>
</ul>
<p>These limitations are quite the handicap, so we've come up with some clever ways to fake those effects, that aren't quite accurate but come passably close. Keep this at the back of your mind for now as we'll revisit this in <strong>Lighting and Shadows</strong>.</p>
<p><em>You can safely skip the next 2 sections (<strong>Ray Casting</strong> and <strong>Path Tracing</strong>) as while they are prominent 3D rendering methods, they have little to do with ThreeJs.</em></p>
<h3 id="heading-ray-casting">Ray Casting</h3>
<blockquote>
<p>Ray casting is the methodological basis for 3-D CAD/CAM solid modeling and image rendering. It is essentially the same as ray tracing for computer graphics where virtual light rays are "cast" or "traced" on their path from the focal point of a camera through each pixel in the camera sensor to determine what is visible along the ray in the 3-D scene. -  <a target="_blank" href="https://en.wikipedia.org/wiki/Ray_casting"><em>wikipedia</em></a> </p>
</blockquote>
<p>It works like this,</p>
<ol>
<li>Cast a ray from the camera for every pixel out till the end of render distance.</li>
<li>Look at the closest intersection point.</li>
<li>Cast rays to all the lights from that point to calculate illumination.</li>
<li>If the ray reaches a light without any other closer intersections, factor in its contribution.</li>
<li><p>Now you have the color of that point, draw it on the pixel.</p>
<p><a target="_blank" href="https://upload.wikimedia.org/wikipedia/commons/8/83/Ray_trace_diagram.svg"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633853085573/zxdZ-fb1E.png" alt="Ray tracing diagram " /></a></p>
</li>
</ol>
<p>This is still relatively performant but since it doesn't factor in light bounces it still pays a lot of the same prices that rasterization does.</p>
<h3 id="heading-path-tracing">Path Tracing</h3>
<blockquote>
<p>Path tracing is a computer graphics Monte Carlo method of rendering images of three-dimensional scenes such that the global illumination is faithful to reality. Fundamentally, the algorithm is integrating over all the illuminance arriving to a single point on the surface of an object. This illuminance is then reduced by a surface reflectance function (BRDF) to determine how much of it will go towards the viewpoint camera. This integration procedure is repeated for every pixel in the output image. - <a target="_blank" href="https://en.wikipedia.org/wiki/Path_tracing"><em>wikipedia</em></a></p>
</blockquote>
<p>If you've ever used Blender, the <a target="_blank" href="https://docs.blender.org/manual/en/latest/render/cycles/introduction.html">Cycles</a> engine uses Path Tracing.</p>
<p>When someone's talking about Ray Tracing, unless specified or context clued otherwise, it's safe to assume they're talking about this.</p>
<p>This follows the same process as Ray Casting but recursively repeats the process at intersection points to gain more accurate information about reflections, refraction and shadows.</p>
<p>This provides extremely photo realistic results, but it's something so computationally expensive that the time it takes to render once is in the magnitude of hours.</p>
<p>That's unless of course, you have your</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633854863333/QYmBOCnzi.png" alt="RTX On" /></p>
<p><strong><em> Now that we have a basic idea of how the 3D world is actually rendered on a 2D Canvas, let's dive into the fundamentals of a ThreeJs Project Setup! </em></strong></p>
<h2 id="heading-the-camera">The Camera 📷</h2>
<p>The camera is a vital part of a ThreeJs render, it's something that looks at your 3D world, also called <strong>the scene</strong>,  and renders (draws) whatever it sees on the canvas element.
 <a target="_blank" href="https://www.freepik.com/vectors/people"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633838971547/FXx7TtNPU.png" alt="People vector created by freepik - www.freepik.com" /></a></p>
<p>The number of drawings it can make in 1 second is called your <strong>frame rate</strong>, measured in fps (frames per second), the more complex your scene is, the more time it takes to draw and that causes a drop in your fps.</p>
<p>You should try to keep your frame rate around <strong>60fps</strong>, anything too low causes your animation to feel laggy, anything below 24 shatters the illusion of the video entirely and starts to feel like still images.</p>
<p>We'll discuss more about what actually makes a scene complex in <strong>3D models</strong> and <strong>Lighting</strong>.</p>
<p>The Camera can take <strong>Near</strong> and <strong>Far</strong> values as parameters when created, the camera can only sees things between its <em>near</em> and <em>far</em> distances.</p>
<p>Try to keep it fairly tight as we don't want the camera to waste time looking for things at places we know are empty.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635858626545/FNR95alsz.jpeg" alt="An Illustration of Camera with Near and Far" /></p>
<p>ThreeJs offers 2 types of Cameras, <strong>Perspective</strong> and <strong>Orthographic</strong>.</p>
<p>-&gt; Perspective is designed to mimic the human eye and the cameras in the actual world, it's usually the one you'll want to use.</p>
<p>-&gt; Orthographic is designed to give a flat and depth-less look. You'll want this if you're going for a more 2D vibe. </p>
<p><em>More about these ^ in detail in future chapters</em></p>
<p><strong>Now that we know about Cameras, we need things to populate the scene and for the Camera to see : )</strong></p>
<h2 id="heading-3d-models">3D Models 🧘🏽</h2>
<p>A complete 3D model is called a <strong>mesh</strong>.</p>
<p>There are 2 Components to 3D Models, the <strong>Geometry</strong> (shape) and the <strong>Material</strong> (texture).</p>
<p>3D Models come in many formats like .glb/.gltf, .fbx, .obj and more.</p>
<p>If you get your model from somewhere online, chances are you'll get it in one of these formats, it'll contain the entire model including both Geometry and Material.</p>
<h3 id="heading-the-geometry">The Geometry</h3>
<p>It is the actual shape of the model.</p>
<p>Like images are made up of small pixels, A Geometry is made up of small triangles, why triangles? because it's the easiest shape for your GPU to calculate.</p>
<p>The number of these triangles is called the model's <strong>poly count</strong>, short for polygon count. Models with a high poly count are called <strong>high poly models</strong>, conversely those with a low poly count are called <strong>low poly models</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635864648461/Uj6rOU-Kz.png" alt="Poly Count Illustration" /></p>
<p>Higher poly models are more detailed and can offer greater realism but at a cost.</p>
<p>Essentially, increasing poly count increases the complexity of your scene.</p>
<p>The higher your Poly Count, the more data the Geometry needs to save that information, so high poly models tend be larger in file size, delaying a user's access to your web experience.</p>
<p>Not only that, high poly models require more computation to render, so they slow things down as well, causing lag on low end devices and with enough polygons, even on higher end ones.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635865760476/_aOvslaZ-.png" alt="Consequences of High Poly Counts Illustration" /></p>
<p>That's why for our purposes, it's better to try and go with the lowest poly models we can to optimize our website.</p>
<p>Now that we know what Geometry is... how do we get it?</p>
<p>You can obtain Geometry in the following ways.</p>
<p>-&gt; ThreeJs itself offers some primitive geometry for you to create in your scene.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635860996939/4T_FsI6hx.png" alt="Primitive Geometry Illustration" /></p>
<p>-&gt; You could Create your own Geometry in 3D Programs like <strong><a target="_blank" href="https://www.blender.org/">Blender</a></strong> or <strong><a target="_blank" href="https://pixologic.com/">Z-Brush</a></strong>, this is something I created in blender in about 30 minutes as anatomy practice.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635861435428/Gb0VoU6zC.png" alt="Creating Geometry by yourself Illustration" /></p>
<p>-&gt; It is understandable though if you don't want to spend a significant chunk of your time practicing modelling and wanna jump straight to creating awesome websites.</p>
<p>You can checkout <strong><a target="_blank" href="https://www.mixamo.com/">Mixamo</a></strong> for free 3D Character Models and Animations, You can also checkout websites like <strong><a target="_blank" href="https://polyhaven.com/">Poly Haven</a></strong>, <strong><a target="_blank" href="https://www.cgtrader.com/">CG Trader</a></strong> and <strong><a target="_blank" href="https://sketchfab.com/">SketchFab</a></strong> for free or paid models.</p>
<p>This is what I can list off the top of my head and I'm sure there are other great websites like these.</p>
<p><strong>Okay so now we can create Geometry, something doesn't feel right though, does it have to be all dull and gray? Nope, Let's fix that with</strong></p>
<h3 id="heading-the-material">The Material</h3>
<p>The material is the texture of the object. It dictates how the object's surface looks.</p>
<p>Say you have a plane, and you want it to look like tree bark, what we can do is just tell that plane to display an image of a tree bark, that's essentially how textures work, they're just polygons displaying images.</p>
<p>There's a flaw with that approach though, if we tell the plane to pretend to be an image, it's gonna look just that, a flat boring image, like we just pasted a printout on a flat piece of cardboard (which is kind of what we did).</p>
<p>Here's a visual on how it'll look.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636032418218/6aemEsn5C.png" alt="Color Texture Illustration" /></p>
<p>And that makes sense because nothing in our world is truly flat, there's a lot of small irregularities in any surface we look at and usually different materials have different patterns of those irregularities.</p>
<p>If we had an ideal engine that could process a near infinite amount of light bounces and the amount of polygons in the order of 10 followed by 23 zeroes and someone who took the time to sculpt on that level, we'd get a completely photo-realistic render by just giving it the image of a tree bark.</p>
<p>We don't have an ideal engine nor an omnipotent sculptor though.</p>
<p>So what we need to do to make it more realistic is tell it how it should actually interact with the light as if the surface already had those irregularities. We need to tell our plane to pretend as if it wasn't just a plane.</p>
<p>We can do this by assigning it different kinds of images that give it information on what to pretend.</p>
<p>Some images used for this purpose are,</p>
<ul>
<li>bump map: The white makes the plane pretend to be raised and the black to be creased.</li>
<li>roughness map: So the plane knows where it should be shiny and where it should be dull</li>
<li>displacement map: Displaces the actual geometry, if your model is high poly enough for it.</li>
<li>AO map: Dictates places that should be occluded.</li>
<li>metalness map: So the plane knows where it should be metal-like and where not.</li>
</ul>
<p>Here's how these images look.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636037103473/ASXyOh8gJ.png" alt="Different Texture Maps" />
And if we utilize all the images we just saw, we'll end up with a result that looks way more realistic and appealing than our previous attempt.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636035278292/J0ORjmU_I.png" alt="Completely textured plane" /></p>
<p>The Material can be as complex as we just saw, or a simple plain color.</p>
<p>ThreeJs Provides a way to effortlessly create simple materials and pass in different maps as parameters for complex ones.</p>
<p>But how are textures applied on complex 3D models from a single image? Let's take a cube for example.</p>
<p><a target="_blank" href="http://www.math.union.edu/~dpvc/talks/2000-11-22.funchal/cube-unfolded.html"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636082458692/Ahz7HvPff.png" alt="image.png" /></a></p>
<p>The cube can be easily be cut up along the edges and be unfolded flat. This process is called <strong>UV Unwrapping</strong></p>
<p>The flat projection of the cube, called a <strong>UV Map</strong> can then display the parts of the image it overlaps with.</p>
<p>All 3D models go through a similar process if they have a texture.</p>
<p><em>We'll see more about texturing in detail in future chapters</em></p>
<p>If you've downloaded a model off the internet, it's already textured and you don't have to worry about either geometry or texture.</p>
<p>If you need textures, you can get them online at places like <strong><a target="_blank" href="https://polyhaven.com/">poly haven</a></strong>, <strong><a target="_blank" href="https://www.poliigon.com/textures/free">poliigon</a></strong>, <strong><a target="_blank" href="http://texturelib.com/">texture lib</a></strong>, <strong><a target="_blank" href="https://ambientcg.com/">ambient cg</a></strong> and many more such places! (Or you can directly texture paint a model in Blender, Z-brush and the like)</p>
<p><strong>Now that we have populated our scene, we need some illumination to actually see our models, so let there be Light (and Shadows).</strong></p>
<h2 id="heading-lights-and-shadows">Lights and Shadows 💡👤</h2>
<p><strong>Expensive</strong>. If there's one thing I want you to take away from this section, it's that both Lighting and Shadows are expensive.</p>
<h3 id="heading-lights">Lights 💡</h3>
<p>There are a plethora of Lights you can choose from in ThreeJs, there's the omnipresent <strong>Ambient Light</strong> that illuminates everything equally, there's the <strong>Point Light</strong> which emits Light in all directions from a singular source, there's a <strong>Directional Light</strong> like the sunlight, there's <strong>Spot Light</strong>, <strong>Hemisphere Light</strong> and <strong>Rectangular Area Light</strong>.</p>
<p>A bunch of lights that can illuminate in a bunch of ways.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636038870036/SOLoWdLlh.png" alt="Lights Illustration" /></p>
<p>Lighting is essential to any 3D experience, it not only allows you to actually see areas of your scene well, a clever lighting setup with contrasting hues, visual cues and other design tricks will enhance the experience many folds!</p>
<p>While it is an excellent tool that we can make use of to enhance, it's a tool that we're often not able to afford.</p>
<p>Lights take a lot of computational power (some more than others), they increase the complexity of the scene by a lot and it's usually not a good idea to add too many lights to your scene.</p>
<h3 id="heading-shadows">Shadows 👤</h3>
<p>Yes, there's a reason I've been mentioning Lights and Shadows separately, Shadows are something that are optional to have and for good reason, shadows on their own are very expensive to calculate and as such shouldn't be overused.</p>
<p>But Heaust, doesn't adding Lights automatically mean that you're getting shadows too since a shadow is nothing but an absence of light and wherever light doesn't reach should be dark?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636041214563/bdK2rmvIu.png" alt="Saitama OK face" /></p>
<p>Yes that would be the case if we were were using a Ray Tracing engine, but as we've already discussed we're using something called Rasterization, which doesn't actually calculate the render using light but distance from the camera instead.</p>
<p>And since we don't automatically get shadows, we have need to calculate <strong>Shadow Maps</strong> separately to get the shadows.</p>
<p><a target="_blank" href="https://threejs.org/examples/#webgl_shadowmap_viewer"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636041310739/NHljW8M7m.png" alt="Shadow Map Example Illustraion" /></a></p>
<p>As you can see top left in this example, the renderer has to render shadow maps for each light it's calculating shadows for on every frame along with the main render.</p>
<p>You can guess how significantly this would impact the frame rate if for every frame you have to do multiple renders!</p>
<p>So a good rule of thumb would be to only use shadows if absolutely necessary and only for dynamic elements.</p>
<p><em>We'll talk about Lighting and Shadows in detail in future chapters</em></p>
<p>So... we can't use a lot of Lighting, and we can't even have any Shadows, are we doomed to suffer the cruel fate of mediocrity and bland design?</p>
<p>Nope, all isn't lost yet!</p>
<h3 id="heading-baking">Baking 🥧</h3>
<p>Since Everything we see is color anyway, we can paint the textures of surfaces to be dark or bright according to the environment we design for it! (we don't have to personally do it, Blender or other CG software can do it for us).</p>
<p>This removes the need to enable shadows altogether in static environments!</p>
<p>It's a great way to get all the performance without compromising any of the experience.</p>
<p>Here's what I mean, take a look at this render.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636045935874/beRLPLC_R.png" alt="Baked Render" /></p>
<p>It's clear that the sphere is casting a shadow on the plane from a light coming from somewhere in the top right, right?</p>
<p>Nope! this entire thing is a facade, the shadow and highlights are painted (baked)!</p>
<p>There isn't a single light (other than ambient) in that scene. Here's how the texture for the plane looks.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636046082368/M-tTD893D.png" alt="Baked Texture" /></p>
<p>So a lot of the times, you'll see we don't even need Lights and Shadows, we can just create clever illusions with baking that they're already in the scene.</p>
<p>For shadows of moving objects we can animate a plane with a Shadow texture.</p>
<p>Moreover we can add environment maps to shiny objects to tell them what to reflect so they don't have to waste time calculating.</p>
<p>We'll look at that and Many more things in future chapters.</p>
<h2 id="heading-mip-mapping">MIP Mapping 🗺️</h2>
<p>We know how we give textures to our models via images now.</p>
<p>The images we provide though, are 2D, and the models are 3D, So it stands to reason the closer parts of the model should be larger and the further parts should smaller, compared to our original image at times.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636047464146/Uhkf2o0v8.png" alt="Textured Plane Stretching out in perspective" /></p>
<p>That means at some point that image has to go through a process of being enlarged and / or shrunk.</p>
<p>For shrinking what happens is your GPU takes your, say, 1024x1024 image, halves it to 512x512 and saves it, halves that to 256x256 and saves it and so on till it gets a 1x1 image.</p>
<p><a target="_blank" href="https://www.youtube.com/watch?v=8OtOFN17jxM"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636048595139/hHNsd37PZ.png" alt="Mipmapping diagram" /></a></p>
<p>It goes through this process so that if it ever has a need of a smaller res image, it already has it all computed and doesn't have to render from a high res image which really isn't as optimized and results in inaccurate rendering and noise.</p>
<p>So the takeaway from this is, always have your texture image's resolution in powers / exponents of 2, you will not be able to use mipmapping otherwise.</p>
<p>There are times that mipmapping will work against you though, if you want a pixelated look on your textures like in minecraft, you should probably turn mipmapping off as it results in blurred images when images get too stretched (this is usually desirable as we don't often want a pixelated look).</p>
<h2 id="heading-skybox">Skybox ⛅</h2>
<p>Imagine your entire world is inside a Cube, now give that cube a texture to give the illusion of a 3D environment. That's a skybox and ThreeJs Makes it really easy to implement.
<a target="_blank" href="https://polyhaven.com/a/shudu_lake"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636081961198/w7T8IocxF.png" alt="image.png" /></a></p>
<p>Think something like google street view.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Now you're completely ready to just dive into the <a target="_blank" href="https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene">api docs</a> of ThreeJs and start creating amazing 3D web experiences.</p>
<p>If you're too lazy to read through all of that on your own though, stick with me for our next chapter, <a target="_blank" href="https://heaust.hashnode.dev/threejs-chapter-2"><strong>Create a Spinning 3D Model</strong></a>.</p>
<p>That's right! All of the theory is over (for now), we're getting our hands dirty with code for the next few chapters of the series!</p>
<p>Thanks for reading and Congrats! for making it all the way to the end. If you've never heard about this stuff before, I know it can be hard to wrap your head around, but give yourself a pat on the back! Now you'll be able to more intuitively navigate the CG world whether it's for creating awesome websites with ThreeJs or anything else.</p>
<p>See you in the next Chapter! I hope you learned something : )</p>
<p>[If you need to contact me, you can email me at heaust.azure@gmail.com]</p>
]]></content:encoded></item><item><title><![CDATA[ThreeJs: An Introduction to 3D Web Apps]]></title><description><![CDATA[What Is ThreeJs?
If you go on their  Github, they describe it thus,

"The aim of the project is to create an easy to use, lightweight, cross-browser, general purpose 3D library. The current builds only include a WebGL renderer but WebGPU (experimenta...]]></description><link>https://blog.heaust.org/threejs-intro</link><guid isPermaLink="true">https://blog.heaust.org/threejs-intro</guid><category><![CDATA[ThreeJS]]></category><category><![CDATA[3d]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[webdev]]></category><category><![CDATA[Frontend Development]]></category><dc:creator><![CDATA[Heaust]]></dc:creator><pubDate>Sun, 03 Oct 2021 01:04:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1636111805443/pbOqfVZCn.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="what-is-threejs">What Is ThreeJs?</h2>
<p>If you go on their  <a target="_blank" href="https://github.com/mrdoob/three.js">Github</a>, they describe it thus,</p>
<blockquote>
<p>"The aim of the project is to create an easy to use, lightweight, cross-browser, general purpose 3D library. The current builds only include a WebGL renderer but WebGPU (experimental), SVG and CSS3D renderers are also available in the examples."</p>
</blockquote>
<p>Adding to that, what the library does is abstract away all the low level <em>(closer to hardware: think languages like c++, rust, etc)</em> webgl.</p>
<p>It gives you the ability and creative freedom to do things like importing a 3D model, animating it, applying textures on a mesh, setting up lights, cameras and plethora of other cool stuff <strong>without</strong> actually writing an odyssey length piece of code</p>
<p><strong>
To give you some context, it would take more than 20 lines of code just to create a plane with purely webgl, it would take only 3 with ThreeJs
</strong></p>
<p>It's an excellent tool to create performant 3D web apps, giving you all the control, without all the hassle,
So you can get straight to creating.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633222345402/jWNfophef.jpeg" alt="Working on a laptop: Image from https://www.freepik.com/jcomp" /></p>
<h2 id="why-should-i-care-for-3d">Why should I care for 3D?</h2>
<p>Adding 3D elements to your web app is an excellent way to make it stand out and pop out,
It makes the users, aside from the content, enjoy the web experience itself and shoots the retention rate through the roof.</p>
<p>If you're a 2D javascript game-dev, this gives you the chance to explore an ocean of new possibilities.</p>
<p>If you need users to stay on your website, make an impact or just entertain, 3D web apps are just the thing for you!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633222507394/N0eOMNOyM.jpeg" alt="Stand Out: Illustration by https://www.freepik.com/stories" /></p>
<h2 id="types-of-3d-websites">Types of 3D Websites</h2>
<h3 id="straight-up-3d">Straight Up 3D</h3>
<p>These kinds of websites are all or mostly a 3D experience, they have a game-y vibe to them.
A great example is Bruno Simon's  <a target="_blank" href="https://bruno-simon.com/">portfolio</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633217653110/0I-kR20zT.gif" alt="Bruno Simon's portfolio preview" /></p>
<h3 id="augmented-2d">Augmented 2D</h3>
<p>These Kinds are a carefully crafted mix of 3D and 2D, they take an interesting and attractive approach to design and are more suited to a simplistic consumption.</p>
<p>A good example would be the  <a target="_blank" href="https://media.karmanproject.org/">karman project </a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633218015686/klMPafxVr.gif" alt="Karman Project Preview" /></p>
<h3 id="virtual-reality-and-augmented-reality">Virtual Reality and Augmented reality</h3>
<p>You can use ThreeJs and WebXR to create interesting and mesmerizing VR / AR experiences.</p>
<h2 id="how-it-works-basic-idea">How it Works: Basic Idea</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633219140820/oTi-_UmL_.png" alt="Composition of a 3D Scene" /></p>
<p>At the very foundation, you need to set up a camera, the 3D models / the actual subjects of your scene, some lighting to illuminate it all.</p>
<p>You'll add a canvas element bound to ThreeJs in the HTML and the canvas will display whatever the camera sees.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633219426957/a2qT61Iha.png" alt="What the canvas sees" /></p>
<p>You can animate your model, play with lighting and move your camera in real time and bind them to user actions to fuse it all together into a complete 3D experience</p>
<h2 id="challenges-with-threejs-and-3d-websites-in-general">Challenges with ThreeJs and 3D Websites in general</h2>
<h3 id="optimisation">Optimisation</h3>
<p>Your web experience will be running on a wide range and variety of devices and platforms, a challenge all too familiar to those who've worked on the design and styling of websites,
this time there's also the question of performance,</p>
<p>Since ThreeJs rendering utilizes the GPU and the specs vary widely across devices, something that'll give you <strong>60fps</strong> on a laptop might not even manage <strong>5fps</strong> on a smartphone or a tablet.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633211821655/zR1mf2sIw.png" alt="Different Devices: Image by https://www.freepik.com/starline" /></p>
<p>Lighting and Shadows are especially difficult to process. The point is you'll have to pay close attention to both sides of the coin, make the experience stunning for the cutting edge hardware while still keeping it manageable for even mobile devices.</p>
<p>Usually this calls for either clever design, platform specific code or both.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633209795673/LlRqYUU8-.png" alt="Good Practices: Image Generated by https://carbon.now.sh" /></p>
<h3 id="loading-time">Loading Time</h3>
<p>Sometimes you need multiple images for a single texture.</p>
<p>Consider this, you need to make a floor,
So you make a plane and stick a wood floor image on it, done right?</p>
<p>Not really, while that sounds feasible, in practice it actually looks just that,
like a photo pasted onto a flat cardboard.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633215269918/qzjLyTO9B.jpeg" alt="Wood Texture: Image by https://www.freepik.com/pvproductions" /></p>
<p>To make a texture <strong>truly 3D</strong> you need to tell it where the surface <strong>bends</strong> and <strong>curves</strong>, where the <strong>bumps</strong> and the <strong>creases</strong> are, where it's <strong>shiny</strong> and where it's <strong>rough</strong>.</p>
<p>All this information is conveyed by different images like bump maps, displacement maps, metalness maps, roughness maps, normal maps, etc.</p>
<p>Here's an  <a target="_blank" href="https://conceptartempire.com/texture-maps/">article</a>  if you want to know more about it.</p>
<p>The point is you can pile up <strong>a lot of images</strong> real fast.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633215580700/MceEaDrbr.jpeg" alt="Carrying a stack of images: Illustration by https://www.freepik.com/kraphix" /></p>
<p>Combine this with the actual 3D model, its animations, and the size of your app can quickly grow out of control and let's face it, most people aren't gonna wait for a 30MB website to load no matter how mesmerizing it is.</p>
<p>This is usually countered by fancy loading screens, a pure low-size 2D page for users to browse while they wait for the 3D content to load and minimizing the image sizes to have low asset sizes to begin with.</p>
<p>A general rule of thumb to follow is always use JPEGs wherever possible and use tools like  <a target="_blank" href="https://squoosh.app/">squoosh</a> to compress your images to the minimum possible size before the quality drop is unacceptable.</p>
<h2 id="is-it-worth-it">Is it Worth it?</h2>
<p>Giving a lot of your time to learning an alien JavaScript library can be a long arduous process, and it's understandable to want to know what lies at the end of this journey before you start,</p>
<p>ThreeJs has you covered on that end too! The front page of their <a target="_blank" href="https://threejs.org/">website</a> boasts a plethora of amazing, captivating experiences crafted by developers using this wonderful library.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633139396214/L4xdv8mwG.png" alt="ThreeJs HomePage" /></p>
<h2 id="did-i-read-the-sub-title-wrong">Did I read the Sub-Title wrong?</h2>
<p>Nope, this is a prologue.
Expect a series describing the ins and outs of 3D Web apps and ThreeJs to follow.</p>
<p>See you in the next Article, <strong><a target="_blank" href="https://heaust.hashnode.dev/threejs-chapter-1">ThreeJs Essentials: Before you Start</a></strong></p>
<p>[If you need to contact me for something you can email me at heaust.azure@gmail.com]</p>
]]></content:encoded></item></channel></rss>