0

I'm new to R3F and I'm trying to learn some basic fundamentals, basically what I'm trying to do is render a car model from a .glb file and then render a circular button on the door of car which would trigger an click event.

However, I could not render the button no matter what I tried, I tried <mesh>, <Sprite/>, <Geometry/>, and the <Html/> tag from @react-fiber/drei. My requirements are:

  • It must be a circle.
  • It must have a fixed size, even after zooming in and out.
  • It must be positioned correctly. For some reason, when I tried to position <Sprite/> just by 5 pixels, It was moved significantly far from its original position.
  • Optionally, it must appear "in front" of other elements, kinda like having z-index: 9999.
function ClickableSprite() {
  return (
    <>
      <sprite
    position={spritePosition}
    ref={pulsingSpriteRef}
    material={texture}
    {...rest}
  />
      <sprite position={spritePosition}
       translateX={10} translateY={10} translateZ={10} {...rest}/>
      <Html
    onAfterRender={console.log}
    position={spritePosition}
    transform
    occlude
    // distanceFactor={10}
    style={{
      width: "10px",
      height: "10px",
      borderRadius: "50%",
      background: "red",
    }}
  />
    </>
  );
}
2
  • 1
    Could you show some sandbox? Commented Jun 4 at 19:20
  • @ŁukaszDanielMastalerz Honestly I don't have much of a project as much as an initial setup. Here is the setup in case I'm doing something wrong, other than that, I included all attempts at this problem. Commented Jun 4 at 23:48

2 Answers 2

1

Try this, if I understood you correctly...

Instead of sphere, use your glb. If you want the texture to be fixed, you need to use the useFrame hook to update the sprite position every frame. But to keep it in the same place in the view, you need copy the camera position and then move the sprite slightly forward in the direction the camera is facing. This allows the sprite to stay in front of the camera even when it moves or rotates. And you need to set depthTest set to false.

import React, { useRef } from "react";
import { Canvas, useLoader, useFrame, useThree } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
import * as THREE from "three";
import "./styles.css";

function FixedSprite({ onClick }) {
  const spriteRef = useRef();
  const { camera } = useThree();
  const circleTexture = useLoader(
    THREE.TextureLoader,
    "https://media.craiyon.com/2023-11-26/2ets6-v1SVGOdKAtPxmlMw.webp"
  );

  useFrame(() => {
    if (!spriteRef.current) return;
    const distanceInFront = -2.5;
    spriteRef.current.position.copy(camera.position);
    spriteRef.current.position.add(
      new THREE.Vector3(0, 0, distanceInFront).applyQuaternion(
        camera.quaternion
      )
    );
  });

  return (
    <sprite ref={spriteRef} scale={[1, 1, 1]} onClick={onClick}>
      <spriteMaterial attach="material" map={circleTexture} depthTest={false} />
    </sprite>
  );
}

function CarScene() {
  return (
    <>
      <mesh>
        <sphereGeometry args={[2, 32, 32]} />
        <meshStandardMaterial color="blue" />
      </mesh>
      <FixedSprite onClick={() => console.log("clicked")} />
      <ambientLight intensity={0.5} />
      <directionalLight position={[5, 5, 5]} intensity={1.1} />
      <OrbitControls makeDefault />
    </>
  );
}

export default function App() {
  return (
    <Canvas>
      <CarScene />
    </Canvas>
  );
}
Sign up to request clarification or add additional context in comments.

1 Comment

Your code gave me the right idea and helped me get through the issue! Here is my final code, I didn't need to fix my sprite right in front of the camera and this doesn't solve the zoom issue but it does everything else.
1

Update: I fixed the zoom issue! Now the sprite:

  • has a fixed size, even when zooming in and out, the sprite stays the exact same size. (using PerspectiveCamera)

  • Positioned correctly, I don't know why <mesh/> has a problem with positioning, but <sprite/> works fine.

  • Appears in front of the car even if its positioned behind it using depthTest={false}

[Here is a minimum app with my initial steup](https://stackblitz.com/edit/vitejs-vite-enfe7mff), many thanks to @Łukasz Daniel Mastalerz

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.