threlte logo
@threlte/extras

<Environment>

Scene environment map implementation with included loaders and ground projected environment.

<script lang="ts">
  import { Canvas } from '@threlte/core'
  import { Environment } from '@threlte/extras'
  import Scene from './Scene.svelte'
  import { Pane, Slider, Checkbox, Folder, List } from 'svelte-tweakpane-ui'

  let isBackground = true
  let path = '/hdr/Bridge2_cube/'
  let files: string | string[] = [
    'posx.jpg',
    'negx.jpg',
    'posy.jpg',
    'negy.jpg',
    'posz.jpg',
    'negz.jpg'
  ]
  let selection = ''

  $: if (selection != '') {
    switch (selection) {
      case 'cube_ldr':
        path = '/hdr/Bridge2_cube/'
        files = ['posx.jpg', 'negx.jpg', 'posy.jpg', 'negy.jpg', 'posz.jpg', 'negz.jpg']
        break
      case 'cube_hdr':
        path = '/hdr/pisaHDR/'
        files = ['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr']
        break
      case 'equirect_ldr':
        path = '/hdr/'
        files = 'equirect_ruined_room.jpg'
        break
      case 'equirect_hdr':
        path = '/hdr/'
        files = 'shanghai_riverside_1k.hdr'
        break
      default:
        console.log(`Sorry, we are out of.`)
    }
  }
  let options = {
    cube_ldr: 'cube_ldr',
    cube_hdr: 'cube_hdr',
    equirect_ldr: 'equirect_ldr',
    equirect_hdr: 'equirect_hdr'
  }
  let useGround = true
  let scale = { x: 100, y: 100, z: 100 }
  let radius = 100
  let height = 5

  $: scaleArray = [scale.x, scale.y, scale.z]
</script>

<Pane
  title="Environment"
  position="fixed"
>
  <Folder title="core">
    <Checkbox
      label="isBackground"
      bind:value={isBackground}
    />
  </Folder>
  <Folder title="Ground Projection">
    <List
      bind:value={selection}
      label="scene"
      {options}
    />
    <Checkbox
      label="use ground environment"
      bind:value={useGround}
    />
    <!-- @TODO: breaks svelte 5 -->
    <!-- <Point
      bind:value={scale}
      label="scale"
    /> -->
    <Slider
      bind:value={radius}
      label="radius"
      min={10}
      max={300}
    />
    <Slider
      bind:value={height}
      label="height"
      min={1}
      max={50}
    />
  </Folder>
</Pane>

<div>
  <Canvas>
    <Environment
      {path}
      {files}
      {isBackground}
      groundProjection={useGround ? { radius, height, scale: scaleArray } : undefined}
    />
    <Scene />
  </Canvas>
</div>

<style>
  div {
    height: 100%;
  }
</style>
<script lang="ts">
  import { T } from '@threlte/core'
  import { OrbitControls } from '@threlte/extras'
</script>

<T.PerspectiveCamera
  position={[10, 7, 10]}
  fov={60}
  near={1}
  far={20000}
  makeDefault
>
  <OrbitControls
    autoRotate={true}
    autoRotateSpeed={0.4}
    enableZoom={true}
  />
</T.PerspectiveCamera>

<T.Mesh
  castShadow
  receiveShadow
  position.y={2.5}
>
  <T.SphereGeometry args={[2.5]} />
  <T.MeshStandardMaterial
    color="white"
    metalness={0.9}
    roughness={0.05}
    envMapIntensity={0.5}
  />
</T.Mesh>

Usage

Pass absolute path to path. For example, if you are using sveltekit and you put your files in static/envmap/hdr then path will be /envmap/hdr/

The component decides whether to use cubic or equirectangular map based on the files prop. Provide a string array for cubic or a string for equirectangular.

Currently supported formats are ‘ldr’ (.jpg, .png, etc.) and ‘hdr’ .hdr. Format is inferred based on file extension but it can be provided in format prop.

isBackground prop controls if environment is set as the background of scene. This is not related to GroundProjection which produces a faux background effect by creating a spherical object textured by the provided environment.

To enable GroundProjection pass a configuration object as a groundProjection prop. The most common use case is to pass radius, height and scale ({ radius: 200, height: 5, scale: {x: 100,y: 100,z: 100}) however, you can pass any props you would pass THREE.Mesh since it is built as its extension.

<!-- Cubic jpg envmap -->
<Environment
  path="/envmap/bridge_cube/"
  files={['posx.jpg', 'negx.jpg', 'posy.jpg', 'negy.jpg', 'posz.jpg', 'negz.jpg']}
  isBackground={true}
  format="ldr"
  groundProjection={{ radius: 200, height: 5, scale: { x: 100, y: 100, z: 100 } }}
/>

<!-- Equirectangular jpg envmap -->
<Environment
  path="/envmap/"
  files="pisa_1k.jpg"
  isBackground={true}
/>

<!-- Cubic hdr envmap -->
<Environment
  path="/envmap/pisaHdr/"
  files={[['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr']]}
  isBackground={true}
  groundProjection={{ radius: 200, height: 5, scale: { x: 100, y: 100, z: 100 } }}
/>

<!-- Equirectangular hdr envmap -->
<Environment
  path="/envmap/"
  files="shanghai_riverside_1k.hdr"
  isBackground={true}
  format="hdr"
  groundProjection={{ radius: 200, height: 5, scale: { x: 100, y: 100, z: 100 } }}
/>

Component Signature

Props

name
type
required
description

files
string | string[]
yes
Provide a string to use an equirectangular envmap and a string array to use a cubic envmap

encoding
TextureEncoding
no
Envmap `TextureEncoding`. If not provided it defaults to `sRGBEncoding` for cubemap and `LinearEncoding` for equirectangular

format
'ldr' | 'hdr'
no
Use `ldr` for .png, .jpg and `hdr` for .hdr file formats

groundProjection
Props<GroundProjectedEnv>
no
Props for ground projection. Scalar recommended to 100. Depending on envmap and project requirements, good starting point is radius: 200, height: 5.

isBackground
boolean
no
Boolean to toggle whether to use envmap as a scene background.

path
string
no
Defaults to "/"