@threlte/extras
<Backdrop>
A mesh of a curved plane designed to look like a studio backdrop. Its intent is to break up light and shadows in more interesting ways.
<script lang="ts">
import Scene from './Scene.svelte'
import { Canvas } from '@threlte/core'
import { Checkbox, Color, Folder, Slider, Pane } from 'svelte-tweakpane-ui'
let floor = $state(0.5)
let receiveShadow = $state(true)
let segments = $state(20)
let materialColor = $state('#ffffff')
let materialWireframe = $state(false)
let lightHelperVisible = $state(true)
</script>
<Pane
position="fixed"
title="backdrop"
>
<Folder title="component props">
<Slider
bind:value={floor}
min={0}
max={1}
step={0.25}
label="floor"
/>
<Checkbox
bind:value={receiveShadow}
label="receive shadow"
/>
<Slider
min={10}
max={50}
step={10}
bind:value={segments}
label="segments"
/>
</Folder>
<Folder title="material props">
<Color
bind:value={materialColor}
label="color"
/>
<Checkbox
label="wireframe"
bind:value={materialWireframe}
/>
</Folder>
<Folder title="light helper">
<Checkbox
label="visible"
bind:value={lightHelperVisible}
/>
</Folder>
</Pane>
<div>
<Canvas>
<Scene
{floor}
{materialColor}
{materialWireframe}
{receiveShadow}
{segments}
{lightHelperVisible}
/>
</Canvas>
</div>
<style>
div {
height: 100%;
}
</style><script lang="ts">
import type { BackdropProps } from '@threlte/extras'
import type { TransformControls as ThreeTransformControls } from 'three/examples/jsm/Addons.js'
import type {
ColorRepresentation,
DirectionalLight,
DirectionalLightHelper,
Mesh,
MeshStandardMaterial
} from 'three'
import { Backdrop, OrbitControls } from '@threlte/extras'
import { Resize, TransformControls, useGltf } from '@threlte/extras'
import { T, useThrelte } from '@threlte/core'
let {
lightHelperVisible = true,
materialColor = 'white',
materialWireframe = false,
...backdropProps
}: BackdropProps & {
lightHelperVisible: boolean
materialColor: ColorRepresentation
materialWireframe: boolean
} = $props()
const gltf = useGltf<{
nodes: { LOD3spShape: Mesh }
materials: { 'blinn3-fx': MeshStandardMaterial }
}>('/models/Duck.glb').then((gltf) => {
gltf.nodes.LOD3spShape.castShadow = true
return gltf
})
const { scene } = useThrelte()
let helper = $state.raw<DirectionalLightHelper>()
let controls = $state.raw<ThreeTransformControls>()
let light = $state.raw<DirectionalLight>()
$effect(() => {
if (lightHelperVisible && light !== undefined) {
controls?.attach(light)
}
return () => {
controls?.detach()
}
})
</script>
<T.PerspectiveCamera
makeDefault
position.x={10}
position.y={5}
position.z={10}
>
<OrbitControls
enableDamping
maxPolarAngle={0.5 * Math.PI}
minAzimuthAngle={-1 * 0.25 * Math.PI}
maxAzimuthAngle={0.25 * Math.PI}
maxDistance={20}
/>
</T.PerspectiveCamera>
<T.DirectionalLight
position.x={-1 * 3}
position.y={2}
position.z={4}
castShadow
bind:ref={light}
>
{#snippet children({ ref })}
<TransformControls
bind:controls
onobjectChange={() => {
helper?.update()
}}
/>
<T.DirectionalLightHelper
bind:ref={helper}
args={[ref]}
attach={scene}
visible={lightHelperVisible}
/>
{/snippet}
</T.DirectionalLight>
<Backdrop
{...backdropProps}
scale={[50, 10, 10]}
>
<T.MeshStandardMaterial
color={materialColor}
wireframe={materialWireframe}
/>
</Backdrop>
{#await gltf then { scene }}
<Resize
scale={4}
position.z={1}
rotation.y={Math.PI}
>
<T is={scene} />
</Resize>
{/await}Children are “slotted” into the underlying mesh so you can set the material of the backdrop like so:
<Backdrop>
<T.MeshStandardMaterial color="#ffffdd" />
</Backdrop>Props
floor controls the length of the “floor” segment of the geometry. The higher the value, the longer the segment.
receiveShadow controls whether the mesh receives shadows or not.
segments controls the resolution of the mesh.
Be aware that updating the segments prop causes the geometry to be rebuilt. It’s advised not to
update segments in hot-code paths such as useTask().