<?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" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ three.js - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ three.js - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 18 Jun 2026 21:00:24 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/three-js/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Build a 3D Art Gallery with Three.js ]]>
                </title>
                <description>
                    <![CDATA[ 3D web experiences can provide engaging and interactive user interfaces. And one of the best JavaScript libraries to create these experiences is Three.js. We just posed a comprehensive Three.js course on the freeCodeCamp.org YouTube channel. This cou... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-3d-art-gallery-with-threejs/</link>
                <guid isPermaLink="false">66b200d639b555ffda8bfe6b</guid>
                
                    <category>
                        <![CDATA[ three.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Thu, 07 Mar 2024 16:21:11 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/03/art2.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>3D web experiences can provide engaging and interactive user interfaces. And one of the best JavaScript libraries to create these experiences is Three.js.</p>
<p>We just posed a comprehensive Three.js course on the freeCodeCamp.org YouTube channel. This course is designed to guide you through the process of building an interactive 3D art gallery from the ground up using Three.js. Emilian Kasemi created this course.</p>
<h2 id="heading-understanding-threejs">Understanding Three.js</h2>
<p>Three.js is a JavaScript library that empowers web developers to create detailed and interactive 3D graphics that run smoothly in web browsers without the need for any specialized plugins. Here, we delve deeper into the facets of Three.js, exploring its core components, how it interfaces with WebGL, and the various features that make it an indispensable tool for 3D web development.</p>
<p>Three.js abstracts the complexity of raw WebGL, offering a more approachable set of APIs. Here are some of the core components that you'll frequently work with in Three.js:</p>
<p><strong>Scenes:</strong> The starting point of any Three.js application, a scene acts as a container where you place objects, lights, cameras, and other elements necessary for your 3D world.</p>
<p><strong>Cameras:</strong> Cameras are pivotal in determining how your scene is viewed. Three.js provides various camera types, like PerspectiveCamera and OrthographicCamera, each offering a unique perspective and use case.</p>
<p><strong>Renderers:</strong> The renderer takes the scene and camera, processing them to display your 3D content on a webpage. The WebGLRenderer, one of the most commonly used renderers in Three.js, utilizes WebGL to render scenes with hardware acceleration.</p>
<p><strong>Geometry:</strong> This defines the shape of the objects you'll create. Three.js includes a plethora of predefined geometries like BoxGeometry, SphereGeometry, and more, which you can use to quickly create standard shapes.</p>
<p><strong>Materials:</strong> Materials define the appearance of your objects. Whether it's the color, how it interacts with light, or the texture, materials give your objects character and realism.</p>
<p><strong>Textures:</strong> Textures allow you to add complexity and detail to your materials, giving objects a more lifelike appearance. You can use images or dynamically generated textures to enhance the visual quality of your 3D models.</p>
<p><strong>Lights:</strong> Lights add depth and realism to your scenes. Three.js offers various types of lights, like AmbientLight, PointLight, DirectionalLight, etc., each contributing differently to how your scene is illuminated.</p>
<p>At its core, Three.js is built atop WebGL, a web standard that allows browsers to render interactive 3D graphics. WebGL is powerful but complex, involving a steep learning curve due to its low-level nature. Three.js provides a friendly abstraction, allowing developers to leverage the power of WebGL without getting bogged down by its intricacies. This abstraction enables developers to focus on the creative aspects of 3D design, rather than the detailed technicalities of WebGL programming.</p>
<p>By offering a user-friendly interface, a robust set of features, and a supportive community, Three.js equips developers with the tools they need to bring their 3D visions to life on the web.</p>
<h2 id="heading-course-overview">Course Overview</h2>
<p>This Three.js course is created to help both beginners and experienced developers looking to expand their skill set into the 3D web development realm. Here's what you can expect to learn:</p>
<h3 id="heading-scene-creation">Scene Creation</h3>
<p>The course kicks off with the fundamentals of Three.js, starting with scene creation. You'll learn how to set up the essential components of a Three.js application, defining the space where your 3D objects will live and interact.</p>
<h3 id="heading-camera-setup">Camera Setup</h3>
<p>Understanding camera setups is crucial for defining the perspective from which users will view your 3D world. This section covers various camera configurations, helping you to choose and implement the right camera setup for your project.</p>
<h3 id="heading-renderer-development">Renderer Development</h3>
<p>Rendering is the process of generating a photorealistic image from a 2D or 3D model. In this course, you'll dive into renderer development, learning how to accurately display your 3D scene in a web browser.</p>
<h3 id="heading-geometry-material-and-texture-creation">Geometry, Material, and Texture Creation</h3>
<p>No 3D scene is complete without objects, and this is where geometry, material, and texture come into play. You'll explore how to create complex 3D objects, apply different materials, and add textures to enhance the visual appeal of your scene.</p>
<h3 id="heading-meshing">Meshing</h3>
<p>Meshing is a critical process in 3D modeling that involves creating a mesh—a collection of vertices, edges, and faces that define the shape of a 3D object. This section will guide you through the meshing process in Three.js.</p>
<h3 id="heading-animation">Animation</h3>
<p>Bring your 3D scene to life with animation. Learn how to add movement to objects, creating dynamic and engaging 3D experiences for users.</p>
<h3 id="heading-controls">Controls</h3>
<p>User interaction is key to immersive 3D experiences. This course will cover how to implement controls, allowing users to interact with the 3D objects and navigate the scene.</p>
<h3 id="heading-real-time-ui-configuration-using-a-gui-debugger">Real-Time UI Configuration Using a GUI Debugger</h3>
<p>Fine-tune your 3D scene with real-time UI configuration. You'll get hands-on experience using a GUI debugger to adjust various aspects of your scene and objects, ensuring your 3D gallery looks and functions exactly as you envision.</p>
<h3 id="heading-adding-vr-support">Adding VR Support</h3>
<p>The course concludes with an exciting foray into virtual reality, teaching you how to add VR support to your 3D art gallery. This will enable users with VR devices to immerse themselves fully in the world you've created.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This course offers a comprehensive, step-by-step guide to mastering Three.js. Watch the full course <a target="_blank" href="https://youtu.be/imqiYWidUIA">on the freeCodeCamp.org YouTube channel</a> (8-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/imqiYWidUIA" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Implement a Blender Model in a React.js Application using Three.js ]]>
                </title>
                <description>
                    <![CDATA[ In this step-by-step guide, you'll learn how to build a basic Blender file with incorporated fundamental animations. After that, you'll learn how to integrate Three.js with your React apps using React Three Fiber.  Getting familiar with these concept... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/blender-three-js-react-js/</link>
                <guid isPermaLink="false">66bb8f3cdeef71ff683a6d2a</guid>
                
                    <category>
                        <![CDATA[ animations ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ three.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Matthes B. ]]>
                </dc:creator>
                <pubDate>Thu, 17 Aug 2023 14:58:49 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/08/pexels-chevanon-photography-1335971.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this step-by-step guide, you'll learn how to build a basic Blender file with incorporated fundamental animations. After that, you'll learn how to integrate Three.js with your React apps using React Three Fiber. </p>
<p>Getting familiar with these concepts can help you make sure your upcoming React.js applications stand out.</p>
<h2 id="heading-heres-what-well-cover"><strong>🔐</strong> Here's What We'll Cover:</h2>
<ul>
<li>Crafting a Blender model, encompassing animations, materials and the export process.</li>
<li>Building a React.js application integrated with Three.js via React Three Fiber.</li>
<li>Incorporating your personally created Blender model into the React.js application.</li>
</ul>
<h2 id="heading-prerequisites"><strong>📝</strong> Prerequisites:</h2>
<ul>
<li>A fundamental grasp of the 3D software Blender is recommended.</li>
<li>Basic familiarity with React.js is required.</li>
<li>Prior experience with Three.js is not necessary.</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-what-are-threejs-and-blender">💭 What are Three.js and Blender?</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-reactjs-with-threejs">🔧 How to Set Up React.js with Three.js</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-blender-model"><strong>🔨</strong> How to Create a Blender Model</a></li>
<li><a class="post-section-overview" href="#heading-texture-baking-for-procedural-materials"><strong>✏️</strong> Texture Baking for Procedural Materials</a></li>
<li><a class="post-section-overview" href="#-how-to-implement-the-blender-model-into-the-react-js-application"><strong>✒️</strong> How to Implement the Blender Model into the React.js Application</a></li>
<li><a class="post-section-overview" href="#heading-additional-information"><strong>📄</strong> Additional information</a></li>
<li><a class="post-section-overview" href="#heading-wrap-up"><strong>📋</strong> Wrap-up</a></li>
</ol>
<h2 id="heading-what-are-threejs-and-blender">💭 What are Three.js and Blender?</h2>
<p>Three.js is a JavaScript library that functionas as an API, allowing you to exhibit 3D models within web browsers. </p>
<p>Leveraging Three.js helps you seamlessly integrate interactivity and distinctive functionalities into your website. </p>
<p>Blender is a robust software tailored for crafting and refining 3D models. Its versatility offers boundless opportunities, catering to a wide spectrum of creative visions.</p>
<p>Beyond its display capabilities, Blender provides you with an array of tools encompassing cameras, lighting, and even post-production enhancements.</p>
<p>When used together, these tools facilitate boundless creativity, allowing you to seamlessly translate your artistic creations into your upcoming website project.</p>
<h2 id="heading-how-to-set-up-reactjs-with-threejs">🔧 How to Set Up React.js with Three.js</h2>
<p>To start the process, install the React.js application:</p>
<p><code>npx create-react-app my-app</code></p>
<p>Next, we'll install Three.js and <a target="_blank" href="https://docs.pmnd.rs/react-three-fiber/getting-started/installation">React Three Fiber</a>. React Three Fiber serves as a React renderer for Three.js, harnessing the power of React components to streamline Three.js integration within a React.js environment:</p>
<p><code>npm install three @react-three/fiber</code></p>
<p>For an enriched Three.js experience, we'll also integrate <a target="_blank" href="https://www.npmjs.com/package/@react-three/drei">React Three Drei</a>, a package that introduces an assortment of helpers for diverse Three.js scenarios, including several camera controls, for example:</p>
<p><code>npm install @react-three/drei</code></p>
<h3 id="heading-gltf-tools-extension">glTF Tools extension</h3>
<p>I also recommend installing the <strong>glTF Tools</strong> extension. Although not strictly necessary, this extension can help you perform various tasks. </p>
<p>If you're using Visual Studio Code as your Integrated Development Environment (IDE), you can conveniently add the extension through the extensions tab. Again, this extension is optional, but it can significantly simplify certain processes later on. I will use it throughout this tutorial:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/React1.0.PNG" alt="Image" width="600" height="400" loading="lazy">
<em><strong>gltf Tools</strong> extension in Visual Studio Code</em></p>
<h3 id="heading-completed-setup-for-threejs-in-reactjs">Completed setup for Three.js in React.js</h3>
<p>The dependencies in the <code>package.json</code> file of our React.js application now appear as follows:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"dependencies"</span>: {
    <span class="hljs-string">"@react-three/drei"</span>: <span class="hljs-string">"^9.80.2"</span>,
    <span class="hljs-string">"@react-three/fiber"</span>: <span class="hljs-string">"^8.13.6"</span>,
    <span class="hljs-string">"@testing-library/jest-dom"</span>: <span class="hljs-string">"^5.17.0"</span>,
    <span class="hljs-string">"@testing-library/react"</span>: <span class="hljs-string">"^13.4.0"</span>,
    <span class="hljs-string">"@testing-library/user-event"</span>: <span class="hljs-string">"^13.5.0"</span>,
    <span class="hljs-string">"react"</span>: <span class="hljs-string">"^18.2.0"</span>,
    <span class="hljs-string">"react-dom"</span>: <span class="hljs-string">"^18.2.0"</span>,
    <span class="hljs-string">"react-scripts"</span>: <span class="hljs-string">"5.0.1"</span>,
    <span class="hljs-string">"three"</span>: <span class="hljs-string">"^0.155.0"</span>,
    <span class="hljs-string">"web-vitals"</span>: <span class="hljs-string">"^2.1.4"</span>
  },
</code></pre>
<p>These dependencies are sufficient for accomplishing a variety of tasks with Three.js in a React.js environment. Of course, you can incorporate any additional libraries you may desire for purposes beyond Three.js integration.</p>
<p>In addition to this, I have also made the code from this tutorial available on <a target="_blank" href="https://github.com/Matthes-Baer/blender-threejs-reactjs-article-app">GitHub</a>. This will allow you to quickly access the information without having to scroll through the entire article.</p>
<h2 id="heading-how-to-create-a-blender-model">🔨 How to Create a Blender Model</h2>
<p>To begin, our initial task involves creating a Blender model that will then be integrated into our React.js application. For this stage, let's consider a scene in the <strong>Layout</strong> tab where we've got three objects: two spheres and one plane. You can add such objects with the <code>Shift + A</code> shortcut in Blender.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/blenderFirstImage.PNG" alt="Image" width="600" height="400" loading="lazy">
<em>Blender scene with two spheres and one plane in the <strong>Layout</strong> tab</em></p>
<p>This composition includes just a plane and two spheres, with no additional details. Of course, you can work on more elaborate scene and model designs according to your preferences. </p>
<p>But for the purpose of illustrating the fundamental process of incorporating your custom Blender models into React.js, this basic example will serve us just fine.</p>
<h3 id="heading-how-to-add-animations-to-the-model">How to add animations to the model</h3>
<p>Now, our focus shifts to introducing basic animations to all three objects within this Blender scene. These animations can facilitate movement, rotation, or even adjustments in scale for the objects, enabling dynamic transformations.</p>
<p>In order to add animations in Blender for your objects, you can switch to the <strong>Animation</strong> tab, next to the <strong>Shading</strong> and <strong>Rendering</strong> tab.</p>
<p>In the Animation tab, you can add points to a certain frame. For instance, if you want to shift a sphere a bit to the left, begin by adding a starting keyframe (right-click on the object, choose "Insert Keyframe," then pick "Location"). </p>
<p>Afterward, move ahead a few frames on the object's animation timeline, adjust the object's position, and repeat the same process. This way, you'll have two keyframes: the initial one and the new position.</p>
<p>Remember, this motion is in one direction. If you want to repeat the animation, it will move to the new location and then return to its initial position with a jump. </p>
<p>To make the movement smoother, you can copy the initial keyframe and insert it at the end. This will make the object move back with a smooth motion after reaching the new location. This is also how I set up the keyframes in our Blender model.</p>
<p>Of course, you can add more keyframes to make more complex animations. This is just a basic introduction to starting with Blender animations. Like many aspects of Blender, there's a lot more to explore and learn.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/blenderSecondImage.PNG" alt="Image" width="600" height="400" loading="lazy">
<em>Adding animations to all three objects in the <strong>Animation</strong> tab</em></p>
<p>In this context, it's not necessary to have a thorough understanding of the specifics of these animations we added here. So, you don't really need to know to which exact position the first sphere is being moved through the animation. </p>
<p>The key point is to acknowledge their presence, as they will be integrated into our React.js application at a later stage so we can activate them in the browser.</p>
<h3 id="heading-how-to-add-colors">How to add colors</h3>
<p>Moving forward, we'll add some simple colors for the small sphere and the underlying plane, which you can do within the <strong>Shading</strong> tab, for example.</p>
<p>For basic colors, you can also go to the <strong>Material Properties</strong> section of the object (right-click on the object, then choose the second-to-last category at the bottom). But I want to focus on a specific situation you might encounter with your models later on. Therefore, I'll exclusively use the <strong>Shading</strong> tab for setting object colors in this tutorial.</p>
<p>In the <strong>Shading</strong> tab, you can add nodes at the bottom of the screen. These nodes can modify the color and texture of an object, among other things. You'll also find <code>Vector</code> and <code>Shader</code> nodes that, when combined, can create unique visuals for your objects. </p>
<p>All these adjustments apply to a specific material. So, if you want the same visual for different objects, you can simply apply the same material to them.</p>
<p>The <code>Principled BSDF</code> and <code>Material Output</code> nodes are initially generated when we open the <strong>Shading</strong> tab to look up on of our object's material for the first time. Both nodes are pretty much the basic case. </p>
<p>The <code>Principled BSDF</code> has a lot of settings you can play around with. In our case we just want to change the <code>Base Color</code> property to a blue color.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/blender3.0.PNG" alt="Image" width="600" height="400" loading="lazy">
<em>Material of one sphere where we just adjust the <code>Base Color</code> within the <code>Principled BSDF</code> node</em></p>
<p>For the larger sphere, a similar material application is used. But, in contrast to the <code>Principled BSDF</code> node, we'll use the <code>Glossy BSDF</code> node which is such a node from the <code>Shader</code> category. This will help us recognize a possible issue that you might come across when designing a Blender model for your React.js application – which you will see later on.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/blender3.2-1.PNG" alt="Image" width="600" height="400" loading="lazy">
<em>Using the <code>Glossy BSDF</code> node to add a material to the large sphere</em></p>
<p>Once we've done this, we're ready to export our Blender model. Note that this version is considerably simplified. You can work on more detailed model designs tailored to your preferences. Still, the overall workflow remains similar.</p>
<h3 id="heading-how-to-export-the-model">How to export the model</h3>
<p>To export the model, we need to generate a <code>.glb/.gltf</code> file. This is crucial as Three.js expects particular file formats for compatibility, and in this instance, a <code>.glb</code> or <code>.gltf</code> file aligns with the library's requirements.</p>
<p>So, once you've finished creating your model with objects, animations, colors, and more, you can do the following:</p>
<ol>
<li>Click on the <strong>File</strong> tab located at the top left corner.</li>
<li>Choose <strong>Export</strong> from the options that appear. Now, a variety of export formats will be shown.</li>
<li>If you plan to use your model with Three.js in your application, you need to pick the <code>glTF 2.0 (.glb/.gltf)</code> option, like I mentioned earlier.</li>
</ol>
<p>After selecting this option, a new window will pop up. This window lets you pick the folder where you want to save your file. </p>
<p>On the right side of this window, there are additional choices. You can decide which specific objects you want to export, for instance. In most situations, the default settings should work well. Just remember that you can adjust these settings to your liking if necessary.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/blender3.1-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Remember to export with the <code>glTF 2.0 (.glb/.gltf)</code> format.</em></p>
<h3 id="heading-how-to-visualize-the-exported-model">How to visualize the exported model</h3>
<p>Next, let's transition to Visual Studio Code and navigate to the folder where we've stored our exported file. </p>
<p>Within this directory, you'll find a <code>.glb</code> file. Referring back to the <strong>glTF Tools</strong> extension setup from earlier, you can simply right-click on the <code>.glb</code> file in order to find two additional options positioned at the bottom, called <code>glTF: Import from GLB</code> and <code>glTF: Validate a GLB or GLTF file</code>.</p>
<p>In this scenario, we'll opt for the <code>glTF: Import from GLB</code> option. This action will generate a <code>.gltf</code> file in the same folder, in our case <code>blenderFile.gltf</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/blender4.0.png" alt="Image" width="600" height="400" loading="lazy">
<em>Generating a <code>.gltf</code> file from the original <code>.glb</code> file we exported in Blender with the <strong>glTF Tools</strong> extension</em></p>
<p>We've chosen this approach to bring enhanced accessibility to the <code>.gltf</code> file, enabling direct viewing within Visual Studio Code through the <strong>glTF Tools</strong> extension. This can be quite helpful to check on your file prior to its actual implementation.</p>
<p>If we access the newly created <code>.gltf</code> file, we can observe a bunch of information based on the Blender model. It's important to note that the specifics could differ in your case, as they're tailored to reflect the attributes of the objects and scenes within your Blender project.</p>
<p>If we look at the upper-right corner, there is a symbol that looks like a cube with a cone next to it. By clicking on this symbol, you can seamlessly preview your Blender scene directly within your IDE. This functionality is exclusively accessible for the <code>.gltf</code> file and not applicable to the <code>.glb</code> file in this case.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/blender4.5.png" alt="Image" width="600" height="400" loading="lazy">
<em>The newly created <code>.gltf</code> file with the option to view the model directly in Visual Studio Code (in the upper-right corner, circled in red)</em></p>
<p>It's worth noting that you don't have to do this through the <strong>glTF Tools</strong> extension. Alternatively, various websites allow you to upload your file for visualization. But I've personally found this in-IDE approach to be especially convenient. It centralizes the process, enabling you to assess your file's integrity before actually implementing it. </p>
<p>If you find any errors, this practice lets you preemptively find out whether the issue is based on a problematic file export or just an implementation oversight within your React.js application. Consequently, I wholeheartedly recommend evaluating your model file following its export from Blender.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/blender5.0.PNG" alt="Image" width="600" height="400" loading="lazy">
<em>Viewing the Blender model with <strong>glTF Tools</strong> in Visual Studio Code</em></p>
<p>By using the <strong>glTF Tools</strong> extension to view our Blender model in Visual Studio Code, we can see that all three objects are correctly recognized. Both the small sphere and the plane are shown in their intended colors.</p>
<p>But the large sphere doesn't have the expected color assigned and just appears with a default white color instead. </p>
<p>This discrepancy raises the question: what led to this anomaly? It's circumstances like this that demonstrate how useful it is to preview your model before integrating it into your React.js application.</p>
<p>By scrutinizing your model at this stage, you can affirm that the issue originates from the Blender model itself rather than the implementation process, given that we haven't done any implementation yet. </p>
<p>This pre-implementation assessment proves to be handy and enables you to diagnose and address potential complications before proceeding with the implementation process in React.js.</p>
<h2 id="heading-texture-baking-for-procedural-materials">✏️ Texture Baking for Procedural Materials</h2>
<p>In a nutshell, Blender provides the flexibility to employ procedural nodes for your materials. While these nodes function seamlessly within Blender, they are not directly compatible with other game engines or software frameworks such as Three.js. </p>
<p>To learn more, consider watching the following video. In just 10 minutes, it demonstrates the process of texture baking, which effectively resolves the issue at hand. </p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/AioskAgcU2U" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>Personally, when confronted with this challenge and initially uncertain about its nature, I found this video to be a valuable resource for gaining deeper insights into the subject matter.</p>
<p>In our specific scenario, while we might not encounter as complex a situation as seen in the video, we are still faced with the use of nodes that lack direct compatibility with various software tools.</p>
<p>Next, we'll briefly walk through the steps mentioned in the video. However, if you're interested in delving deeper into this process, I highly recommend watching the video.</p>
<h3 id="heading-how-to-create-an-image-texture-node">How to create an image texture node</h3>
<p>To start, in the <strong>Shading</strong> tab for the material containing the <code>Glossy BSDF</code> node, we'll introduce an <code>Image Texture</code> node and connect it to a new image (by click on <code>New</code>). </p>
<p>We'll leave the settings at their default values, which means a width and height of <code>1024px</code>. Using larger values would considerably extend the processing time we're going to face. Still, it's important to note that a larger texture can offer more detail and an overall improved appearance. </p>
<p>In our current situation, we're aiming for a quick process. But for more significant projects, visual quality might be crucial. In such cases, opting for a higher resolution could be desirable.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/blender6.0-1.PNG" alt="Image" width="600" height="400" loading="lazy">
<em>Creating an <code>Image Texture</code> node and assigning a new image to it with default settings</em></p>
<h3 id="heading-how-to-apply-the-smart-uv-project-process">How to apply the Smart UV Project process</h3>
<p>Next, we need to employ the <code>Smart UV Project</code> option located in the <strong>UV Editing</strong> tab. Essentially, this action unwraps the faces of the particular object onto a texture. </p>
<p>This process enables us to specify which parts of the texture should be colored and modified as soon as we are back in the <strong>Shading</strong> tab. To make this process effective, we must select all the faces of the large sphere.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/blender7.0.png" alt="Image" width="600" height="400" loading="lazy">
<em>Selecting all faces of the object in the <strong>UV Editing</strong> tab and applying <code>Smart UV Project</code> on it</em></p>
<p>Once we've finished this step and utilized the default settings for the <code>Smart UV Project</code> procedure, the image on the left —previously featuring a grid— will now display the shapes of the sphere we applied this process to. In our situation, it seems like the texture captured various angles of our sphere.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/blender8.0.PNG" alt="Image" width="600" height="400" loading="lazy">
<em>The texture after <code>Smart UV Project</code></em></p>
<p>Depending on the specific object, you may need to fine-tune the settings presented after clicking the <code>Smart UV Project</code> button. If you encounter challenges with a particular object, the video I shared earlier can give you additional guidance on this aspect.</p>
<p>Generally, to mitigate issues, you should optimize your object layout during its creation phase. Avoiding the introduction of excessive edges in specific locations can prevent problems like clipping, for instance.</p>
<h3 id="heading-the-bake-process">The Bake process</h3>
<p>Now, let's return to the <strong>Shading</strong> tab, where we'll access the <code>Render Properties</code> on the right side (represented by the small screen or TV symbol). If not already selected, pick <code>Cycles</code> as your <code>Render Engine</code>. Then navigate to the <code>Bake</code> category, which is located below the <code>Performance</code> category.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/blender9.0-1.PNG" alt="Image" width="600" height="400" loading="lazy">
<em><code>Bake</code> option in the <strong>Shading</strong> tab within the <code>Render Properties</code></em></p>
<p>With the existing default settings, you can proceed by clicking the <code>Bake</code> button while ensuring that both the <code>Image Texture</code> node and the large sphere are selected. </p>
<p>Keep in mind that I integrated a <code>Sun</code> light into my scene, as this bake process takes the scene's lighting into account. Without sufficient lighting, the result might appear excessively dark.</p>
<p>After a period of processing (which might be more time-consuming if you've employed larger dimensions for the <code>Image Texture</code> node's image), the baking process will finish. This results in the texture being applied to the image from the <code>Image Texture</code>. Instead of obtaining the texture from the <code>Shader</code> node named <code>Glossy BSDF</code>, we now have access to it through a regular "normal" image texture.</p>
<p>Then we can establish a connection from the <code>Image Texture</code> node to the <code>Material Output</code> node, thereby successfully implementing our material. At this stage, there isn't a significant difference compared to the previous method where we had the <code>Principled BSDF</code> node connected to the <code>Surface</code> input of the <code>Material Output</code> node.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/blender10.0.PNG" alt="Image" width="600" height="400" loading="lazy">
<em><code>Image Texture</code> node with the "baked" texture is connected with the <code>Material Output</code> node instead of the <code>Glossy BSDF</code> node</em></p>
<h3 id="heading-how-to-see-the-final-result">How to see the final result</h3>
<p>Now, we can export the file again, repeat the same process from before in our IDE with <strong>glTF Tools</strong> and view the <code>.gltf</code> file with the extension. Upon examining the outcome, you might notice that it's not an exact match to the version we had using the <code>Glossy BSDF</code> node in Blender. This difference can be primarily attributed to the lighting conditions in the Blender scene.</p>
<p>Bear in mind that the approach I've outlined isn't the typical usage for the baking process, since in this case you could also just have picked a similar base color with the <code>Principled BSDF</code> node and would achieve pretty much the same solution, for example.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/blender11.0.PNG" alt="Image" width="600" height="400" loading="lazy">
<em>Finalized view with <strong>glTF Tools</strong>, including the "baked" texture for the large sphere</em></p>
<p>I introduced the baking process based on personal experience. There were instances where I encountered a scenario where materials appeared differently in Blender compared to when implemented them in a React.js application with Three.js. This situation prompted me to explore the concept of baking, which turned out to be a helpful solution.</p>
<p>To summarize, if you find yourself facing a scenario where your materials don't appear as expected in your React.js application with Three.js, considering the baking process and researching this topic can provide valuable insights. This can be particularly beneficial for people who are new to Blender.</p>
<h2 id="heading-how-to-implement-the-blender-model-in-the-reactjs-application">✒️ How to Implement the Blender model in the React.js Application</h2>
<p>To implement the Blender file, we can use a really useful shortcut (source: <a target="_blank" href="https://github.com/pmndrs/gltfjsx">https://github.com/pmndrs/gltfjsx</a>):</p>
<p><code>npx gltfjsx public/blenderFileName.glb</code></p>
<p>It's important to note that you need to store your Blender file within the <code>public</code> folder of your React.js application for this step. It's also worth highlighting that you need React Three Drei to use this helper. So in our case, we can directly use this shortcut without the need for any additional preparations.</p>
<p>Upon executing this shortcut, we are presented with the following file:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
Command: npx gltfjsx@6.1.4 public/blenderStuff/blenderFile.glb
*/</span>

<span class="hljs-keyword">import</span> { useLayoutEffect, useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { useGLTF, useAnimations } <span class="hljs-keyword">from</span> <span class="hljs-string">"@react-three/drei"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Model</span>(<span class="hljs-params">props</span>) </span>{
  <span class="hljs-keyword">const</span> group = useRef();
  <span class="hljs-keyword">const</span> { nodes, materials, animations } = useGLTF(
    <span class="hljs-string">"./blenderStuff/blenderFile.glb"</span>
  );
  <span class="hljs-keyword">const</span> { actions } = useAnimations(animations, group);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">group</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{group}</span> {<span class="hljs-attr">...props</span>} <span class="hljs-attr">dispose</span>=<span class="hljs-string">{null}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">group</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"Scene"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">mesh</span>
          <span class="hljs-attr">name</span>=<span class="hljs-string">"Cube"</span>
          <span class="hljs-attr">geometry</span>=<span class="hljs-string">{nodes.Cube.geometry}</span>
          <span class="hljs-attr">material</span>=<span class="hljs-string">{materials.Material}</span>
          <span class="hljs-attr">position</span>=<span class="hljs-string">{[-0.07,</span> <span class="hljs-attr">0.16</span>, <span class="hljs-attr">-0.27</span>]}
          <span class="hljs-attr">scale</span>=<span class="hljs-string">{[1,</span> <span class="hljs-attr">0.03</span>, <span class="hljs-attr">1</span>]}
        /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">mesh</span>
          <span class="hljs-attr">name</span>=<span class="hljs-string">"Sphere"</span>
          <span class="hljs-attr">geometry</span>=<span class="hljs-string">{nodes.Sphere.geometry}</span>
          <span class="hljs-attr">material</span>=<span class="hljs-string">{materials[</span>"<span class="hljs-attr">Material.002</span>"]}
          <span class="hljs-attr">position</span>=<span class="hljs-string">{[-0.62,</span> <span class="hljs-attr">0.43</span>, <span class="hljs-attr">-0.79</span>]}
          <span class="hljs-attr">rotation</span>=<span class="hljs-string">{[-0.01,</span> <span class="hljs-attr">0.11</span>, <span class="hljs-attr">-0.02</span>]}
          <span class="hljs-attr">scale</span>=<span class="hljs-string">{0.09}</span>
        /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">mesh</span>
          <span class="hljs-attr">name</span>=<span class="hljs-string">"Sphere001"</span>
          <span class="hljs-attr">geometry</span>=<span class="hljs-string">{nodes.Sphere001.geometry}</span>
          <span class="hljs-attr">material</span>=<span class="hljs-string">{materials[</span>"<span class="hljs-attr">Material.001</span>"]}
          <span class="hljs-attr">position</span>=<span class="hljs-string">{[0.4,</span> <span class="hljs-attr">0.55</span>, <span class="hljs-attr">0.15</span>]}
          <span class="hljs-attr">scale</span>=<span class="hljs-string">{0.41}</span>
        /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">group</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">group</span>&gt;</span></span>
  );
}

useGLTF.preload(<span class="hljs-string">"./blenderStuff/blenderFile.glb"</span>);
</code></pre>
<p>At first glance, you can see that this process has added many elements, so we basically don't need to add much on our own.</p>
<p>An important aspect to configure is the path within the <code>useGLTF</code> hook. In my instance, the accurate path to incorporate is <code>./blenderStuff/blenderFile.glb</code> (this applies to <code>useGLTF.preload()</code> as well). This is because I created a sub-folder named <code>blenderStuff</code> within my <code>public</code> directory.</p>
<h3 id="heading-how-to-add-a-canvas-wrapper-and-other-components">How to add a Canvas wrapper and other components</h3>
<p>With this configuration in place, we're now ready to use the <code>Model</code> component. But to effectively integrate this <code>Model</code> component into our desired location, we need to make some adjustments in the parent component. </p>
<p>In my case, I've opted to implement it within the main <code>App.js</code> file. And I've assigned a height of <code>100vh</code> to the <code>App</code>'s CSS class to ensure the desired display.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;
<span class="hljs-keyword">import</span> { Model } <span class="hljs-keyword">from</span> <span class="hljs-string">"./BlenderFile"</span>;
<span class="hljs-keyword">import</span> { Canvas } <span class="hljs-keyword">from</span> <span class="hljs-string">"@react-three/fiber"</span>;
<span class="hljs-keyword">import</span> { OrbitControls } <span class="hljs-keyword">from</span> <span class="hljs-string">"@react-three/drei"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Canvas</span> <span class="hljs-attr">camera</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">fov:</span> <span class="hljs-attr">64</span>, <span class="hljs-attr">position:</span> [<span class="hljs-attr">-2</span>, <span class="hljs-attr">2</span>, <span class="hljs-attr">0</span>] }}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ambientLight</span> <span class="hljs-attr">intensity</span>=<span class="hljs-string">{5}</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">OrbitControls</span> <span class="hljs-attr">enableZoom</span>=<span class="hljs-string">{true}</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Model</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Canvas</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Generally speaking, you'll need a component to encapsulate all the Three.js related elements. Within the <code>Canvas</code> component, there's an opportunity to configure various settings. In my specific instance, I'm adjusting the initial camera position.</p>
<p>The light for the component plays a crucial role. In our case we made use of <code>ambientLight</code> which will add a light to the whole scene. Without adequate lighting, your scene might appear exceedingly dark or even entirely black despite the presence of object colors. You can also use additional light sources like the <code>spotLight</code> component.</p>
<p>The <code>OrbitControls</code> component, accessible from the Drei helper library, enhances your interactivity by enabling scrolling and rotation within the model right within the browser. This single line of code substantially improves user interactivity options.</p>
<p>Remember that your <code>Canvas</code> component can accommodate multiple models. You can also selectively apply components like <code>OrbitControls</code> to specific Blender models, thereby tailoring their behavior. </p>
<p>To do this, you'll need to build a parent component for each scene you want to make to be integrated within the <code>Canvas</code>. Within each new parent component, incorporate your Blender model component, along with any supplementary helper components you want to add. </p>
<p>This approach proves particularly advantageous when distinct models require different lighting or unique camera positions, for example.</p>
<h3 id="heading-how-to-implement-the-animations">How to implement the animations</h3>
<p>At this point, we've established a functional Three.js <code>Canvas</code> environment, featuring our Blender model. But it's important to remember that we've also introduced basic animations, which are not yet operational.</p>
<p>To tackle this, we can leverage the pre-implemented <code>useAnimations</code> hook.</p>
<pre><code class="lang-javascript">  <span class="hljs-keyword">const</span> { actions, names } = useAnimations(animations, group);

  useLayoutEffect(<span class="hljs-function">() =&gt;</span> {
    names.forEach(<span class="hljs-function">(<span class="hljs-params">animation</span>) =&gt;</span> {
      actions?.[animation]?.play();
    });
  }, [actions, names]);
</code></pre>
<p>By incorporating this implementation, all animations associated with this Blender model will start playing upon the rendering of the page. This behavior also includes an indefinite loop for each animation.</p>
<h2 id="heading-additional-information">📄 Additional Information</h2>
<p>While this tutorial primarily focused on integrating a Blender model into a React.js application using Three.js, there's a realm of untapped potential within Three.js that we didn't cover.</p>
<p>Although we didn't use it in this basic example, you can introduce Post Processing to your Three.js models within React.js. The <a target="_blank" href="https://www.npmjs.com/package/@react-three/postprocessing">React Three Postprocessing</a> library serves as a valuable tool in this regard. It lets you elevate your Three.js scenes with sophisticated effects like Bloom or Noise effects, which can add a more advanced dimension to your visualizations.</p>
<p>Also, when working on future Three.js projects, consider exploring the <a target="_blank" href="https://docs.pmnd.rs/react-three-fiber/tutorials/using-with-react-spring">React Spring</a> library which integrates well with React Three Fiber. React Spring provides the opportunity to incorporate custom animations within your Three.js scenes, on top of any animations directly integrated within Blender. </p>
<p>For instance, you could make a specific object within your scene get larger or smaller upon clicking it. As with other aspects of Three.js, this aspect might enhance interactivity and might be worth your time to get into.</p>
<p>By the way, if you find that your frames are running at a lower rate, consider toggling Hardware Acceleration within your browser settings to potentially improve performance.</p>
<h2 id="heading-wrap-up">📋 Wrap-up</h2>
<p>At this point, we've successfully crafted a Blender model with animations and materials. Afterwards we integrated it into our React.js application using React Three Fiber.</p>
<p>Although the example we looked at here was quite basic, the integration approach remains the same for more complex Blender models. The fundamental functions of Three.js can be combined with supplementary helpers to enhance your scenes. </p>
<p>In addition to Post Processing, additional animations or also specific Blender materials, aspects like cameras and lights often are the most important when aiming to enhance the visual impact of your Blender models within Three.js scenes.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Create a Squid Game JavaScript Game with Three.js ]]>
                </title>
                <description>
                    <![CDATA[ Would you survive the TV show Squid Game? Would you survive coding the TV show Squid Game? Time to find out. We just published a JavaScript course on the freeCodeCamp.org YouTube channel that will teach you how to create the Red Light / Green Light g... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-a-squid-game-javascript-game-with-three-js/</link>
                <guid isPermaLink="false">66b201ad08bc664c3c097e6a</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ three.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Tue, 09 Nov 2021 17:42:24 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/11/squidgame.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Would you survive the TV show Squid Game? Would you survive coding the TV show Squid Game?</p>
<p>Time to find out.</p>
<p>We just published a JavaScript course on the freeCodeCamp.org YouTube channel that will teach you how to create the Red Light / Green Light game from Squid Game.</p>
<p>You will use Three.js and you will learn how to use a 3d model in a JavaScript game.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/11/squid-game-red-light-green-light.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Don't get caught.</em></p>
<p>Shuvo from the Angle Brace YouTube channel developed this course. He has created a lot of fun JavaScript tutorials on his channel.</p>
<p>Here are the sections in this course:</p>
<ul>
<li>Setup</li>
<li>Three.js Basics</li>
<li>Loading 3D Model</li>
<li>Doll Class</li>
<li>GSAP Animation</li>
<li>Creating Track</li>
<li>Player Class</li>
<li>Keypress Handling</li>
<li>Functioning the Doll</li>
<li>Game Logic</li>
</ul>
<p>Watch the full course below or <a target="_blank" href="https://youtu.be/4HSxX-EKJjw">on the freeCodeCamp.org YouTube channel</a> (1-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/4HSxX-EKJjw" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Three.js Tutorial - How to Build a Simple Car with Texture in 3D ]]>
                </title>
                <description>
                    <![CDATA[ Putting together a 3D scene in the browser with Three.js is like playing with Legos. We put together some boxes, add lights, define a camera, and Three.js renders the 3D image. In this tutorial, we're going to put together a minimalistic car from box... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/three-js-tutorial/</link>
                <guid isPermaLink="false">66c4c820e7521bfd6862b3b4</guid>
                
                    <category>
                        <![CDATA[ Game Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ three.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hunor Márton Borbély ]]>
                </dc:creator>
                <pubDate>Mon, 22 Mar 2021 13:47:23 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/03/Screenshot-2021-03-19-at-11.31.27-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Putting together a 3D scene in the browser with Three.js is like playing with Legos. We put together some boxes, add lights, define a camera, and Three.js renders the 3D image.</p>
<p>In this tutorial, we're going to put together a minimalistic car from boxes and learn how to map texture onto it. </p>
<p>First, we'll set things up – we'll define the lights, the camera, and the renderer. Then we'll learn how to define geometries and materials to create 3D objects. And finally we are going to code textures with JavaScript and HTML Canvas.</p>
<h2 id="heading-how-to-setup-the-threejs-project">How to Setup the Three.js Project</h2>
<p>Three.js is an external library, so first we need to add it to our project. I used NPM to install it to my project then imported it at the beginning of the JavaScript file.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>; 

<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();

. . .
</code></pre>
<p>First, we need to define the scene. The scene is a container that contains all the 3D objects we want to display along with the lights. We are about to add a car to this scene, but first let's set up the lights, the camera, and the renderer.</p>
<h3 id="heading-how-to-set-up-the-lights">How to Set Up the Lights</h3>
<p>We'll add two lights to the scene: an ambient light and a directional light. We define both by setting a color and an intensity. </p>
<p>The color is defined as a hex value. In this case we set it to white. The intensity is a number between 0 and 1, and as both of them shine simultaneously we want these values somewhere around 0.5. </p>
<pre><code class="lang-javascript">. . . 

const ambientLight = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.6</span>);
scene.add(ambientLight);

<span class="hljs-keyword">const</span> directionalLight = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.8</span>);
directionalLight.position.set(<span class="hljs-number">200</span>, <span class="hljs-number">500</span>, <span class="hljs-number">300</span>);
scene.add(directionalLight); 

. . .
</code></pre>
<p>The ambient light is shining from every direction, giving a base color for our geometry while the directional light simulates the sun. </p>
<p>The directional light shines from very far away with parallel light rays. We set a position for this light that defines the direction of these light rays. </p>
<p>This position can be a bit confusing so let me explain. Out of all the parallel rays we define one in particular. This specific light ray will shine from the position we define (200,500,300) to the 0,0,0 coordinate. The rest will be in parallel to it. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Loop-driver.015.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>As the light rays are in parallel, and they shine from very far away, the exact coordinates don't matter here – rather, their proportions do. </p>
<p>The three position-parameters are the X, Y, and Z coordinates. By default, the Y-axis points upwards, and as it has the highest value (500), that means the top of our car receives the most light. So it will be the brightest. </p>
<p>The other two values define by how much the light is bent along the X and Z axis, that is how much light the front and the side of the car will receive. </p>
<h3 id="heading-how-to-set-up-the-camera">How to Set Up the Camera</h3>
<p>Next, let's set up the camera that defines how we look at this scene. </p>
<p>There are two options here – perspective cameras and orthographic cameras. Video games mostly use perspective cameras, but we are going to use an orthographic one to have a more minimal, geometric look.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Loop-driver.018.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In <a target="_blank" href="https://www.freecodecamp.org/news/render-3d-objects-in-browser-drawing-a-box-with-threejs/">my previous article</a>, we discussed the differences between the two cameras in more detail. Therefore in this one, we'll only discuss how to set up an orthographic camera. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Loop-driver.019.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>For the camera, we need to define a view frustum. This is the region in the 3D space that is going to be projected to the screen. </p>
<p>In the case of an orthographic camera, this is a box. The camera projects the 3D objects inside this box toward one of its sides. Because each projection line is in parallel, orthographic cameras don't distort geometries.</p>
<pre><code class="lang-javascript">. . .

<span class="hljs-comment">// Setting up camera</span>
<span class="hljs-keyword">const</span> aspectRatio = <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight;
<span class="hljs-keyword">const</span> cameraWidth = <span class="hljs-number">150</span>;
<span class="hljs-keyword">const</span> cameraHeight = cameraWidth / aspectRatio;

<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.OrthographicCamera(
  cameraWidth / <span class="hljs-number">-2</span>, <span class="hljs-comment">// left</span>
  cameraWidth / <span class="hljs-number">2</span>, <span class="hljs-comment">// right</span>
  cameraHeight / <span class="hljs-number">2</span>, <span class="hljs-comment">// top</span>
  cameraHeight / <span class="hljs-number">-2</span>, <span class="hljs-comment">// bottom</span>
  <span class="hljs-number">0</span>, <span class="hljs-comment">// near plane</span>
  <span class="hljs-number">1000</span> <span class="hljs-comment">// far plane</span>
);
camera.position.set(<span class="hljs-number">200</span>, <span class="hljs-number">200</span>, <span class="hljs-number">200</span>);
camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">10</span>, <span class="hljs-number">0</span>);

. . .
</code></pre>
<p>To set up an orthographic camera, we have to define how far each side of the frustum is from the viewpoint. We define that the left side is 75 units away to the left, the right plane is 75 units away to the right, and so on. </p>
<p>Here these units don't represent screen pixels. The size of the rendered image will be defined at the renderer. Here these values have an arbitrary unit that we use in the 3D space. Later on, when defining 3D objects in the 3D space, we are going to use the same units to set their size and position. </p>
<p>Once we define a camera we also need to position it and turn it in a direction. We are moving the camera by 200 units in each dimension, then we set it to look back towards the 0,10,0 coordinate. This is almost at the origin. We look towards a point slightly above the ground, where our car's center will be. </p>
<h3 id="heading-how-to-set-up-the-renderer">How to Set Up the Renderer</h3>
<p>The last piece we need to set up is a renderer that renders the scene according to our camera into our browser. We define a WebGLRenderer like this:</p>
<pre><code class="lang-js">. . .

<span class="hljs-comment">// Set up renderer</span>
<span class="hljs-keyword">const</span> renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer({ <span class="hljs-attr">antialias</span>: <span class="hljs-literal">true</span> });
renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);
renderer.render(scene, camera);

<span class="hljs-built_in">document</span>.body.appendChild(renderer.domElement);
</code></pre>
<p>Here we also set up the size of the canvas. This is the only place where we set the size in pixels since we're setting how it should appear in the browser. If we want to fill the whole browser window, we pass on the window's size. </p>
<p>And finally, the last line adds this rendered image to our HTML document. It creates an HTML Canvas element to display the rendered image and adds it to the DOM. </p>
<h2 id="heading-how-to-build-the-car-in-threejs">How to Build the Car in Three.js</h2>
<p>Now let's see how can we can compose a car. First, we will create a car without texture. It is going to be a minimalistic design – we'll just put together four boxes.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Screenshot-2021-03-19-at-11.18.04.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-add-a-box">How to Add a Box</h3>
<p>First, we create a pair of wheels. We will define a gray box that represents both a left and a right wheel. As we never see the car from below, we won't notice that instead of having a separate left and right wheel we only have one big box. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Screenshot-2021-03-19-at-11.01.43.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We are going to need a pair of wheels both in the front and at the back of the car, so we can create a reusable function.</p>
<pre><code class="lang-js">. . . 

function createWheels() {
  <span class="hljs-keyword">const</span> geometry = <span class="hljs-keyword">new</span> THREE.BoxBufferGeometry(<span class="hljs-number">12</span>, <span class="hljs-number">12</span>, <span class="hljs-number">33</span>);
  <span class="hljs-keyword">const</span> material = <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0x333333</span> });
  <span class="hljs-keyword">const</span> wheel = <span class="hljs-keyword">new</span> THREE.Mesh(geometry, material);
  <span class="hljs-keyword">return</span> wheel;
}

. . .
</code></pre>
<p>We define the wheel as a mesh. The mesh is a combination of a geometry and a material and it will represent our 3D object. </p>
<p>The geometry defines the shape of the object. In this case, we create a box by settings its dimensions along the X, Y, and Z-axis to be 12, 12, and 33 units. </p>
<p>Then we pass on a material that will define the appearance of our mesh. There are different material options. The main difference between them is how they react to light.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/materials.001.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In this tutorial, we'll use <code>MeshLambertMaterial</code>. The <code>MeshLambertMaterial</code> calculates the color for each vertex. In the case of drawing a box, that's basically each side. </p>
<p>We can see how that works, as each side of the box has a different shade. We defined a directional light to shine primarily from above, so the top of the box is the brightest.</p>
<p>Some other materials calculate the color, not only for each side but for each pixel within the side. They result in more realistic images for more complex shapes. But for boxes illuminated with directional light, they don't make much of a difference. </p>
<h3 id="heading-how-to-build-the-rest-of-the-car">How to Build the Rest of the Car</h3>
<p>Then in a similar way let's let's create the rest of the car. We define the <code>createCar</code> function that returns a Group. This group is another container like the scene. It can hold Three.js objects. It is convenient because if we want to move around the car, we can simply move around the Group. </p>
<pre><code class="lang-js">. . .

function createCar() {
  <span class="hljs-keyword">const</span> car = <span class="hljs-keyword">new</span> THREE.Group();

  <span class="hljs-keyword">const</span> backWheel = createWheels();
  backWheel.position.y = <span class="hljs-number">6</span>;
  backWheel.position.x = <span class="hljs-number">-18</span>;
  car.add(backWheel);

  <span class="hljs-keyword">const</span> frontWheel = createWheels();
  frontWheel.position.y = <span class="hljs-number">6</span>;  
  frontWheel.position.x = <span class="hljs-number">18</span>;
  car.add(frontWheel);

  <span class="hljs-keyword">const</span> main = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.BoxBufferGeometry(<span class="hljs-number">60</span>, <span class="hljs-number">15</span>, <span class="hljs-number">30</span>),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0x78b14b</span> })
  );
  main.position.y = <span class="hljs-number">12</span>;
  car.add(main);

  <span class="hljs-keyword">const</span> cabin = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.BoxBufferGeometry(<span class="hljs-number">33</span>, <span class="hljs-number">12</span>, <span class="hljs-number">24</span>),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0xffffff</span> })
  );
  cabin.position.x = <span class="hljs-number">-6</span>;
  cabin.position.y = <span class="hljs-number">25.5</span>;
  car.add(cabin);

  <span class="hljs-keyword">return</span> car;
}

