This secondary entry point includes miscellaneous utilities and components for animations, shadows, frame buffer objects (FBOs), HTML overlays, and more.
- animations
- fbo
- NgtsFBO
- NgtsBakeShadows
- NgtsSoftShadows
- depthBuffer
- NgtsSampler
- surfaceSampler
- NgtsComputedAttribute
- NgtsDecal
- NgtsHTML
- intersect
- NgtsIntersect
- NgtsPreload
- calculateScaleFactor
- getVersion
- setUpdateRange
function animations<TAnimation extends NgtsAnimationClip>(
animationsFactory: () => NgtsAnimation<TAnimation> | undefined | null,
object: ElementRef<Object3D> | Object3D | (() => ElementRef<Object3D> | Object3D | undefined | null),
{ injector }: { injector?: Injector } = {},
): NgtsAnimationApi<TAnimation>;Creates an animation API for managing THREE.js animation clips on an object. It takes a Signal of animation clips (an array or an object containing an array), a reference to the object to be animated, and an optional injector.
This function is commonly used together with gltfResource since GLTF files often contain animation data for 3D models. It provides an abstraction around AnimationMixer, which simplifies the process of playing and controlling animations.
The NgtsAnimationApi object contains the following properties:
clips: An array ofAnimationClipobjects representing the available animations.mixer: An instance ofAnimationMixerused to control the playback of animations.names: An array of strings representing the names of the available animations.actions: An object where keys are animation names and values areAnimationActionobjects (available whenisReadyistrue).isReady: A getter indicating if the animations have finished initializing.
const gltf = gltfResource(() => 'model.glb');
const api = animations(
() => gltf.value()?.animations,
() => gltf.value()?.scene,
);
effect(() => {
if (api.isReady) {
api.actions['walk'].play();
}
});Note:
injectAnimationsis deprecated. Useanimationsinstead.
function fbo(
params: () => NgtsFBOParams = () => ({}),
{ injector }: { injector?: Injector } = {},
): THREE.WebGLRenderTarget;Creates a WebGLRenderTarget (Frame Buffer Object) for off-screen rendering. It takes a Signal of FBO parameters and an optional injector. The FBO is automatically sized to the canvas dimensions if width/height are not specified, and is disposed on component destroy.
The NgtsFBOParams object includes the following properties:
width: The width of the FBO in pixels, orRenderTargetOptionsif height is not provided. Defaults to canvas width × device pixel ratio.height: The height of the FBO in pixels. Defaults to canvas height × device pixel ratio.settings: An object containingTHREE.RenderTargetOptionsfor the FBO.samples: The number of samples for multisample anti-aliasing (MSAA). Set to 0 to disable MSAA. (Default: 0)depth: If set, the scene depth will be rendered into buffer.depthTexture. (Default: false)wrapS: The wrapping mode for the s-coordinate of the FBO texture.wrapT: The wrapping mode for the t-coordinate of the FBO texture.magFilter: The magnification filter for the FBO texture.minFilter: The minification filter for the FBO texture.format: The internal format of the color buffer.type: The data type of the color buffer.anisotropy: The level of anisotropic filtering for the FBO texture.depthBuffer: Whether to include a depth buffer. (Default: true)stencilBuffer: Whether to include a stencil buffer. (Default: false)generateMipmaps: Whether to generate mipmaps for the FBO texture. (Default: true)depthTexture: A DepthTexture instance to use for the depth buffer.colorSpace: The color space of the FBO texture.
// Basic usage - sized to canvas
const renderTarget = fbo();
// Custom size with multisampling
const target = fbo(() => ({
width: 512,
height: 512,
settings: { samples: 4 },
}));
// Render to FBO
beforeRender(({ gl, scene, camera }) => {
gl.setRenderTarget(target);
gl.render(scene, camera);
gl.setRenderTarget(null);
});Note:
injectFBOis deprecated. Usefboinstead.
A structural directive that allows you to render a part of your scene into an FBO using an ng-template. It provides the created WebGLRenderTarget as implicit context to the template. The input accepts FBO configuration including width, height, and THREE.RenderTargetOptions.
<ng-template [fbo]="{ width: 512, height: 512 }" let-target>
<!-- target is the WebGLRenderTarget -->
<ngt-mesh>
<ngt-plane-geometry />
<ngt-mesh-basic-material [map]="target.texture" />
</ngt-mesh>
</ng-template>A directive that bakes shadows in your scene. It sets gl.shadowMap.autoUpdate to false and requests a single gl.shadowMap.needsUpdate = true. This can improve performance by making shadows static.
<ngts-bake-shadows />A directive that injects Percentage-Closer Soft Shadows (PCSS) into the scene. PCSS produces contact-hardening soft shadows where shadows are sharper near the contact point and softer further away, creating more realistic shadow effects.
This works by patching Three.js's shadow shader chunk at runtime. When the directive is destroyed or options change, it restores the original shader and recompiles affected materials.
<ngts-soft-shadows [options]="{ size: 25, samples: 10, focus: 0 }" />| Property | Description | Default |
|---|---|---|
size |
Size of the light source. The larger the value, the softer the shadows. | 25 |
samples |
Number of samples for shadow calculation. More samples = less noise but more expensive. | 10 |
focus |
Depth focus to shift the focal point where the shadow is sharpest. 0 means at the beginning. | 0 |
function depthBuffer(
params: () => { size?: number; frames?: number } = () => ({}),
{ injector }: { injector?: Injector } = {},
): THREE.DepthTexture;Creates a depth buffer texture that captures scene depth information. Renders the scene to an off-screen FBO with a depth texture attachment, which can be used for effects like soft particles, SSAO, or custom shaders. Returns the buffer's depthTexture.
Since this is a rather expensive effect you can limit the amount of frames it renders when your objects are static. For instance making it render only once by setting frames: 1.
export class MyCmp {
depth = depthBuffer(() => ({
size: 256, // The size of the depth buffer (default: 256)
frames: Infinity, // The amount of frames to render (default: Infinity)
}));
// Use in a shader
constructor() {
effect(() => {
material.uniforms['depthTexture'].value = this.depth;
});
}
}Note:
injectDepthBufferis deprecated. UsedepthBufferinstead.
A component that distributes instances across a mesh surface using MeshSurfaceSampler. It samples points from a mesh and automatically updates an InstancedMesh with the sampled transforms. Both the source mesh and target instances can be provided as inputs or as children.
| Property | Description | Default value |
|---|---|---|
mesh |
The mesh to sample points from. If not provided, uses the first Mesh child. | null |
instances |
The InstancedMesh to update with sampled transforms. If not provided, uses the first InstancedMesh child. | null |
options |
Sampler configuration object (see below). | See below |
| Property | Description | Default value |
|---|---|---|
weight |
Name of a vertex attribute to use for weighted sampling. Higher values = more likely to be sampled. | undefined |
transform |
Custom transform function applied to each sampled instance. Receives sample data and should mutate payload.dummy to set position, rotation, and scale. |
undefined |
count |
Number of samples to distribute across the mesh surface. | 16 |
<ngts-sampler [options]="{ weight: 'normal', transform: transformPoint, count: 500 }">
<ngt-mesh>
<ngt-sphere-geometry *args="[2]" />
</ngt-mesh>
<ngt-instanced-mesh *args="[undefined, undefined, 500]">
<ngt-sphere-geometry *args="[0.1]" />
</ngt-instanced-mesh>
</ngts-sampler>or use references when you can't compose declaratively:
@Component({
template: `
<ngts-sampler [instances]="instancedRef()" [mesh]="mesh()" [options]="{ count: 500 }" />
<ngt-instanced-mesh #instanced *args="[undefined, undefined, 500]">
<!-- content -->
</ngt-instanced-mesh>
`,
})
class MyCmp {
instancedRef = viewChild<ElementRef<InstancedMesh>>('instanced');
gltf = gltfResource(() => 'my/mesh/url');
mesh = computed(() => this.gltf.value()?.scene || null);
}function surfaceSampler(
mesh: () => ElementRef<THREE.Mesh> | THREE.Mesh | null | undefined,
options?: {
count?: () => number;
transform?: () => TransformFn | undefined;
weight?: () => string | undefined;
instancedMesh?: () => ElementRef<THREE.InstancedMesh> | THREE.InstancedMesh | null | undefined;
},
): Signal<THREE.InstancedBufferAttribute>;Creates a computed signal that samples points on a mesh surface. Uses THREE.MeshSurfaceSampler to distribute points across the mesh geometry. Returns an InstancedBufferAttribute containing transform matrices for each sample, suitable for use with InstancedMesh or custom instancing solutions.
const meshRef = viewChild<ElementRef<THREE.Mesh>>('mesh');
const instancesRef = viewChild<ElementRef<THREE.InstancedMesh>>('instances');
const samples = surfaceSampler(() => meshRef()?.nativeElement, {
count: () => 1000,
instancedMesh: () => instancesRef()?.nativeElement,
transform:
() =>
({ dummy, position, normal }) => {
dummy.position.copy(position);
dummy.lookAt(position.clone().add(normal));
dummy.scale.setScalar(Math.random() * 0.5 + 0.5);
},
});A component that allows you to compute and attach an attribute to a geometry declaratively.
It accepts the following inputs:
compute: A function that computes the attribute. It receives the geometry as an argument and should return aBufferAttribute.name: The name of the attribute to attach to the geometry.options: pass-through options forBufferAttribute
<ngt-sphere-geometry>
<ngts-computed-attribute
name="my-attribute-name"
[compute]="computeAttributeFn"
[options]="{ usage: StaticReadUsage }"
/>
</ngt-sphere-geometry>Abstraction around Three's DecalGeometry. It will use the its parent mesh as the decal surface by default.
The decal box has to intersect the surface, otherwise it will not be visible. if you do not specify a rotation it will look at the parents center point. You can also pass a single number as the rotation which allows you to spin it.
| Property | Description | Default value |
|---|---|---|
map |
The texture to use for the decal. | undefined |
debug |
Makes the "bounding box" of the decal visible. | false |
depthTest |
Whether to enable depth testing. | false |
polygonOffsetFactor |
The factor by which the polygon offset is multiplied. | -10 |
It also accepts a mesh input that allows you to specify the surface the decal must attach to.
<mesh>
<sphereGeometry />
<meshBasicMaterial />
<ngts-decal [options]="{ position: [0, 0, 0], rotation: [0, 0, 0], scale: 1, debug: true }">
<ngt-mesh-basic-material [map]="texture()" [polygonOffset]="true" [polygonOffsetFactor]="-1" />
</ngts-decal>
</mesh>If you do not specify a material it will create a transparent ngt-mesh-basic-material with a polygonOffsetFactor of -10.
<ngt-mesh>
<ngt-mesh-sphere-geometry />
<ngt-mesh-basic-material />
<ngts-decal [options]="{ map: texture() }" />
</ngt-mesh>If declarative composition is not possible, use the mesh input to define the surface the decal must attach to.
<ngts-decal [mesh]="meshRef()">
<ngt-mesh-basic-material [map]="texture()" [polygonOffset]="true" [polygonOffsetFactor]="-1" />
</ngts-decal>A component for rendering HTML content positioned in 3D space. It creates a THREE.Group anchor point in the scene and projects HTML onto the canvas using CSS positioning or CSS 3D transforms.
Import NgtsHTML which includes both NgtsHTMLImpl (the 3D anchor) and NgtsHTMLContent (the HTML container).
import { NgtsHTML } from 'angular-three-soba/misc';
@Component({
imports: [NgtsHTML],
template: `
<ngt-mesh [position]="[0, 2, 0]">
<ngts-html [options]="{ transform: true }">
<div [htmlContent]="{ distanceFactor: 10 }">Label</div>
</ngts-html>
</ngt-mesh>
`,
})
class MyCmp {}| Property | Description | Default |
|---|---|---|
occlude |
Controls occlusion: false, true, 'raycast', 'blending', or array of Object3D refs. |
false |
transform |
When true, uses CSS 3D transforms. When false, projects to 2D screen coordinates. |
false |
castShadow |
Forward shadow casting to occlusion mesh (blending mode only). | false |
receiveShadow |
Forward shadow receiving to occlusion mesh (blending mode only). | false |
| Property | Description | Default |
|---|---|---|
eps |
Epsilon for position/zoom change detection. | 0.001 |
zIndexRange |
Range for automatic z-index calculation [max, min]. |
[16777271, 0] |
center |
Centers the HTML element on the projected point. | false |
prepend |
Prepends to parent instead of appending. | false |
fullscreen |
Makes the container fill the entire canvas size. | false |
containerClass |
CSS class applied to the inner container div. | '' |
containerStyle |
Inline styles applied to the inner container div. | {} |
pointerEvents |
CSS pointer-events value. |
'auto' |
calculatePosition |
Custom function to calculate screen position. | defaultCalculatePosition |
sprite |
When true (with transform), HTML always faces the camera. |
false |
distanceFactor |
Scales HTML based on distance from camera. | undefined |
parent |
Custom parent element for the HTML content. | undefined |
| Output | Description |
|---|---|
occluded |
Emits when occlusion state changes (true = hidden, false = visible). |
<ngts-html [options]="{ occlude: true }">
<div [htmlContent]="{}" (occluded)="isHidden = $event" [class.faded]="isHidden">
Content with custom occlusion handling
</div>
</ngts-html>function intersect<TObject extends THREE.Object3D>(
object: () => ElementRef<TObject> | TObject | undefined | null,
options?: { injector?: Injector; source?: WritableSignal<boolean> },
): Signal<boolean>;Tracks whether an object is within the camera's view frustum. Uses THREE.js's built-in frustum culling by monitoring onBeforeRender calls. Returns a read-only signal that emits true when the object is visible.
const meshRef = viewChild<ElementRef<THREE.Mesh>>('mesh');
const isVisible = intersect(() => meshRef());
effect(() => {
if (isVisible()) {
// Object is in view - start expensive animations
}
});Note:
injectIntersectis deprecated. Useintersectinstead.
A directive that tracks whether the host Object3D is in the camera frustum. Apply to any THREE.js element to get a two-way bound signal indicating visibility.
<ngt-mesh [(intersect)]="isInView">
<ngt-box-geometry />
<ngt-mesh-basic-material />
</ngt-mesh>isInView = signal(false);
effect(() => {
console.log('Mesh visible:', this.isInView());
});A directive that pre-compiles shaders and textures to reduce runtime jank. When added to a scene, it triggers WebGLRenderer.compile() and uses a CubeCamera to ensure environment maps are also compiled.
<!-- Preload entire scene -->
<ngts-preload [all]="true" />
<!-- Preload specific scene/camera -->
<ngts-preload [scene]="customScene" [camera]="customCamera" />| Input | Description |
|---|---|
all |
When true, temporarily makes invisible objects visible during compile. |
scene |
Custom scene to preload. Defaults to the store's scene. |
camera |
Custom camera for compilation. Defaults to the store's camera. |
function calculateScaleFactor(point3: THREE.Vector3, radiusPx: number, camera: THREE.Camera, size: NgtSize): number;Calculates a scale factor to maintain consistent pixel-size at a 3D position. Given a 3D point and a desired radius in pixels, computes how much to scale an object so it appears that size on screen.
beforeRender(({ camera, size }) => {
const scale = calculateScaleFactor(mesh.position, 50, camera, size);
mesh.scale.setScalar(scale);
});function getVersion(): number;Retrieves the current THREE.js version as a numeric value. Parses the THREE.js REVISION constant, stripping any non-numeric characters.
if (getVersion() >= 150) {
// Use features available in r150+
}function setUpdateRange(attribute: THREE.BufferAttribute, updateRange: { start: number; count: number }): void;Sets the update range on a BufferAttribute for partial GPU uploads. Handles the API change in THREE.js r159 where updateRange was replaced with updateRanges array and addUpdateRange() method.
const positions = geometry.attributes['position'];
// Only update first 100 vertices
setUpdateRange(positions, { start: 0, count: 100 * 3 });
positions.needsUpdate = true;