Rendering Components
Visual rendering, lighting, cameras, materials, and LOD components powered by Three.js.
Rendering components control how entities appear visually in your game. Web Engine uses Three.js for rendering, supporting meshes, lights, cameras, materials, shadows, and advanced features like LOD and render layers.
MeshRenderer#
The MeshRenderer component defines what mesh an entity renders. It supports both built-in primitives (box, sphere, plane) and loaded 3D models (GLTF, FBX).
Properties#
| Property | Type | Default | Description |
|---|---|---|---|
| primitiveType | ui8 | 0 | 0=use assetId, 1=Box, 2=Sphere, 3=Plane, 4=Cylinder, 5=Capsule, 6=Cone, 7=Torus |
| assetId | ui32 | 0 | Asset ID for loaded models (when primitiveType=0) |
| castShadow | ui8 | 0 | Whether mesh casts shadows (0=no, 1=yes) |
| receiveShadow | ui8 | 1 | Whether mesh receives shadows (0=no, 1=yes) |
Usage#
import { addComponent, MeshRenderer, PrimitiveType } from '@web-engine/core'; // Create box primitiveconst box = world.addEntity();addComponent(world, MeshRenderer, box);MeshRenderer.primitiveType[box] = PrimitiveType.Box;MeshRenderer.castShadow[box] = 1;MeshRenderer.receiveShadow[box] = 1; // Create entity with loaded modelconst character = world.addEntity();addComponent(world, MeshRenderer, character);MeshRenderer.primitiveType[character] = PrimitiveType.None;MeshRenderer.assetId[character] = loadedModelAssetId;Primitive Types
Built-in primitives: Box (1x1x1), Sphere (r=0.5), Plane (1x1), Cylinder (r=0.5, h=1), Capsule (r=0.35, h=1.2), Cone (r=0.5, h=1), Torus (R=0.5, r=0.2)
MaterialRef#
The MaterialRef component assigns materials to meshes with support for material overrides, UV tiling/offset, color tinting, and material variants.
Properties#
| Property | Type | Default | Description |
|---|---|---|---|
| assetId | ui32 | 0 | Material asset ID (0=use default/mesh material) |
| tiling | [f32, 2] | [1, 1] | UV tiling multiplier [x, y] |
| offset | [f32, 2] | [0, 0] | UV offset [x, y] |
| colorOverride | [f32, 3] | [1, 1, 1] | RGB color multiplier [r, g, b] |
| alphaOverride | f32 | 1.0 | Alpha multiplier (0-1) |
| variantHash | ui32 | 0 | Hash of active variant name |
| slotCount | ui8 | 0 | Number of material slots (multi-material) |
Usage#
import { addComponent, MaterialRef } from '@web-engine/core'; // Assign material with color tintconst entity = world.addEntity();addComponent(world, MaterialRef, entity);MaterialRef.assetId[entity] = materialAssetId;MaterialRef.colorOverride[entity][0] = 1.0; // RedMaterialRef.colorOverride[entity][1] = 0.5; // GreenMaterialRef.colorOverride[entity][2] = 0.5; // Blue // UV tiling for repeating texturesMaterialRef.tiling[entity][0] = 2.0; // Tile 2x horizontallyMaterialRef.tiling[entity][1] = 2.0; // Tile 2x vertically // Material transparencyMaterialRef.alphaOverride[entity] = 0.5; // 50% transparentMaterial Overrides
MaterialRef overrides multiply with the base material properties. This allows runtime tinting and transparency without creating new material instances.
MeshColor#
The MeshColor component applies a color tint to a mesh, similar to MaterialRef.colorOverride but as a separate component.
Properties#
| Property | Type | Default | Description |
|---|---|---|---|
| r | f32 | 1.0 | Red channel (0-1) |
| g | f32 | 1.0 | Green channel (0-1) |
| b | f32 | 1.0 | Blue channel (0-1) |
| a | f32 | 1.0 | Alpha channel (0-1) |
Light#
The Light component creates light sources in the scene. Supports point lights, directional lights (sun), and spot lights.
Properties#
| Property | Type | Default | Description |
|---|---|---|---|
| type | ui8 | 0 | 0=Point, 1=Directional, 2=Spot |
| color | [f32, 3] | [1, 1, 1] | Light color [r, g, b] |
| intensity | f32 | 1.0 | Light intensity multiplier |
| range | f32 | 10.0 | Light range in meters (point/spot only) |
| angle | f32 | 0.5 | Spot light cone angle in radians |
| castShadow | ui8 | 0 | Whether light casts shadows (0=no, 1=yes) |
Usage#
import { addComponent, Light } from '@web-engine/core'; // Create directional light (sun)const sun = world.addEntity();addComponent(world, Transform, sun);addComponent(world, Light, sun);Light.type[sun] = 1; // DirectionalLight.intensity[sun] = 1.0;Light.color[sun][0] = 1.0; // WhiteLight.color[sun][1] = 1.0;Light.color[sun][2] = 1.0;Light.castShadow[sun] = 1;Transform.rotation[sun][0] = -Math.PI / 4; // Angle down // Create point light (lamp)const lamp = world.addEntity();addComponent(world, Transform, lamp);addComponent(world, Light, lamp);Light.type[lamp] = 0; // PointLight.intensity[lamp] = 2.0;Light.range[lamp] = 15.0;Light.color[lamp][0] = 1.0; // OrangeLight.color[lamp][1] = 0.7;Light.color[lamp][2] = 0.3; // Create spot light (flashlight)const flashlight = world.addEntity();addComponent(world, Transform, flashlight);addComponent(world, Light, flashlight);Light.type[flashlight] = 2; // SpotLight.intensity[flashlight] = 3.0;Light.range[flashlight] = 20.0;Light.angle[flashlight] = Math.PI / 6; // 30 degree coneCamera#
The Camera component defines camera projection properties. Paired with CameraTag to mark the active camera.
Properties#
| Property | Type | Default | Description |
|---|---|---|---|
| fov | f32 | 60.0 | Field of view in degrees (45-90 typical) |
| near | f32 | 0.1 | Near clipping plane distance (meters) |
| far | f32 | 1000.0 | Far clipping plane distance (meters) |
| aspect | f32 | 1.77 | Aspect ratio (width/height), set automatically |
| focus | f32 | 10.0 | Focus distance for depth of field (meters) |
Usage#
import { addComponent, Camera, CameraTag } from '@web-engine/core'; // Create main cameraconst camera = world.addEntity();addComponent(world, Transform, camera);addComponent(world, Camera, camera);addComponent(world, CameraTag, camera); // Mark as active Camera.fov[camera] = 75.0; // Wide FOV for FPSCamera.near[camera] = 0.1;Camera.far[camera] = 500.0; // Position cameraTransform.position[camera][1] = 1.7; // Eye heightTransform.position[camera][2] = 5.0; // Back from originActive Camera
Only one camera should have CameraTag at a time. The rendering system uses this to determine which camera renders the scene.
Renderable#
The Renderable tag component marks entities that should be rendered. Used for frustum culling and occlusion queries.
import { addComponent, Renderable } from '@web-engine/core'; // Mark entity as renderableaddComponent(world, Renderable, entity);RenderLayers#
The RenderLayers component controls which cameras can see an entity using bitmask-based layer membership.
Properties#
| Property | Type | Default | Description |
|---|---|---|---|
| mask | ui32 | 0xFFFFFFFF | 32-bit bitmask of layer membership |
Usage#
import { addComponent, RenderLayers, RenderLayerBit } from '@web-engine/core'; // Create UI element on UI layerconst uiElement = world.addEntity();addComponent(world, RenderLayers, uiElement);RenderLayers.mask[uiElement] = RenderLayerBit.UI; // Create object visible on multiple layersconst multiLayerObject = world.addEntity();addComponent(world, RenderLayers, multiLayerObject);RenderLayers.mask[multiLayerObject] = RenderLayerBit.Default | RenderLayerBit.Shadows;Layer Bits
Available layers: Default, UI, Gizmos, Shadows, Transparent, PostProcess, Reflections, Custom1-4. Combine using bitwise OR (|).
CullingMask#
The CullingMask component on cameras determines which RenderLayers the camera can see.
Properties#
| Property | Type | Default | Description |
|---|---|---|---|
| mask | ui32 | 0xFFFFFFFF | 32-bit bitmask of visible layers |
import { addComponent, CullingMask, RenderLayerBit } from '@web-engine/core'; // Main camera sees default and shadows onlyaddComponent(world, CullingMask, mainCamera);CullingMask.mask[mainCamera] = RenderLayerBit.Default | RenderLayerBit.Shadows; // UI camera sees UI onlyaddComponent(world, CullingMask, uiCamera);CullingMask.mask[uiCamera] = RenderLayerBit.UI;BoundingBox#
The BoundingBox component stores precomputed axis-aligned bounding boxes for frustum culling and spatial queries.
Properties#
| Property | Type | Default | Description |
|---|---|---|---|
| min | [f32, 3] | [0, 0, 0] | Minimum corner [x, y, z] |
| max | [f32, 3] | [0, 0, 0] | Maximum corner [x, y, z] |
| center | [f32, 3] | [0, 0, 0] | Center point [x, y, z] |
| radius | f32 | 0.0 | Bounding sphere radius |
Automatic Computation
BoundingBox is automatically computed by RenderSystem from mesh geometry. Mark entities with BoundingBoxDirty to trigger recalculation.
LODGroup#
The LODGroup component manages Level of Detail switching based on camera distance, reducing rendering cost for distant objects.
Properties#
| Property | Type | Default | Description |
|---|---|---|---|
| distances | [f32, 3] | [10, 50, 100] | Distance thresholds for LOD levels [LOD0→1, LOD1→2, LOD2→cull] |
| assetIds | [ui32, 3] | [0, 0, 0] | Asset IDs for each LOD level [LOD0, LOD1, LOD2] |
| currentLOD | ui8 | 0 | Current active LOD (0=highest, 3=culled) |
| hysteresis | ui8 | 25 | Hysteresis percentage (0-255 mapped to 0-100%) |
| lastChangeTime | f32 | 0.0 | Time of last LOD change (seconds) |
| cooldownMs | ui8 | 100 | Cooldown between changes (0-255 = 0-2550ms) |
Usage#
import { addComponent, LODGroup } from '@web-engine/core'; // Setup LOD for treeconst tree = world.addEntity();addComponent(world, LODGroup, tree); // LOD0: Full detail (0-20m)LODGroup.assetIds[tree][0] = highDetailTreeAssetId;LODGroup.distances[tree][0] = 20.0; // LOD1: Medium detail (20-50m)LODGroup.assetIds[tree][1] = mediumDetailTreeAssetId;LODGroup.distances[tree][1] = 50.0; // LOD2: Low detail (50-100m)LODGroup.assetIds[tree][2] = lowDetailTreeAssetId;LODGroup.distances[tree][2] = 100.0; // Beyond 100m: Culled (not rendered) // Optional: Adjust hysteresis to prevent flickeringLODGroup.hysteresis[tree] = 26; // 10% hysteresis (26/255 ≈ 0.1)LOD Hysteresis
Hysteresis prevents visual popping when objects are near threshold boundaries. With 10% hysteresis at 50m: switch to higher LOD at 45m, switch to lower LOD at 55m.
Environment#
The Environment component controls global environmental settings like skybox, fog, ambient light, and sun position.
Properties#
| Property | Type | Default | Description |
|---|---|---|---|
| preset | ui8 | 0 | 0=None, 1=Sunset, 2=Dawn, 3=Night, 4=Warehouse, etc. |
| background | ui8 | 1 | Show skybox as background (0=no, 1=yes) |
| blur | f32 | 0.0 | Skybox blur amount (0-1) |
| sunPosition | [f32, 3] | [0, 1, 0] | Sun direction [x, y, z] |
| ambientIntensity | f32 | 1.0 | Ambient light intensity |
| fogColor | [f32, 3] | [0.5, 0.5, 0.5] | Fog color [r, g, b] |
| fogDensity | f32 | 0.0 | Fog density (0=no fog) |
PostProcessing#
The PostProcessing component configures global post-processing effects like bloom, vignette, chromatic aberration, and tone mapping.
Properties#
| Property | Type | Default | Description |
|---|---|---|---|
| enabled | ui8 | 0 | Enable post-processing (0=no, 1=yes) |
| bloomIntensity | f32 | 0.5 | Bloom glow intensity |
| bloomThreshold | f32 | 0.9 | Brightness threshold for bloom |
| bloomSmoothing | f32 | 0.5 | Bloom smoothness |
| vignetteDarkness | f32 | 0.5 | Vignette edge darkness |
| vignetteOffset | f32 | 1.0 | Vignette offset from edges |
| chromaticAberration | f32 | 0.0 | Chromatic aberration offset |
| toneMappingExposure | f32 | 1.0 | Tone mapping exposure |
| ssaoIntensity | f32 | 0.5 | Screen-space ambient occlusion intensity |
Best Practices#
- Use LODGroup for distant objects to improve performance
- Enable castShadow only on important objects (shadows are expensive)
- Use RenderLayers to separate UI, world, and effects rendering
- Keep light count low (1 directional + 2-3 point lights max for realtime shadows)
- Use MaterialRef.colorOverride for runtime tinting instead of material clones
- Set appropriate camera.near/far planes to minimize z-fighting
- Use BoundingBox for custom culling and spatial queries