<span class="hljs-keyword">const</span> car = createCar();
scene.add(car);

renderer.render(scene, camera);

. . .
</code></pre>
<p>We generate two pairs of wheels with our function, then define the main part of the car. Then we'll add the top of the cabin as the forth mesh. These are all just boxes with different dimensions and different colors.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/car-boxes.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>By default every geometry will be in the middle, and their centers will be at the 0,0,0 coordinate. </p>
<p>First, we raise them by adjusting their position along the Y-axis. We raise the wheels by half of their height – so instead of sinking in halfway to the ground, they lay on the ground. Then we also adjust the pieces along the X-axis to reach their final position. </p>
<p>We add these pieces to the car group, then add the whole group to the scene. It's important that we add the car to the scene before rendering the image, or we'll need to call rendering again once we've modified the scene. </p>
<h3 id="heading-how-to-add-texture-to-the-car">How to Add Texture to the Car</h3>
<p>Now that we have our very basic car model, let's add some textures to the cabin. We are going to paint the windows. We'll define a texture for the sides and one for the front and the back of the cabin. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/textures.001.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>When we set up the appearance of a mesh with a material, setting a color is not the only option. We can also map a texture. We can provide the same texture for every side or we can provide a material for each side in an array. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Loop-driver.038.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>As a texture, we could use an image. But instead of that, we are going to create textures with JavaScript. We are going to code images with HTML Canvas and JavaScript. </p>
<p>Before we continue, we need to make some distinctions between Three.js and HTML Canvas. </p>
<p>Three.js is a JavaScript library. It uses WebGL under the hood to render 3D objects into an image, and it displays the final result in a canvas element. </p>
<p>HTML Canvas, on the other hand, is an HTML element, just like the <code>div</code> element or the paragraph tag. What makes it special, though, is that we can draw shapes on this element with JavaScript. </p>
<p>This is how Three.js renders the scene in the browser, and this is how we are going to create textures. Let's see how they work. </p>
<h3 id="heading-how-to-draw-on-an-html-canvas">How to Draw on an HTML Canvas</h3>
<p>To draw on a canvas, first we need to create a canvas element. While we create an HTML element, this element will never be part of our HTML structure. On its own, it won't be displayed on the page. Instead, we will turn it into a Three.js texture. </p>
<p>Let's see how can we draw on this canvas. First, we define the width and height of the canvas. The size here doesn't define how big the canvas will appear, it's more like the resolution of the canvas. The texture will be stretched to the side of the box, regardless of its size.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getCarFrontTexture</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"canvas"</span>);
  canvas.width = <span class="hljs-number">64</span>;
  canvas.height = <span class="hljs-number">32</span>;
  <span class="hljs-keyword">const</span> context = canvas.getContext(<span class="hljs-string">"2d"</span>);

  context.fillStyle = <span class="hljs-string">"#ffffff"</span>;
  context.fillRect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">64</span>, <span class="hljs-number">32</span>);

  context.fillStyle = <span class="hljs-string">"#666666"</span>;
  context.fillRect(<span class="hljs-number">8</span>, <span class="hljs-number">8</span>, <span class="hljs-number">48</span>, <span class="hljs-number">24</span>);

  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> THREE.CanvasTexture(canvas);
}
</code></pre>
<p>Then we get the 2D drawing context. We can use this context to execute drawing commands. </p>
<p>First, we are going to fill the whole canvas with a white rectangle. To do so, first we set the fill style to be while. Then fill a rectangle by setting its top-left position and its size. When drawing on a canvas, by default the 0,0 coordinate will be at the top-left corner. </p>
<p>Then we fill another rectangle with a gray color. This one starts at the 8,8 coordinate and it doesn't fill the canvas, it only paints the windows. </p>
<p>And that's it – the last line turns the canvas element into a texture and returns it, so we can use it for our car. </p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getCarSideTexture</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"canvas"</span>);
  canvas.width = <span class="hljs-number">128</span>;
  canvas.height = <span class="hljs-number">32</span>;
  <span class="hljs-keyword">const</span> context = canvas.getContext(<span class="hljs-string">"2d"</span>);

  context.fillStyle = <span class="hljs-string">"#ffffff"</span>;
  context.fillRect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">128</span>, <span class="hljs-number">32</span>);

  context.fillStyle = <span class="hljs-string">"#666666"</span>;
  context.fillRect(<span class="hljs-number">10</span>, <span class="hljs-number">8</span>, <span class="hljs-number">38</span>, <span class="hljs-number">24</span>);
  context.fillRect(<span class="hljs-number">58</span>, <span class="hljs-number">8</span>, <span class="hljs-number">60</span>, <span class="hljs-number">24</span>);

  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> THREE.CanvasTexture(canvas);
}
</code></pre>
<p>In a similar way, we can define the side texture. We create a canvas element again, we get its context, then first fill the whole canvas to have a base color, and then draw the windows as rectangles. </p>
<h3 id="heading-how-to-map-textures-to-a-box">How to Map Textures to a Box</h3>
<p>Now let's see how can we use these textures for our car. When we define the mesh for the top of the cabin, instead of setting only one material, we set one for each side. We define an array of six materials. We map textures to the sides of the cabin, while the top and bottom will still have a plain color. </p>
<pre><code class="lang-javascript">. . .

