Getting started
This tutorial explains how to build a simple scene with the 3DS module. The basics are somewhat similar to Three.js. There are some differences though that we will highlight along the way.
Let’s start with a basic HTML file index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
html, body {
margin: 0;
padding: 0;
}
</style>
<title>My first example</title>
</head>
<body>
</body>
<script type="module" id="main"></script>
</html>
At each step of the tutorial, we highlight the additional lines of code needed to perform the described action. The complete script can be found at the end of this page.
Step 0. Declaring required modules / Choosing a geometry.
Beside the 3DS module, the application relies on several 3d-party JavaScript modules (Three.js in particular). There are several options to load them. We use here the “importmap” specification. In practice the following should be added in the header
<script type="importmap">
{
"imports": {
"three": "vendor/three.module.js",
"three/addons": "vendor/three/addons/Addons.js",
"3ds": "3ds/3dsEuc.js"
}
}
</script>
Remarks
If
importmap
is not supported by your browser, you can add the polyfill provided byes-module-shims
In this example we assume that the directories
vendor
and3ds
are at the same level as theindex.html
file.The names in the import map (
three
,three/addons
, and3ds
) cannot be changed.
Geometry
In the above example, we have chosen a geometry, via the module 3dsEuc.js
.
Indeed, all the tools for each geometry are wrapped in a single file with the name 3dsXXX.js
where XXX
has the
following meaning.
XXX |
Geometry |
---|---|
Euc |
\(\mathbb E^3\) (euclidean geometry) |
Hyp |
\(\mathbb H^3\) (hyperbolic geometry) |
Sph |
\(S^3\) (spherical geometry) |
S2E |
\(S^2 \times \mathbb E\) (product geometry) |
H2E |
\(\mathbb H^2 \times \mathbb E\) (product geometry) |
Nil |
Nil |
SL2 |
The universal cover of \({\rm SL}(2,\mathbb R)\) |
Sol |
Sol |
From now on, all the instructions to generate a scene will be added in the script
tag identify as main
.
Step 1. Choosing a discrete group.
Next, one need to load a discrete subgroup of isometries. This subgroup corresponds to the fundamental group of the quotient manifold/orbifold we are working in. In this tutorial we will only work in \(\mathbb E^3\), hence the discrete subgroup is just the trivial group. It is loaded as follows
import {trivialSet as set} from "3ds";
Step 2. Defining a scene, a camera and a renderer.
Before adding object, one needs to define a scene and a camera. Those items are bind together in a renderer whose task is to dynamically create a shader and run it.
import {SphereCamera, BasicRenderer, Scene} from "3ds";
// ...
// initial setup
const camera = new SphereCamera({set: set});
const scene = new Scene();
const renderer = new BasicRenderer(camera, scene, {}, {
logarithmicDepthBuffer: true
});
// adjust the renderer to the size of the screen
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// add a canvas in the HTML file to display the scene.
document.body.appendChild(renderer.domElement);
The camera takes as an argument the discrete subgroup of isometries we loaded. By default, the position of the camera is the origin of the geometry. It points toward the negative z-direction (in a preferred frame in the tangent space at the origin).
The renderer takes as arguments the camera and the scene.
Step 3 Populating the scene
The scene is made of objects which are either solids or lights.
Before defining those object, we need to extend our list of imports with the relevant classes.
In our examples we will also use Point()
as well as the Color implementation from Three.js.
import {
PointLight,
PhongMaterial,
Ball,
Point
} from "3ds";
import {Color} from "three";
Then we define all the objects in the scene. Here a single point light, and a ball with a phong material.
// A light
const light = new PointLight(
new Point(-1, 1, -2),
new Color(0, 1, 1),
0.5,
);
// Phong shading material
const mat = new PhongMaterial({shininess: 10});
// A ball
const ball = new Ball(
new Point(-1, -0.5, -2),
0.3,
mat
);
The light has three arguments here (it can be different for another kind of lights or another geometry): its location, its colors and its intensity
The phong material accepts various parameters (see the doc)
In general, a solid is a shape with a given material. A solid can be defined using a built-in class (as here).
Alternatively, one can define separately a shape and a material and combine them in a Solid()
object.
Finally, one adds those objects to the scene
scene.add(light, ball);
Step 4 Rendering the scene.
Before rendering the scene, we need to build the underlying shader. This is done as follows
renderer.build();
Note that every object added to the scene after this function has been called will not be taken into account.
Then we define a function that is called at each frame.
function animate() {
renderer.render();
}
Here we just call the Renderer.render()
method of our renderer. We can elaborate to animate the scene, handle events, etc.
Finally, we define the animation loop
renderer.setAnimationLoop(animate);
A useful command to add when debugging is
renderer.checkShader();
It displays in the log the shader built by the renderer.
Step 5. Summary
The complete index.html
file is
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
html, body {
margin: 0;
padding: 0;
}
</style>
<title>My first example</title>
<script type="importmap">
{
"imports": {
"three": "vendor/three.module.js",
"three/addons": "vendor/three/addons/Addons.js",
"3ds": "3ds/3dsEuc.js"
}
}
</script>
</head>
<body>
</body>
<script type="module" id="main">
import {
trivialSet as set
SphereCamera, BasicRenderer, Scene,
PointLight,
Point,
PhongMaterial,
Ball
} from "3ds";
import {Color} from "three";
// initial setup
const camera = new SphereCamera({set: set});
const scene = new Scene();
const renderer = new BasicRenderer(camera, scene);
// adjust the renderer to the size of the screen
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// add a canvas in the HTML file to display the scene.
document.body.appendChild(renderer.domElement);
// A light
const light = new PointLight(
new Point(-1, 1, -2),
new Color(0, 1, 1),
0.5
);
// Phong shading material
const mat = new PhongMaterial({shininess: 10});
// A ball
const ball = new Ball(
new Point(-1, -0.5, -2),
0.3,
mat
);
scene.add(light, ball);
renderer.build();
function animate() {
renderer.render();
}
renderer.setAnimationLoop(animate);
renderer.checkShader();
</script>
</html>