NgtsShadow
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-shadow', template: ` <ngt-canvas [camera]="{ position: [3, 3, 3], fov: 50 }"> <app-soba-wrapper *canvasContent [grid]="false"> <app-scene-graph /> </app-soba-wrapper> </ngt-canvas> `, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'shadow-demo relative block h-full' }, imports: [NgtCanvas, SobaWrapper, SceneGraph],})export default class Shadow { static clientProviders = [provideNgtRenderer()];}import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import { NgtArgs } from 'angular-three';import { NgtsShadow } from 'angular-three-soba/staging';
@Component({ selector: 'app-scene-graph', template: ` <!-- Floating sphere --> <ngt-mesh [position]="[0, 1, 0]"> <ngt-sphere-geometry *args="[0.5, 32, 32]" /> <ngt-mesh-standard-material color="#ff6b6b" /> </ngt-mesh>
<!-- Drop shadow below sphere --> <ngts-shadow [options]="{ color: 'black', opacity: 0.5, scale: [1.5, 1.5, 1], position: [0, 0.01, 0], rotation: [-Math.PI / 2, 0, 0], }" />
<!-- Floating box --> <ngt-mesh [position]="[-1.5, 0.75, 0]"> <ngt-box-geometry *args="[0.8, 0.8, 0.8]" /> <ngt-mesh-standard-material color="#4ecdc4" /> </ngt-mesh>
<!-- Drop shadow below box --> <ngts-shadow [options]="{ color: 'black', opacity: 0.4, scale: [1.2, 1.2, 1], position: [-1.5, 0.01, 0], rotation: [-Math.PI / 2, 0, 0], }" />
<!-- Floating torus --> <ngt-mesh [position]="[1.5, 0.8, 0]" [rotation]="[Math.PI / 4, 0, 0]"> <ngt-torus-geometry *args="[0.4, 0.15, 16, 32]" /> <ngt-mesh-standard-material color="#a29bfe" /> </ngt-mesh>
<!-- Drop shadow below torus --> <ngts-shadow [options]="{ color: 'black', opacity: 0.35, scale: [1.3, 1.3, 1], position: [1.5, 0.01, 0], rotation: [-Math.PI / 2, 0, 0], }" />
<!-- Ground plane for reference --> <ngt-mesh [rotation]="[-Math.PI / 2, 0, 0]" [position]="[0, 0, 0]"> <ngt-plane-geometry *args="[10, 10]" /> <ngt-mesh-standard-material color="#f5f5f5" /> </ngt-mesh> `, schemas: [CUSTOM_ELEMENTS_SCHEMA], changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgtArgs, NgtsShadow],})export class SceneGraph { protected readonly Math = Math;}NgtsShadow is a port of Drei’s Shadow which renders a simple circular drop shadow using a canvas-generated radial gradient texture.
This creates a flat plane with a transparent gradient that simulates a soft shadow beneath objects.
Usage
Basic Drop Shadow
<ngt-mesh [position]="[0, 1, 0]"> <ngt-sphere-geometry /> <ngt-mesh-standard-material color="hotpink" /></ngt-mesh>
<ngts-shadow [options]="{ color: 'black', opacity: 0.5 }" [scale]="[2, 2, 1]"/>Colored Shadow
<ngt-mesh [position]="[0, 0.5, 0]"> <ngt-box-geometry /> <ngt-mesh-standard-material color="orange" /></ngt-mesh>
<ngts-shadow [options]="{ color: '#8B4513', opacity: 0.4 }" [scale]="[1.5, 1.5, 1]"/>Adjusted Color Stop
The colorStop controls where the gradient transitions:
<!-- Softer, more spread out shadow --><ngts-shadow [options]="{ colorStop: 0.0, opacity: 0.3 }" [scale]="[3, 3, 1]"/>
<!-- Sharper, more concentrated shadow --><ngts-shadow [options]="{ colorStop: 0.5, opacity: 0.6 }" [scale]="[1.5, 1.5, 1]"/>Multiple Shadows
<ngt-group> <ngt-mesh [position]="[-1.5, 0.5, 0]"> <ngt-sphere-geometry *args="[0.5]" /> <ngt-mesh-standard-material color="red" /> </ngt-mesh> <ngts-shadow [position]="[-1.5, 0.01, 0]" [options]="{ opacity: 0.4 }" [scale]="[1, 1, 1]" />
<ngt-mesh [position]="[1.5, 0.5, 0]"> <ngt-sphere-geometry *args="[0.5]" /> <ngt-mesh-standard-material color="blue" /> </ngt-mesh> <ngts-shadow [position]="[1.5, 0.01, 0]" [options]="{ opacity: 0.4 }" [scale]="[1, 1, 1]" /></ngt-group>With Fog
<!-- Enable fog interaction --><ngts-shadow [options]="{ fog: true, color: 'black', opacity: 0.5 }" [scale]="[2, 2, 1]"/>Dynamic Shadow Size
@let shadowScale = calculateShadowScale();
<ngts-shadow [options]="{ opacity: 0.5 }" [scale]="shadowScale" [position]="[0, 0.01, 0]"/>Notes
- The shadow is rendered as a flat plane facing upward (Y-up)
- Position the shadow slightly above the ground (e.g., y: 0.01) to prevent z-fighting
- Scale determines the size of the shadow on the X and Y axes
- Use
colorStopto control the gradient sharpness - This is a simple, performant alternative to real-time shadow mapping
- Best used for static objects or simple drop shadows
Options
options input accepts any properties from THREE.Mesh in addition to the following:
Properties
| name | type | description |
|---|---|---|
| colorStop | number | Position of the color stop in the radial gradient (0-1). Default: 0.0 |
| fog | boolean | Whether the shadow is affected by fog. Default: false |
| color | THREE.ColorRepresentation | Color of the shadow. Default: 'black' |
| opacity | number | Opacity of the shadow (0-1). Default: 0.5 |
| depthWrite | boolean | Whether to write to the depth buffer. Default: false |