function createCar() {
  <span class="hljs-keyword">const</span> car = <span class="hljs-keyword">new</span> THREE.Group();

  <span class="hljs-keyword">const</span> backWheel = createWheels();
  backWheel.position.y = <span class="hljs-number">6</span>;
  backWheel.position.x = <span class="hljs-number">-18</span>;
  car.add(backWheel);

  <span class="hljs-keyword">const</span> frontWheel = createWheels();
  frontWheel.position.y = <span class="hljs-number">6</span>;
  frontWheel.position.x = <span class="hljs-number">18</span>;
  car.add(frontWheel);

  <span class="hljs-keyword">const</span> main = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.BoxBufferGeometry(<span class="hljs-number">60</span>, <span class="hljs-number">15</span>, <span class="hljs-number">30</span>),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0xa52523</span> })
  );
  main.position.y = <span class="hljs-number">12</span>;
  car.add(main);

  <span class="hljs-keyword">const</span> carFrontTexture = getCarFrontTexture();

  <span class="hljs-keyword">const</span> carBackTexture = getCarFrontTexture();

  <span class="hljs-keyword">const</span> carRightSideTexture = getCarSideTexture();

  <span class="hljs-keyword">const</span> carLeftSideTexture = getCarSideTexture();
  carLeftSideTexture.center = <span class="hljs-keyword">new</span> THREE.Vector2(<span class="hljs-number">0.5</span>, <span class="hljs-number">0.5</span>);
  carLeftSideTexture.rotation = <span class="hljs-built_in">Math</span>.PI;
  carLeftSideTexture.flipY = <span class="hljs-literal">false</span>;

  <span class="hljs-keyword">const</span> cabin = <span class="hljs-keyword">new</span> THREE.Mesh(<span class="hljs-keyword">new</span> THREE.BoxBufferGeometry(<span class="hljs-number">33</span>, <span class="hljs-number">12</span>, <span class="hljs-number">24</span>), [
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">map</span>: carFrontTexture }),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">map</span>: carBackTexture }),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0xffffff</span> }), <span class="hljs-comment">// top</span>
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0xffffff</span> }), <span class="hljs-comment">// bottom</span>
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">map</span>: carRightSideTexture }),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">map</span>: carLeftSideTexture }),
  ]);
  cabin.position.x = <span class="hljs-number">-6</span>;
  cabin.position.y = <span class="hljs-number">25.5</span>;
  car.add(cabin);

  <span class="hljs-keyword">return</span> car;
}

