Skip to content

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:

text
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:

ts
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:

ts
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

ts
// 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:

ts
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:

ts
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:

ts
// 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:

ts
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:

ts
// 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:

ts
// 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:

ts
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:

ts
// ✅ 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

ts
// ✅ 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

ts
// ✅ 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

ts
// 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)

ts
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

ts
// 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

ts
// 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

ts
// 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

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

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

Textures Look Wrong

ts
// ❌ 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

ts
// ❌ 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.