Develop
Develop
Select your platform

Three.js Meshes, Geometry & Materials

Understanding how to create and work with 3D objects is fundamental to any Three.js application. This guide covers the building blocks: geometries (shapes), materials (appearance), and meshes (the combination).

The Core Equation

Every visible 3D object follows this pattern:
Mesh = Geometry + Material

Geometry: The shape (vertices, faces, UVs)
Material: The appearance (color, texture, shading)
Mesh: The renderable object that combines them
In IWSDK, you create these using Three.js classes and attach them to entities.

Geometries: The Shape of Things

Geometry defines the shape of 3D objects using vertices, faces, and texture coordinates.

Built-in Geometries

Three.js provides many primitive shapes:
import {
  BoxGeometry,
  SphereGeometry,
  CylinderGeometry,
  PlaneGeometry,
  TorusGeometry,
} from '@iwsdk/core';

// Basic shapes with parameters
const box = new BoxGeometry(1, 1, 1); // width, height, depth
const sphere = new SphereGeometry(0.5, 32, 16); // radius, widthSeg, heightSeg
const cylinder = new CylinderGeometry(0.5, 0.5, 2, 32); // radiusTop, radiusBottom, height, segments
const plane = new PlaneGeometry(2, 2); // width, height
const torus = new TorusGeometry(1, 0.3, 16, 100); // radius, tube, radialSeg, tubularSeg

Custom Geometry

For complex shapes, create custom BufferGeometry:
import { BufferGeometry, BufferAttribute } from '@iwsdk/core';

// Create triangle
const geometry = new BufferGeometry();

// Define vertices (3 points in 3D space)
const vertices = new Float32Array([
  -1,
  -1,
  0, // vertex 1
  1,
  -1,
  0, // vertex 2
  0,
  1,
  0, // vertex 3
]);

// Define normals (for lighting)
const normals = new Float32Array([
  0,
  0,
  1, // normal for vertex 1
  0,
  0,
  1, // normal for vertex 2
  0,
  0,
  1, // normal for vertex 3
]);

// Define UV coordinates (for texturing)
const uvs = new Float32Array([
  0,
  0, // UV for vertex 1
  1,
  0, // UV for vertex 2
  0.5,
  1, // UV for vertex 3
]);

geometry.setAttribute('position', new BufferAttribute(vertices, 3));
geometry.setAttribute('normal', new BufferAttribute(normals, 3));
geometry.setAttribute('uv', new BufferAttribute(uvs, 2));

Reusing Geometry

// Create geometry once, reuse for multiple entities
const sharedGeometry = new BoxGeometry(1, 1, 1);

// Create multiple entities with same shape
for (let i = 0; i < 10; i++) {
  const material = new MeshStandardMaterial({
    color: Math.random() * 0xffffff,
  });
  const mesh = new Mesh(sharedGeometry, material);
  const entity = world.createTransformEntity(mesh);

  // Position each entity differently
  entity.object3D.position.set(i * 2, 0, 0);
}

Materials: The Look of Things

Materials define how surfaces look - their color, shininess, transparency, and how they react to light.

Material Types

For WebXR, use physically-based materials:
import {
  MeshStandardMaterial, // Good balance of quality and performance
  MeshPhysicalMaterial, // Advanced PBR features (clearcoat, transmission)
  MeshBasicMaterial, // Unlit (use sparingly)
} from '@iwsdk/core';

// Standard PBR material (recommended)
const pbr = new MeshStandardMaterial({
  color: 0x00ff00, // Base color (green)
  metalness: 0.1, // 0 = dielectric, 1 = metallic
  roughness: 0.7, // 0 = mirror, 1 = completely rough
});

// Advanced PBR material
const advanced = new MeshPhysicalMaterial({
  color: 0xffffff,
  metalness: 0,
  roughness: 0.1,
  clearcoat: 1.0, // Clear coating effect
  clearcoatRoughness: 0.1,
  transmission: 0.9, // Glass-like transparency
  thickness: 1.0,
});

// Unlit material (for UI elements)
const unlit = new MeshBasicMaterial({
  color: 0xff0000,
  transparent: true,
  opacity: 0.8,
});

Working with Textures

Textures add detail and realism:
import { TextureLoader } from '@iwsdk/core';