. . .
</code></pre>
<p>Most of these textures will be mapped correctly without any adjustments. But if we turn the car around then we can see the windows appear in the wrong order on the left side.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/fixing-texture-before-after.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Right side and left side before and after fixing the texture</em></p>
<p>This is expected as we use the texture for the right side here as well. We can define a separate texture for the left side or we can mirror the right side.</p>
<p>Unfortunately, we can't flip a texture horizontally. We can only flip a texture vertically. We can fix this in 3 steps.</p>
<p>First, we turn the texture around by 180 degrees, which equals PI in radians. Before turning it, though, we have to make sure that the texture is rotated around its center. This is not the default – we have to set that the center of rotation is halfway. We set 0.5 on both axes which basically means 50%. Then finally we flip the texture upside down to have it in the correct position.</p>
<h2 id="heading-wrap-up">Wrap-up</h2>
<p>So what did we do here? We created a scene that contains our car and the lights. We built the car from simple boxes. </p>
<p>You might think this is too basic, but if you think about it many mobile games with stylish looks are actually created using boxes. Or just think about Minecraft to see how far you can get by putting together boxes. </p>
<p>Then we created textures with HTML canvas. HTML canvas is capable of much more than what we used here. We can draw different shapes with curves and arcs, but then again sometimes a minimal design is all that we need.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Screenshot-2021-03-19-at-11.31.27-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>And finally, we defined a camera to establish how we look at this scene, as well as a renderer that renders the final image into the browser.</p>
<h2 id="heading-next-steps">Next Steps</h2>
<p>If you want to play around with the code, you can find the source code on <a target="_blank" href="https://codepen.io/HunorMarton/pen/qBqzQOJ">CodePen</a>. And if you want to move forward with this project, then check out my YouTube video on how to turn this into a game. </p>
<p>In this tutorial, we create a traffic run game. After defining the car we draw the race track, we add game logic, event handlers, and animation.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/JhgBwJn1bQw" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Three.js Tutorial – How to Render 3D Objects in the Browser ]]>
                </title>
                <description>
                    <![CDATA[ If you have ever wanted to build a game with JavaScript, you might have come across Three.js.  Three.js is a library that we can use to render 3D graphics in the browser. The whole thing is in JavaScript, so with some logic you can add animation, int... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/render-3d-objects-in-browser-drawing-a-box-with-threejs/</link>
                <guid isPermaLink="false">66c4c8174173ed342943d0c2</guid>
                
                    <category>
                        <![CDATA[ 3d ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #Game Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Game Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ three.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hunor Márton Borbély ]]>
                </dc:creator>
                <pubDate>Wed, 03 Feb 2021 23:06:37 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/02/Stack.002-1.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you have ever wanted to build a game with JavaScript, you might have come across Three.js. </p>
<p>Three.js is a library that we can use to render 3D graphics in the browser. The whole thing is in JavaScript, so with some logic you can add animation, interaction, or even turn it into a game. </p>
<p>In this tutorial, we will go through a very simple example. We'll render a 3D box, and while doing so we'll learn the fundamentals of Three.js. </p>
<p>Three.js uses WebGL under the hood to render 3D graphics. We could use plain WebGL, but it's very complex and rather low level. On the other hand, Three.js is like playing with Legos. </p>
<p>In this article, we'll go through how to place a 3D object in a scene, set up the lighting and a camera, and render the scene on a canvas. So let’s see how we can do all this.</p>
<h2 id="heading-define-the-scene-object">Define the Scene Object</h2>
<p>First, we have to define a scene. This will be a container where we place our 3D objects and lights. The scene object also has some properties, like the background color. Setting that is optional though. If we don't set it, the default will be black.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();
scene.background = <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-number">0x000000</span>); <span class="hljs-comment">// Optional, black is default</span>

...
</code></pre>
<h2 id="heading-geometry-material-mesh">Geometry + Material = Mesh</h2>
<p>Then we add our 3D box to the scene as a mesh. A mesh is a combination of a geometry and a material.</p>
<pre><code class="lang-js">...

<span class="hljs-comment">// Add a cube to the scene</span>
<span class="hljs-keyword">const</span> geometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">3</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>); <span class="hljs-comment">// width, height, depth</span>
<span class="hljs-keyword">const</span> material = <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0xfb8e00</span> });
<span class="hljs-keyword">const</span> mesh = <span class="hljs-keyword">new</span> THREE.Mesh(geometry, material);
mesh.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>); <span class="hljs-comment">// Optional, 0,0,0 is the default</span>
scene.add(mesh);

...
</code></pre>
<h3 id="heading-what-is-a-geometry">What is a Geometry?</h3>
<p>A geometry is a rendered shape that we’re building - like a box. A geometry can be build from vertices or we can use a predefined one. </p>
<p>The BoxGeometry is the most basic predefined option. We only have to set the width, height, and depth of the box and that’s it. </p>
<p>You might think that we can't get far by defining boxes, but many games with minimalistic design use only a combination of boxes. </p>
<p>There are other predefined geometries as well. We can easily define a plane, a cylinder, a sphere, or even an icosahedron.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/freecodecamp-4.001.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-work-with-material">How to Work with Material</h3>
<p>Then we define a material. A material describes the appearance of an object. Here we can define things like texture, color, or opacity. </p>
<p>In this example we are only going to set a color. There are still different options for materials. The main difference between most of them is how they react to light. </p>
<p>The simplest one is the MeshBasicMaterial. This material doesn't care about light at all, and each side will have the same color. It might not be the best option, though, as you can’t see the edges of the box.</p>
<p>The simplest material that cares about light is the MeshLambertMaterial. This will calculate the color of each vertex, which is practically each side. But it doesn't go beyond that.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/freecodecamp-4.002.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you need more precision, there are more advanced materials. The MeshPhongMaterial not only calculates the color by vertex but by each pixel. The color can change within a side. This can help with realism but also costs in performance. </p>
<p>It also depends on the light settings and the geometry if it has any real effect. If we render boxes and use directional light, the result won't change that much. But if we render a sphere, the difference is more obvious.</p>
<h3 id="heading-how-to-position-a-mesh">How to Position a Mesh</h3>
<p>Once we have a mesh we can also position it within the scene and set a rotation by each axis. Later if we want to animate objects in the 3D space we will mostly adjust these values. </p>
<p>For positioning we use the same units that we used for setting the size. It doesn't matter if you are using small numbers or big numbers, you just need to be consistent in your own world. </p>
<p>For the rotation we set the values in radians. So if you have your values in degrees you have to divide them by 180° then multiply by PI.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/freecodecamp-3.004.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-add-light">How to Add Light</h2>
<p>Then let's add lights. A mesh with basic material doesn’t need any light, as the mesh will have the set color regardless of the light settings. </p>
<p>But the Lambert material and Phong material require light. If there isn't any light, the mesh will remain in darkness.</p>
<pre><code class="lang-js">...

<span class="hljs-comment">// Set up lights</span>
<span class="hljs-keyword">const</span> ambientLight = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.6</span>);
scene.add(ambientLight);

