Create Parametric Surfaces with Three.js
This article provides a step-by-step guide on how to generate
procedurally defined 3D parametric surfaces in Three.js using
ParametricGeometry. You will learn how to write a
parametric mathematical function, import the necessary geometry module,
instantiate the surface, and render it inside a Three.js scene.
What is a Parametric Surface?
A parametric surface is a 3D shape defined by a mathematical function
that maps 2D coordinates (usually represented as \(u\) and \(v\)) into 3D space coordinates (\(x, y, z\)). In Three.js, both \(u\) and \(v\) range from 0.0 to
1.0. By changing these input values sequentially, Three.js
coordinates a grid of vertices to build a custom 3D mesh.
Step 1: Import ParametricGeometry
In modern versions of Three.js, ParametricGeometry is
not included in the core namespace. You must import it from the addons
folder.
import * as THREE from 'three';
import { ParametricGeometry } from 'three/addons/geometries/ParametricGeometry.js';Step 2: Define the Parametric Function
The core of a parametric surface is the generator function. This
function must accept three arguments: 1. u (number from 0
to 1) 2. v (number from 0 to 1) 3. target (a
THREE.Vector3 object where the calculated \(x, y, z\) coordinates are stored)
Here is a function that generates a waving, sinusoidal ripple surface:
const radialWave = (u, v, target) => {
// Scale u and v to map to a 10x10 area centered at the origin
const x = (u - 0.5) * 10;
const z = (v - 0.5) * 10;
// Calculate the distance from the center to create a radial ripple effect
const distance = Math.sqrt(x * x + z * z);
const y = Math.sin(distance);
// Set the coordinates in the target Vector3
target.set(x, y, z);
};Step 3: Instantiate the Geometry
To construct the geometry, pass your custom function to the
ParametricGeometry constructor. You must also specify the
number of subdivisions (slices and stacks) to define the resolution of
the grid.
// Define the resolution of the surface grid
const slices = 40;
const stacks = 40;
// Create the geometry
const geometry = new ParametricGeometry(radialWave, slices, stacks);Note: Higher values for slices and stacks result in a smoother surface but require more processing power.
Step 4: Create the Mesh and Add to Scene
Once the geometry is defined, associate it with a material and add it
to your scene as a standard mesh. To see both the top and bottom of the
generated surface, set the material’s side property to
THREE.DoubleSide.
// Create a material that responds to light
const material = new THREE.MeshStandardMaterial({
color: 0x00ffcc,
side: THREE.DoubleSide,
wireframe: false
});
// Create the mesh and add it to the scene
const surfaceMesh = new THREE.Mesh(geometry, material);
scene.add(surfaceMesh);Complete Implementation Example
Below is a complete script demonstrating how to set up the scene, light, camera, and the parametric surface:
import * as THREE from 'three';
import { ParametricGeometry } from 'three/addons/geometries/ParametricGeometry.js';
// 1. Setup Scene, Camera, and Renderer
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 10, 10);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 2. Add Lighting
const light = new THREE.DirectionalLight(0xffffff, 1.5);
light.position.set(5, 10, 7);
scene.add(light);
scene.add(new THREE.AmbientLight(0x404040));
// 3. Define the Parametric Equation (Saddle Shape)
const saddleFunction = (u, v, target) => {
const x = (u - 0.5) * 6;
const z = (v - 0.5) * 6;
const y = (x * x - z * z) * 0.25; // Hyperbolic paraboloid equation
target.set(x, y, z);
};
// 4. Create Parametric Geometry
const geometry = new ParametricGeometry(saddleFunction, 30, 30);
const material = new THREE.MeshStandardMaterial({ color: 0x3a86ff, side: THREE.DoubleSide });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// 5. Animation Loop
function animate() {
requestAnimationFrame(animate);
mesh.rotation.y += 0.01; // Slowly rotate the surface
renderer.render(scene, camera);
}
animate();