const loader = new TextureLoader();

// Load textures
const diffuseTexture = loader.load('/textures/brick_diffuse.jpg');
const normalTexture = loader.load('/textures/brick_normal.jpg');
const roughnessTexture = loader.load('/textures/brick_roughness.jpg');

// Apply to material
const material = new MeshStandardMaterial({
  map: diffuseTexture, // Base color texture
  normalMap: normalTexture, // Surface detail
  roughnessMap: roughnessTexture, // Roughness variation
});

// Configure texture settings
diffuseTexture.wrapS = RepeatWrapping;
diffuseTexture.wrapT = RepeatWrapping;
diffuseTexture.repeat.set(2, 2); // Tile 2x2

Color Space Management

Critical for correct colors in VR:
// Set color space for albedo textures
diffuseTexture.colorSpace = SRGBColorSpace;

// Linear textures (normals, roughness, etc.) use LinearSRGBColorSpace
normalTexture.colorSpace = LinearSRGBColorSpace;
roughnessTexture.colorSpace = LinearSRGBColorSpace;

Meshes: Putting It Together

Meshes combine geometry and materials into renderable objects:
import { Mesh } from '@iwsdk/core';

// Create mesh
const geometry = new BoxGeometry(1, 1, 1);
const material = new MeshStandardMaterial({ color: 0x00ff00 });
const mesh = new Mesh(geometry, material);

// Add to IWSDK entity
const entity = world.createTransformEntity(mesh);

// Set additional Three.js properties
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.name = 'GreenBox';

Multiple Materials

Objects can have different materials on different faces:
// Array of materials for different faces
const materials = [
  new MeshStandardMaterial({ color: 0xff0000 }), // +X
  new MeshStandardMaterial({ color: 0x00ff00 }), // -X
  new MeshStandardMaterial({ color: 0x0000ff }), // +Y
  new MeshStandardMaterial({ color: 0xffff00 }), // -Y
  new MeshStandardMaterial({ color: 0xff00ff }), // +Z
  new MeshStandardMaterial({ color: 0x00ffff }), // -Z
];

const mesh = new Mesh(new BoxGeometry(1, 1, 1), materials);

Lighting and Environment

IWSDK’s Default Lighting

IWSDK provides good default lighting:
// This happens automatically in World.create()
// - Gradient environment (soft ambient lighting)
// - PMREM generation for reflections
// - Proper tone mapping

Custom Environment Maps

For realistic reflections and lighting:
import { RGBELoader, PMREMGenerator } from '@iwsdk/core';

const loader = new RGBELoader();
const pmremGenerator = new PMREMGenerator(world.renderer);

loader.load('/environments/sunset.hdr', (texture) => {
  const envMap = pmremGenerator.fromEquirectangular(texture).texture;
  pmremGenerator.dispose();

  // Apply to scene
  world.scene.environment = envMap;
  world.scene.background = envMap; // Optional: visible background

  // Apply to specific materials
  material.envMap = envMap;
});

Lighting Best Practices for WebXR

Environment vs Direct Lights:
// ✅ Prefer environment lighting for VR (matches real-world lighting)
world.scene.environment = envMap;

// ✅ Add directional light only if needed
const sun = new DirectionalLight(0xffffff, 0.5);
sun.position.set(10, 10, 5);
world.scene.add(sun);

// ❌ Avoid too many dynamic lights (performance impact)

Performance Optimization

Geometry Sharing

// ✅ Reuse geometry for identical shapes
const boxGeometry = new BoxGeometry(1, 1, 1);

for (let i = 0; i < 100; i++) {
  const material = new MeshStandardMaterial({
    color: new Color().setHSL(i / 100, 1, 0.5),
  });
  const mesh = new Mesh(boxGeometry, material); // Same geometry
  // ... create entities
}

Material Sharing and Uniforms

// ✅ Share materials when possible
const sharedMaterial = new MeshStandardMaterial({ color: 0x00ff00 });

// ✅ Use uniforms for animated properties
const uniformMaterial = new MeshStandardMaterial({
  color: 0xffffff,
});

// In system update:
uniformMaterial.color.setHSL(time * 0.001, 1, 0.5); // Animate color

Texture Optimization