...
</code></pre>
<p>We'll add two lights - an ambient light and a directional light. </p>
<p>First, we add the ambient light. The ambient light is shining from every direction, giving a base color for our geometry. </p>
<p>To set an ambient light we set a color and an intensity. The color is usually white, but you can set any color. The intensity is a number between 0 and 1. The two lights we define work in an accumulative way so in this case we want the intensity to be around 0.5 for each.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/freecodecamp-3.003.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The directional light has a similar setup, but it also has a position. The word position here is a bit misleading, because it doesn’t mean that the light is coming from an exact position. </p>
<p>The directional light is shining from very far away with many parallel light rays all having a fixed angle. But instead of defining angles, we define the direction of a single light ray. </p>
<p>In this case, it shines from the direction of the 10,20,0 position towards the 0,0,0 coordinate. But of course, the directional light is not only one light ray, but an infinite amount of parallel rays. </p>
<p>Think of it as the sun. On a smaller scale, light rays of the sun also come down in parallel, and the sun’s position isn't what matters but rather its direction. </p>
<p>And that’s what the directional light is doing. It shines on everything with parallel light rays from very far away.</p>
<pre><code class="lang-js">...

const dirLight = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.6</span>);
dirLight.position.set(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>, <span class="hljs-number">0</span>); <span class="hljs-comment">// x, y, z</span>
scene.add(dirLight);

