threlte logo
@threlte/extras

<Sky>

This component adds a Three.js Sky object to the scene, renders that on-demand to a cubemap which is assigned to the default scene as the environment map.

<script lang="ts">
  import { useTweakpane } from '$lib/useTweakpane'
  import { Canvas } from '@threlte/core'
  import { Sky } from '@threlte/extras'
  import { spring } from 'svelte/motion'
  import Scene from './Scene.svelte'

  const presets = {
    sunset: {
      turbidity: 10,
      rayleigh: 3,
      azimuth: 180,
      elevation: 0.5,
      mieCoefficient: 0.005,
      mieDirectionalG: 0.7,
      exposure: 0.37
    },
    noon: {
      turbidity: 0.65,
      rayleigh: 0.17,
      azimuth: 180,
      elevation: 85,
      mieCoefficient: 0.013,
      mieDirectionalG: 0.7,
      exposure: 1
    },
    afternoon: {
      turbidity: 4.78,
      rayleigh: 0.3,
      azimuth: 180,
      elevation: 30,
      mieCoefficient: 0.002,
      mieDirectionalG: 0.86,
      exposure: 0.65
    },
    night: {
      turbidity: 20,
      rayleigh: 0.57,
      azimuth: 180,
      elevation: -5,
      mieCoefficient: 0.038,
      mieDirectionalG: 0,
      exposure: 0.26
    }
  }

  const springValues = spring(presets.sunset, {
    damping: 0.95,
    precision: 0.0001,
    stiffness: 0.05
  })

  const { addInput, action, addButton } = useTweakpane({
    title: 'Sky',
    expanded: true
  })

  const setEnvironment = addInput({
    label: 'Set Environment',
    value: true
  })

  const turbidity = addInput({
    label: 'Turbidity',
    value: presets.sunset.turbidity,
    params: {
      min: 0,
      max: 20
    }
  })

  const rayleigh = addInput({
    label: 'Rayleigh',
    value: presets.sunset.rayleigh,
    params: {
      min: 0,
      max: 4
    }
  })

  const azimuth = addInput({
    label: 'Azimuth',
    value: presets.sunset.azimuth,
    params: {
      min: -180,
      max: 180
    }
  })

  const elevation = addInput({
    label: 'Elevation',
    value: presets.sunset.elevation,
    params: {
      min: -5,
      max: 90
    }
  })

  const mieCoefficient = addInput({
    label: 'Mie Coefficient',
    value: presets.sunset.mieCoefficient,
    params: {
      min: 0,
      max: 0.1
    }
  })

  const mieDirectionalG = addInput({
    label: 'Mie Directional G',
    value: presets.sunset.mieDirectionalG,
    params: {
      min: 0,
      max: 1
    }
  })

  const exposure = addInput({
    label: 'Exposure',
    value: presets.sunset.exposure,
    params: {
      min: 0,
      max: 2
    }
  })

  const applyPreset = (preset: keyof typeof presets) => {
    turbidity.set(presets[preset].turbidity)
    rayleigh.set(presets[preset].rayleigh)
    azimuth.set(presets[preset].azimuth)
    elevation.set(presets[preset].elevation)
    mieCoefficient.set(presets[preset].mieCoefficient)
    mieDirectionalG.set(presets[preset].mieDirectionalG)
    exposure.set(presets[preset].exposure)
  }

  addButton({
    title: 'Noon',
    label: 'Preset',
    onClick() {
      applyPreset('noon')
    }
  })

  addButton({
    title: 'Afternoon',
    label: 'Preset',
    onClick() {
      applyPreset('afternoon')
    }
  })

  addButton({
    title: 'Sunset',
    label: 'Preset',
    onClick() {
      applyPreset('sunset')
    }
  })

  addButton({
    title: 'Night',
    label: 'Preset',
    onClick() {
      applyPreset('night')
    }
  })

  $: {
    springValues.set({
      turbidity: $turbidity,
      rayleigh: $rayleigh,
      azimuth: $azimuth,
      elevation: $elevation,
      mieCoefficient: $mieCoefficient,
      mieDirectionalG: $mieDirectionalG,
      exposure: $exposure
    })
  }
</script>

<div use:action />

<Canvas>
  <Sky
    setEnvironment={$setEnvironment}
    turbidity={$springValues.turbidity}
    rayleigh={$springValues.rayleigh}
    azimuth={$springValues.azimuth}
    elevation={$springValues.elevation}
    mieCoefficient={$springValues.mieCoefficient}
    mieDirectionalG={$springValues.mieDirectionalG}
  />

  <Scene exposure={$springValues.exposure} />
</Canvas>
<script lang="ts">
  import { T, useThrelte } from '@threlte/core'
  import { Grid, OrbitControls } from '@threlte/extras'
  import { SphereGeometry } from 'three'
  import { DEG2RAD } from 'three/src/math/MathUtils.js'

  export let exposure = 1

  const { renderer, invalidate } = useThrelte()

  $: {
    renderer.toneMappingExposure = exposure
    invalidate()
  }

  const sphereGeo = new SphereGeometry(2.5, 32, 32)
</script>

<T.PerspectiveCamera
  position={[0, 7, 18]}
  fov={60}
  near={1}
  far={20000}
  makeDefault
>
  <OrbitControls
    enableZoom={true}
    maxPolarAngle={85 * DEG2RAD}
    enableDamping
    target={[0, 2.5, 0]}
  />
</T.PerspectiveCamera>

<T.Mesh
  castShadow
  position.x={3}
  position.y={2.5}
>
  <T is={sphereGeo} />
  <T.MeshStandardMaterial
    roughness={0.1}
    metalness={1}
  />
</T.Mesh>

<T.Mesh
  position.x={-3}
  castShadow
  position.y={2.5}
>
  <T is={sphereGeo} />
  <T.MeshStandardMaterial />
</T.Mesh>

<Grid
  cellColor="white"
  sectionColor="white"
/>

Usage

<script lang="ts">
  import { T, Canvas } from '@threlte/core'
  import { Sky } from '@threlte/extras'
</script>

<Canvas>
  <Sky elevation={0.5} />

  <T.PerspectiveCamera
    makeDefault
    position={[0, 3, 18]}
    fov={60}
    on:create={({ ref }) => {
      ref.lookAt(0, 0, 0)
    }}
  />
</Canvas>

Environment

By default, this component will render the sky to the scene environment. This can be disabled by setting the setEnvironment prop to false.

<Sky setEnvironment={false} />

Performance

The <Sky> component will only re-render the cubemap when the properties change.

Component Signature

Props

name
type
required
default

azimuth
number
no
180

cubeMapSize
number
no
128

elevation
number
no
2

mieCoefficient
number
no
0.005

mieDirectionalG
number
no
0.7

rayleigh
number
no
3

scale
number
no
1000

setEnvironment
boolean
no
true

turbidity
number
no
10

webGLRenderTargetOptions
number
no
{}