component

LightRig

Sun + hemisphere/ground fill with shadow configuration.

⌖ Explore the library in 3D → · View as Markdown

Lighting system — lights a whole scene rather than an object.
Tune every prop in the workshop →

Add it

npx @runek/cli add lightrig

Pulls @runek/core@^0.15.0, three@^0.184.0.

Use it

import { LightRig } from './runek/LightRig'

<LightRig position={[0, 0, 0]} />

Or as a node in a world file (worlds as data):

{ "type": "LightRig", "props": { "position": [0, 0, 0] } }

Props

Prop Type Default Description
seed number Accepted for the component contract; this component has no seeded variation yet.
sunPosition [x, y, z]
sunColor color
sunIntensity number
ambient number
skyColor color "#bcd4ff"
groundColor color "#4a4030"
shadows boolean true
shadowRange number 30 Half-extent of the shadow camera frustum, in units.

Registry manifest

registry manifest /r/components/lightrig.json Self-contained JSON: inlined source plus resolved dependencies, exactly what runek add lightrig fetches.

Browse the whole catalog in the gallery →.

Source (LightRig.tsx)
import { useSun, type Vec3 } from '@runek/core'
import { Color } from 'three'

export interface LightRigProps {
  /** Accepted for the component contract; this component has no seeded variation yet. */
  seed?: number
  sunPosition?: Vec3
  sunColor?: string
  sunIntensity?: number
  ambient?: number
  skyColor?: string
  groundColor?: string
  shadows?: boolean
  /** Half-extent of the shadow camera frustum, in units. */
  shadowRange?: number
}

// Sun tint across the day: warm at the horizon, near-white at noon, cool moonlight at night.
const NOON = new Color('#fff4e0')
const HORIZON = new Color('#ff9a55')
const MOON = new Color('#9fb2e6')

function sunTint(day: boolean, elevation: number): string {
  if (!day) return `#${MOON.getHexString()}`
  return `#${HORIZON.clone().lerp(NOON, elevation).getHexString()}`
}

/**
 * A drop-in sun + sky/ground fill rig. Pair with `<World lights={false}>`. With no
 * explicit sun props it tracks the world's time-of-day: the sun's position, tint,
 * and intensity follow `<World time>` / `timezone` (golden hour, blue hour, dim
 * moonlight). Any prop you set wins over the time-derived value.
 */
export function LightRig({
  sunPosition,
  sunColor,
  sunIntensity,
  ambient,
  skyColor = '#bcd4ff',
  groundColor = '#4a4030',
  shadows = true,
  shadowRange = 30,
}: LightRigProps) {
  const sun = useSun()
  const e = sun.elevation

  const position = sunPosition ?? sun.position
  const color = sunColor ?? sunTint(sun.day, e)
  const intensity = sunIntensity ?? (sun.day ? 0.3 + 1.4 * e : 0.08)
  const amb = ambient ?? (sun.day ? 0.18 + 0.22 * e : 0.1)
  const hemiIntensity = sun.day ? 0.25 + 0.45 * e : 0.15

  return (
    <>
      <hemisphereLight color={skyColor} groundColor={groundColor} intensity={hemiIntensity} />
      <ambientLight intensity={amb} />
      <directionalLight
        position={position}
        color={color}
        intensity={intensity}
        castShadow={shadows}
        shadow-mapSize={[2048, 2048]}
        shadow-camera-near={1}
        shadow-camera-far={120}
        shadow-camera-left={-shadowRange}
        shadow-camera-right={shadowRange}
        shadow-camera-top={shadowRange}
        shadow-camera-bottom={-shadowRange}
      />
    </>
  )
}