...
</code></pre>
<p>Here we set the position of the light to be from above (with the Y value) and shift it a bit along the X-axis as well. The Y-axis has the highest value. This means that the top of the box receives the most light and it will be the shiniest side of the box. </p>
<p>The light is also moved a bit along the X-axis, so the right side of the box will also receive some light, but less. </p>
<p>And because we don’t move the light position along the Z-axis, the front side of the box will not receive any light from this source. If there wasn't an ambient light, the front side would remain in darkness. </p>
<p>There are other light types as well. The PointLight, for instance, can be used to simulate light bulbs. It has a fixed position and it emits light in every direction. And the SpotLight can be used to simulate the spotlight of a car. It emits light from a single point into a direction along a cone.</p>
<h2 id="heading-how-to-set-up-the-camera">How to Set up the Camera</h2>
<p>So far, we have created a mesh with geometry and material. And we have also set up lights and added to the scene. We still need a camera to define how we look at this scene. </p>
<p>There are two options here: perspective cameras and orthographic cameras.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/Stack.010.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Video games mostly use perspective cameras, because how they work is similar to how you see things in real life. Things that are further away appear to be smaller and things that are right in front of you appear bigger. </p>
<p>With orthographic projections, things will have the same size no matter how far they are from the camera. Orthographic cameras have a more minimal, geometric look. They don't distort the geometries - the parallel lines will appear in parallel.</p>
<p>For both cameras, we have to define a view frustum. This is the region in the 3D space that is going to be projected to the screen. Anything outside of this region won't appear on the screen. This is because it is either too close or too far away, or because the camera isn't pointed towards it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/freecodecamp-2.001.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>With perspective projection, everything within the view frustum is projected towards the viewpoint with a straight line. Things further away from the camera appear smaller on the screen, because from the viewpoint you can see them under a smaller angle.</p>
<pre><code class="lang-js">...