// Optimize textures for VR
const texture = loader.load('/textures/diffuse.jpg');

// ✅ Use appropriate sizes (power of 2)
// 512x512, 1024x1024, 2048x2048

// ✅ Enable mipmaps for distant objects
texture.generateMipmaps = true;

// ✅ Use compressed formats when possible
// .ktx2, .basis for better performance

LOD (Level of Detail)

import { LOD } from '@iwsdk/core';

const lod = new LOD();

// High detail (close)
const highMesh = new Mesh(
  new SphereGeometry(1, 32, 16),
  new MeshStandardMaterial({ color: 0x00ff00 }),
);

// Medium detail
const medMesh = new Mesh(
  new SphereGeometry(1, 16, 8),
  new MeshStandardMaterial({ color: 0x00ff00 }),
);

// Low detail (far)
const lowMesh = new Mesh(
  new SphereGeometry(1, 8, 4),
  new MeshStandardMaterial({ color: 0x00ff00 }),
);

lod.addLevel(highMesh, 0); // 0-10 meters
lod.addLevel(medMesh, 10); // 10-50 meters
lod.addLevel(lowMesh, 50); // 50+ meters

const entity = world.createTransformEntity(lod);

Common Patterns for WebXR

Real-World Sized Objects

// Create objects with appropriate VR/AR scale
const geometry = new BoxGeometry(0.1, 0.1, 0.1); // 10cm cube
const material = new MeshStandardMaterial({
  color: 0x4caf50,
  roughness: 0.3,
  metalness: 0.1,
});

const mesh = new Mesh(geometry, material);
const entity = world.createTransformEntity(mesh);

// Position at comfortable interaction height
entity.object3D.position.set(0, 1.2, -0.5);

UI Panels

// Use unlit materials for UI elements
const panelMaterial = new MeshBasicMaterial({
  color: 0xffffff,
  transparent: true,
  opacity: 0.9,
});

const panelGeometry = new PlaneGeometry(0.3, 0.2); // 30cm x 20cm
const panel = new Mesh(panelGeometry, panelMaterial);

const panelEntity = world.createTransformEntity(panel, { persistent: true });

Particle Effects

// Use instanced meshes for particles
import { InstancedMesh } from '@iwsdk/core';

const particleGeometry = new SphereGeometry(0.01, 8, 4); // Small spheres
const particleMaterial = new MeshBasicMaterial({ color: 0xffff00 });

const particles = new InstancedMesh(particleGeometry, particleMaterial, 1000);

// Update instances in systems
const matrix = new Matrix4();
for (let i = 0; i < 1000; i++) {
  matrix.setPosition(
    Math.random() * 10 - 5,
    Math.random() * 10,
    Math.random() * 10 - 5,
  );
  particles.setMatrixAt(i, matrix);
}
particles.instanceMatrix.needsUpdate = true;

Troubleshooting Common Issues

Objects Appear Black

// ❌ Missing or incorrect normals
geometry.computeVertexNormals(); // Fix

// ❌ No environment lighting
world.scene.environment = envMap; // Fix

Textures Look Wrong

// ❌ Wrong color space
texture.colorSpace = SRGBColorSpace; // For albedo textures
texture.colorSpace = LinearSRGBColorSpace; // For data textures

// ❌ Incorrect UV coordinates
geometry.attributes.uv.needsUpdate = true;

Performance Issues

// ❌ Too many draw calls
// Fix: Use InstancedMesh or merge geometries

// ❌ High-resolution textures
// Fix: Resize to appropriate sizes (512, 1024, 2048)

// ❌ Too many materials
// Fix: Share materials between objects

Summary

Key Concepts:
  1. Mesh = Geometry + Material - The fundamental building block
  2. Use PBR materials - MeshStandardMaterial for realistic lighting
  3. Manage color spaces - sRGB for colors, LinearSRGB for data
  4. Optimize for VR - Share resources, use appropriate resolutions
  5. IWSDK provides lighting - Environment maps work out of the box
  6. Real-world scale - Size objects appropriately for VR interaction
Understanding these concepts enables you to create visually appealing, performant 3D objects that look natural in WebXR environments.
Did you find this page helpful?
Thumbs up icon
Thumbs down icon