ObsequiousNoodle
ObsequiousNoodle

Reputation: 13

How do I keep a light in a scene fixed when using orbit controls in React Three Fiber?

As the title states, I want to keep a light in position while rotating an object using OrbitControls. I currently have a box with a light shining on one side of it. I want to be able to rotate the box and have the light stay in position, lighting the other sides as I rotate the box. :) Here is my current code

`import React, { Suspense, useEffect, useState } from "react";
import { Canvas } from "@react-three/fiber";
import { OrbitControls, Preload, useGLTF } from "@react-three/drei";

import CanvasLoader from "../Loader";

const Island = ({ isMobile }) => {
  const island = useGLTF("./block/scene.gltf");

  return (
    <mesh>
      <hemisphereLight intensity={0.15} groundColor='black' />
      {/* <spotLight
        position={[0, -10, 10]}
        angle={0.22}
        penumbra={1}
        intensity={2}
        castShadow
        shadow-mapSize={1024}
      /> */}


      <primitive
        object={island.scene}
        scale={isMobile ? 0.7 : 0.005}
        position={isMobile ? [0, -4, -2.2] : [0, -4.2, 0]}
        rotation={[0, 0, 0]}
      />





    </mesh>

  );
};

const IslandCanvas = () => {
  const [isMobile, setIsMobile] = useState(false);

  useEffect(() => {
    // Add a listener for changes to the screen size
    const mediaQuery = window.matchMedia("(max-width: 500px)");

    // Set the initial value of the `isMobile` state variable
    setIsMobile(mediaQuery.matches);

    // Define a callback function to handle changes to the media query
    const handleMediaQueryChange = (event) => {
      setIsMobile(event.matches);
    };

    // Add the callback function as a listener for changes to the media query
    mediaQuery.addEventListener("change", handleMediaQueryChange);

    // Remove the listener when the component is unmounted
    return () => {
      mediaQuery.removeEventListener("change", handleMediaQueryChange);
    };
  }, []);

  return (

    <Canvas

      frameloop='demand'
      shadows
      dpr={[1, 2]}
      camera={{ position: [20, 3, 5], fov: 25 }}
      gl={{ preserveDrawingBuffer: true }}
    >
      {/* <pointLight
      intensity={1}
      position={[0, -30, 1000]}
      /> */}

      <Suspense fallback={<CanvasLoader />}>
        <group>
          <pointLight
            intensity={1}
            position={[0, -30, 1000]}
          />
        </group>

        <group>
        <OrbitControls

enableZoom={false}
maxPolarAngle={Math.PI / 2}
minPolarAngle={Math.PI / 2}
/>
          <Island isMobile={isMobile} />

        </group>

      </Suspense>

      <Preload all />
    </Canvas>
  );
};

export default IslandCanvas;`

I have tried wrapping the light in a separate group on the canvas, I have tried putting orbit controls inside of the object mesh and not the canvas, none of this has worked so far and I'm a little lost. This is my first overflow question I've posted so lmk if I'm leaving anything out

Upvotes: 1

Views: 1440

Answers (1)

Carson Darling
Carson Darling

Reputation: 345

From what I have seen, there are two options here:

Option 1

Attach the lights to the camera object, so that as the camera is moved around, the lights automatically follow the camera. For this to work, the camera must also be added to the scene.

import { Canvas } from '@react-three/fiber';
import { PointLight } from 'three';

const light = new PointLight();
light.position.set(1, 1, 0); // This position is relative to the camera's position

function Scene() {
  return (
    <Canvas
      onCreated={({ camera, scene }) => {
        camera.add(light);
        scene.add(camera);
      }}
    >
      {/* your scene */}
    </Canvas>
  );
}

Option 2

Add lights to the scene, but update their position when OrbitControls update the camera position.

import { OrbitControls } from '@react-three/drei';
import { Canvas } from '@react-three/fiber';
import { useRef } from 'react';
import { PointLight } from 'three';

function Scene() {
  const lightRef = useRef<PointLight>(null);
  return (
    <Canvas>
      {/* your scene */}

      <pointLight ref={lightRef} position={[1, 1, 0]} />

      <OrbitControls
        onChange={(e) => {
          if (!e) return;
          const camera = e.target.object;

          if (lightRef.current) {
            // This sets the point light to a location above your camera
            // Note that this position is in world space, not relative to
            // the camera
            lightRef.current.position.set(0, 1, 0);
            lightRef.current.position.add(camera.position);
          }
        }}
      />
    </Canvas>
  );
}

Upvotes: 1

Related Questions