Whatever you're making from data visualizations, or telling stories, being creative, selling products, 3D scenes open up new ways for people to engage with your content. (and it's also just really fun)
This article will walk through the basics of:
- Setting up a scene
- Adding lights and shapes
- Incorporating the scene into a site
Tools
We'll be using svelte, three.js, and svelte-cubed.
If you're unfamiliar with these tools and just want to make something that looks cool you can still follow along! And if you want to learn more, that's great too. Here are some helpful introductory resources you can come back to later (or banish into eternal browser-tab land):
The best way to learn is to build, so let's go.
(full code is at the bottom if you run into any issues)
Setting up a Scene: Canvas and Camera
Head on over to the Svelte REPL: https://svelte.dev/repl/
This is where we'll do all our development so you can easily share your scene and won't have to worry about setting anything up on your machine.
Start by importing our main tools in the script tag:
<script>
import * as THREE from "three";
import * as SC from "svelte-cubed";
// all of our javascript and logic would go here
</script>
This enables us to use THREE and SC inside our App.svelte file. (And don't worry about bundle size; the Svelte compiler will optimize that for us)
Every 3D scene is projected onto a canvas, so let's create one. Under the script tags, add a svelte-cubed canvas:
<SC.Canvas>
<!-- all of our scene stuff will go here! -->
</SC.Canvas>
Way to go! Now buy my book... just kidding. Let's continue.
We can't see anything because we have no camera. We'll go with the classic perspective camera (you can experiment with the orthographic camera at the end to see how it differs).
<!-- CAMERA -->
<SC.PerspectiveCamera near={1} far={100} fov={55} />
wait wut? Okay, so we have to configure our camera a little bit to create our view frustum aka what our camera can see.
- near: anything closer than this will not be shown by the camera (unit: meters)
- far: anything farther than this will not be shown by the camera (unit: meters)
- fov: (Field of View) anything outside of this angle will not be shown by the camera (unit: degrees)
Let's see if it's working by creating a new color and setting it as our canvas background:
<SC.Canvas background={new THREE.Color('seagreen')}>
<!-- CAMERA -->
<SC.PerspectiveCamera near={1} far={100} fov={55} />
<!-- all of our scene stuff will go here! -->
</SC.Canvas>
Success!
Adding Shapes
What regular people call shapes are called a "mesh" in 3D-land, so we'll use that word from now on. A mesh is a combination of a geometry and a material, just like your table: it has a geometry (e.g. rectangle or oval) and a material (e.g. wood or glass).
Three.js offers many geometries and materials you can experiment with, and each variant can take several unique properties. For now let's add an octahedron... and together we'll learn what an octahedron is.
<!-- MESHES -->
<SC.Mesh
geometry={new THREE.OctahedronGeometry()}
material={new THREE.MeshStandardMaterial({
color: new THREE.Color('salmon')
})}
/>
Notice that we passed a javascript object into our new material with a color property. This is where you can handle all sorts of material enhancements, but we'll just use color for now.
And there it is! But it's not the color of salmon :( That's happening because our MeshStandardMaterial interacts with light and we don't have any lights! (You can experiment with a MeshBasicMaterial for something that does not interact with light to compare)
Adding Light
Unsurprisingly three.js offers many lighting options. Not sure how to incorporate this pun but: something, something, the unbearable lightness of three-ing. Now we're going to add two lights: a directional light and an ambient light, and you'll probably get the gist of what they do in the process.
<SC.DirectionalLight
color={new THREE.Color('white')}
intensity={.75}
position={[10, 10, 10]}
/>
Notice how the un-lit sides are really dark. Let's add the ambient light:
<SC.AmbientLight
color={new THREE.Color('white')}
intensity={.5}
/>
Looking good! But still only looks 2D because we're looking at the mesh straight on.
We can move the mesh with its rotation
or position
properties, or we can move the camera with its position
property, but to get a full range of interactions we'll add orbit controls after our camera.
<!-- CAMERA -->
<SC.PerspectiveCamera near={1} far={100} fov={55} />
<SC.OrbitControls
enabled
enablePan
enableZoom
enableRotate
enableDamping
/>
Now you can interact with your scene by clicking and dragging, scrolling to zoom, and right-click and drag to pan.
COOL. But none of your friends know what an octahedron is, so you want to include this scene within the context of a regular website with some words. Svelte (and svelte-cubed) make this incredibly easy.
Adding the Scene to Your Site
Let's move ALL our code into a new component.
- Copy all the code (script included) in
App.svelte
- Click the + icon to create a new file and name it
Octo.svelte
- Paste all your code in
Octo.svelte
- Delete all the code in
App.svelte
Now we can't see anything. App.svelte
is the entry point for your application, and it's empty so the page is empty.
Let's add some markup in App.svelte
:
<h1>What is an Octahedron?</h1>
<div class="scene-container"></div>
<p>An octahedron is a three-dimensional shape having eight plane faces, especially a regular solid figure with eight equal triangular faces.</p>
We'll use the "scene-container" div to contain our scene, and whatever dimensions we set on this container will control our scene dimensions too! Pretty neat.
Import your Octo.svelte
component and nest it inside the "scene-container" div:
<script>
import Octo from "./Octo.svelte";
</script>
And drop it in the markup like an element:
<div class="scene-container">
<Octo />
</div>
Uh oh, it's taking over the whole page because we haven't styled the "scene-container". In App.svelte
add the style tags under your markup:
<style>
.scene-container {
/* position relative let's the canvas position itself relative to this container */
position: relative;
width: 75%;
max-width: 400px
height: 400px;
margin: 0 auto;
}
</style
And there you go! An informative geometry themed page with a responsive and interactive 3D scene. But you're just getting started.
Add more shapes, change the camera, add wireframe: true
to the mesh material properties. What if you change the mesh position to animate movement? What if you change the directional light position to change the angle of illumination? What about importing models and animating scenes?
If you enjoyed this and want to do more experiments, I built a little open source tool to help with some basics that will generate the svelte code for you to build on: https://sc3-lab.netlify.app/
The plan is to write a couple more of these for movement, transitions, and importing models. Until then, share what you built and be proud of it!
For Reference
REPL
https://svelte.dev/repl/71b063fc410543598e8a727999cf7bbe
App.svelte
<script>
import Octo from "./Octo.svelte";
</script>
<h1>What is an Octahedron?</h1>
<div class="scene-container">
<Octo></Octo>
</div>
<p>An octahedron is a three-dimensional shape having eight plane faces, especially a regular solid figure with eight equal triangular faces.</p>
<style>
.scene-container {
/* position relative let's the canvas position itself relative to this container */
position: relative;
width: 75%;
max-width: 400px;
height: 400px;
margin: 0 auto;
}
</style>
Octo.svelte
<script>
import * as THREE from "three";
import * as SC from "svelte-cubed";
</script>
<SC.Canvas background={new THREE.Color('seagreen')}>
<!-- LIGHTS -->
<SC.AmbientLight
color={new THREE.Color('white')}
intensity={.5}
/>
<SC.DirectionalLight
color={new THREE.Color('white')}
intensity={.75}
position={[10, 10, 10]}
/>
<!-- MESHES -->
<SC.Mesh
geometry={new THREE.OctahedronGeometry()}
material={new THREE.MeshStandardMaterial({
color: new THREE.Color('salmon')
})}
/>
<!-- CAMERA -->
<SC.PerspectiveCamera near={1} far={100} fov={55} />
<SC.OrbitControls
enabled
enablePan
enableZoom
enableRotate
enableDamping
/>
</SC.Canvas>
Top comments (0)