NgtsCaustics
import { ChangeDetectionStrategy, Component } from '@angular/core';import { SobaWrapper } from '@soba/wrapper.ts';import { NgtCanvas, provideNgtRenderer } from 'angular-three/dom';import { SceneGraph } from './scene-graph';
@Component({ selector: 'app-caustics', template: ` <ngt-canvas [camera]="{ position: [0, 2, 5], fov: 50 }"> <app-soba-wrapper *canvasContent [grid]="false" [lights]="false"> <app-scene-graph /> </app-soba-wrapper> </ngt-canvas> `, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'caustics-demo relative block h-full' }, imports: [NgtCanvas, SobaWrapper, SceneGraph],})export default class Caustics { static clientProviders = [provideNgtRenderer()];}import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import { NgtArgs } from 'angular-three';import { NgtsMeshTransmissionMaterial } from 'angular-three-soba/materials';import { NgtsCaustics, NgtsEnvironment } from 'angular-three-soba/staging';
@Component({ selector: 'app-scene-graph', template: ` <!-- Floor to receive caustics --> <ngt-mesh [rotation]="[-Math.PI / 2, 0, 0]" [position]="[0, -1, 0]"> <ngt-plane-geometry *args="[10, 10]" /> <ngt-mesh-standard-material color="white" /> </ngt-mesh>
<!-- Glass sphere with caustics --> <ngts-caustics [options]="{ frames: Infinity, intensity: 0.05, color: 'white', lightSource: [5, 5, 5], backside: true, ior: 0.9, backsideIOR: 1.26, }" > <ngt-mesh [position]="[0, 0, 0]"> <ngt-sphere-geometry *args="[0.8, 64, 64]" /> <ngts-mesh-transmission-material [options]="{ backside: true, backsideThickness: 0.1, thickness: 0.05, transmission: 1, roughness: 0, clearcoat: 1, clearcoatRoughness: 0, }" /> </ngt-mesh> </ngts-caustics>
<!-- Second glass object --> <ngts-caustics [options]="{ frames: Infinity, intensity: 0.03, color: '#88ccff', lightSource: [5, 5, 5], ior: 1.1, }" > <ngt-mesh [position]="[2, 0, -1]"> <ngt-dodecahedron-geometry *args="[0.6]" /> <ngts-mesh-transmission-material [options]="{ transmission: 1, roughness: 0.1, thickness: 0.1, }" /> </ngt-mesh> </ngts-caustics>
<!-- Environment for reflections --> <ngts-environment [options]="{ preset: 'sunset' }" />
<!-- Light source --> <ngt-directional-light [position]="[5, 5, 5]" [intensity]="Math.PI" /> <ngt-ambient-light [intensity]="0.3" /> `, schemas: [CUSTOM_ELEMENTS_SCHEMA], changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgtArgs, NgtsCaustics, NgtsEnvironment, NgtsMeshTransmissionMaterial],})export class SceneGraph { protected readonly Math = Math;}NgtsCaustics is a port of Drei’s Caustics which renders realistic caustic light patterns on surfaces.
Caustics are the light patterns created when light is refracted or reflected by curved transparent surfaces (like water, glass, or crystals).
Usage
Basic Caustics
<ngts-caustics> <ngt-mesh> <ngt-sphere-geometry /> <ngt-mesh-physical-material [transmission]="1" [roughness]="0" /> </ngt-mesh></ngts-caustics>Glass Sphere
<ngts-caustics [options]="{ frames: Infinity, intensity: 0.05, color: 'white', ior: 1.5 }"> <ngt-mesh> <ngt-sphere-geometry *args="[1, 64, 64]" /> <ngt-mesh-physical-material [transmission]="1" [roughness]="0" [thickness]="2" [ior]="1.5" /> </ngt-mesh></ngts-caustics>Crystal with Colored Caustics
<ngts-caustics [options]="{ frames: Infinity, intensity: 0.1, color: '#88ccff', ior: 2.0 }"> <ngt-mesh> <ngt-icosahedron-geometry *args="[1, 0]" /> <ngt-mesh-physical-material [transmission]="1" [roughness]="0" color="white" /> </ngt-mesh></ngts-caustics>With Custom Light Source
<ngt-point-light #light [position]="[10, 10, 10]" />
<ngts-caustics [options]="{ lightSource: light, intensity: 0.08 }"> <ngt-mesh> <ngt-torus-geometry *args="[1, 0.4, 32, 64]" /> <ngt-mesh-physical-material [transmission]="1" [roughness]="0" /> </ngt-mesh></ngts-caustics>Backside Rendering
For thick glass objects, enable backside rendering:
<ngts-caustics [options]="{ backside: true, ior: 1.5, backsideIOR: 1.1, intensity: 0.06 }"> <ngt-mesh> <ngt-box-geometry *args="[2, 2, 2]" /> <ngt-mesh-physical-material [transmission]="1" [roughness]="0" /> </ngt-mesh></ngts-caustics>Debug Mode
<ngts-caustics [options]="{ debug: true, frames: Infinity }"> <ngt-mesh> <ngt-sphere-geometry /> <ngt-mesh-physical-material [transmission]="1" /> </ngt-mesh></ngts-caustics>Caustics Only (Hide Object)
<ngts-caustics [options]="{ causticsOnly: true, intensity: 0.1 }"> <ngt-mesh> <ngt-sphere-geometry /> <ngt-mesh-physical-material [transmission]="1" /> </ngt-mesh></ngts-caustics>High Resolution Caustics
<ngts-caustics [options]="{ resolution: 4096, worldRadius: 0.2, intensity: 0.04 }"> <ngt-mesh> <ngt-sphere-geometry /> <ngt-mesh-physical-material [transmission]="1" [roughness]="0" /> </ngt-mesh></ngts-caustics>Water Surface
<ngts-caustics [options]="{ frames: Infinity, intensity: 0.15, color: '#aaddff', ior: 1.33, worldRadius: 0.5 }"> <ngt-mesh [rotation]="[-Math.PI / 2, 0, 0]"> <ngt-plane-geometry *args="[10, 10, 64, 64]" /> <ngt-mesh-physical-material [transmission]="1" [roughness]="0.1" color="#88ccff" /> </ngt-mesh></ngts-caustics>IOR Reference
| Material | IOR |
|---|---|
| Air | 1.0 |
| Water | 1.33 |
| Glass | 1.5 |
| Crystal | 2.0 |
| Diamond | 2.42 |
Notes
- Caustics require transmissive materials (
transmission: 1on MeshPhysicalMaterial) - Higher
resolutionimproves quality but increases GPU cost worldRadiusaffects the scale of the caustic pattern- Use
frames: 1for static scenes to render caustics once - Use
frames: Infinityfor animated objects or moving lights - The
lightSourcecan be a Vector3 position or an Object3D reference - Enable
backsidefor thick transparent objects to get more accurate refraction
Options
options input accepts any properties from THREE.Group in addition to the following:
Properties
| name | type | description |
|---|---|---|
| frames | number | How many frames to render. Set to Infinity for continuous rendering. Default: 1 |
| debug | boolean | Enables visual debugging cues including camera helper. Default: false |
| causticsOnly | boolean | When enabled, displays only caustics and hides the models. Default: false |
| backside | boolean | When enabled, includes back face rendering. Default: false |
| ior | number | The Index of Refraction (IOR) value for front faces. Default: 1.1 |
| backsideIOR | number | The Index of Refraction (IOR) value for back faces. Default: 1.1 |
| worldRadius | number | The world-space texel size for caustic calculations. Default: 0.3125 |
| intensity | number | Intensity of the projected caustics effect. Default: 0.05 |
| color | THREE.ColorRepresentation | Color of the caustics effect. Default: 'white' |
| resolution | number | Buffer resolution for caustic texture rendering. Default: 2024 |
| lightSource | THREE.Vector3 | THREE.Object3D | [number, number, number] | Light source position or object. Default: [5, 5, 5] |