<span class="hljs-comment">// Perspective camera</span>
<span class="hljs-keyword">const</span> aspect = <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight;
<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(
  <span class="hljs-number">45</span>, <span class="hljs-comment">// field of view in degrees</span>
  aspect, <span class="hljs-comment">// aspect ratio</span>
  <span class="hljs-number">1</span>, <span class="hljs-comment">// near plane</span>
  <span class="hljs-number">100</span> <span class="hljs-comment">// far plane</span>
);

...
</code></pre>
<p>To define a perspective camera, you need to set a field of view, which is the vertical angle from the viewpoint. Then you define an aspect ratio of the width and the height of the frame. If you fill the whole browser window and you want to keep its aspect ratio, then this is how you can do it. </p>
<p>Then the last two parameters define how far the near and far planes are from the viewpoint. Things that are too close to the camera will be ignored, and things that are too far away will be ignored as well.</p>
<pre><code class="lang-js">...

<span class="hljs-comment">// Orthographic camera</span>
<span class="hljs-keyword">const</span> width = <span class="hljs-number">10</span>;
<span class="hljs-keyword">const</span> height = width * (<span class="hljs-built_in">window</span>.innerHeight / <span class="hljs-built_in">window</span>.innerWidth);
<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.OrthographicCamera(
  width / <span class="hljs-number">-2</span>, <span class="hljs-comment">// left</span>
  width / <span class="hljs-number">2</span>, <span class="hljs-comment">// right</span>
  height / <span class="hljs-number">2</span>, <span class="hljs-comment">// top</span>
  height / <span class="hljs-number">-2</span>, <span class="hljs-comment">// bottom</span>
  <span class="hljs-number">1</span>, <span class="hljs-comment">// near</span>
  <span class="hljs-number">100</span> <span class="hljs-comment">// far</span>
);

...
</code></pre>
<p>Then there’s the orthographic camera. Here we are not projecting things towards a single point but towards a surface. Each projection line is in parallel. That’s why it doesn’t matter how far objects are from the camera, and that’s why it doesn’t distort geometries. </p>
<p>For orthographic cameras, we have to define how far each plane is from the viewpoint. The left plane is therefor five units to the left, and the right plane is five units to the right, and so on.</p>
<pre><code class="lang-js">...

camera.position.set(<span class="hljs-number">4</span>, <span class="hljs-number">4</span>, <span class="hljs-number">4</span>);
camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

...
</code></pre>
<p>Regardless of which camera are we using, we also need to position it and set it in a direction. If we are using an orthographic camera the actual numbers here don’t matter that much. The objects will appear the same size no matter how far away they are from the camera. What matters, though, is their proportion. </p>
<p>Through this whole tutorial, we saw all the examples through the same camera. This camera was moved by the same unit along every axis and it looks towards the 0,0,0 coordinate. Positioning an orthographic camera is like positioning a directional light. It's not the actual position that matters, but its direction.</p>
<h2 id="heading-how-to-render-the-scene">How to Render the Scene</h2>
<p>So we managed to put together the scene and a camera. Now only the final piece is missing that renders the image into our browser. </p>
<p>We need to define a WebGLRenderer. This is the piece that is capable of rendering the actual image into an HTML canvas when we provide a scene and a camera. This is also where we can set the actual size of this canvas – the width and height of the canvas in pixels as it should appear in the browser.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-comment">// Scene</span>
<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();

<span class="hljs-comment">// Add a cube to the scene</span>
<span class="hljs-keyword">const</span> geometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">3</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>); <span class="hljs-comment">// width, height, depth</span>
<span class="hljs-keyword">const</span> material = <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0xfb8e00</span> });
<span class="hljs-keyword">const</span> mesh = <span class="hljs-keyword">new</span> THREE.Mesh(geometry, material);
mesh.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
scene.add(mesh);

<span class="hljs-comment">// Set up lights</span>
<span class="hljs-keyword">const</span> ambientLight = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.6</span>);
scene.add(ambientLight);

<span class="hljs-keyword">const</span> directionalLight = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.6</span>);
directionalLight.position.set(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>, <span class="hljs-number">0</span>); <span class="hljs-comment">// x, y, z</span>
scene.add(directionalLight);

<span class="hljs-comment">// Camera</span>
<span class="hljs-keyword">const</span> width = <span class="hljs-number">10</span>;
<span class="hljs-keyword">const</span> height = width * (<span class="hljs-built_in">window</span>.innerHeight / <span class="hljs-built_in">window</span>.innerWidth);
<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.OrthographicCamera(
  width / <span class="hljs-number">-2</span>, <span class="hljs-comment">// left</span>
  width / <span class="hljs-number">2</span>, <span class="hljs-comment">// right</span>
  height / <span class="hljs-number">2</span>, <span class="hljs-comment">// top</span>
  height / <span class="hljs-number">-2</span>, <span class="hljs-comment">// bottom</span>
  <span class="hljs-number">1</span>, <span class="hljs-comment">// near</span>
  <span class="hljs-number">100</span> <span class="hljs-comment">// far</span>
);

camera.position.set(<span class="hljs-number">4</span>, <span class="hljs-number">4</span>, <span class="hljs-number">4</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">// Renderer</span>
<span class="hljs-keyword">const</span> renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer({ <span class="hljs-attr">antialias</span>: <span class="hljs-literal">true</span> });
renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);
renderer.render(scene, camera);

<span class="hljs-comment">// Add it to HTML</span>
<span class="hljs-built_in">document</span>.body.appendChild(renderer.domElement);
</code></pre>
<p>And finally, the last line here adds this rendered canvas to our HTML document. And that’s all you need to render a box. It might seem a little too much for just a single box, but most of these things we only have to set up once. </p>
<p>If you want to move forward with this project, then check out my YouTube video on how to turn this into a simple game. In the video, we create a stack building game. We add game logic, event handlers and animation, and even some physics with Cannon.js.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/hBiGFpBle7E" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>If you have any feedback or questions on this tutorial, feel free to Tweet me <a target="_blank" href="https://twitter.com/HunorBorbely">@HunorBorbely</a> or leave a comment on <a target="_blank" href="https://www.youtube.com/channel/UCxhgW0Q5XLvIoXHAfQXg9oQ">YouTube</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
